summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/linux/sharedfolders
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/linux/sharedfolders')
-rw-r--r--src/VBox/Additions/linux/sharedfolders/.scm-settings29
-rw-r--r--src/VBox/Additions/linux/sharedfolders/Makefile.kmk45
-rw-r--r--src/VBox/Additions/linux/sharedfolders/Makefile.module91
-rw-r--r--src/VBox/Additions/linux/sharedfolders/dirops.c893
-rwxr-xr-xsrc/VBox/Additions/linux/sharedfolders/files_vboxsf89
-rw-r--r--src/VBox/Additions/linux/sharedfolders/lnkops.c119
-rw-r--r--src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c538
-rw-r--r--src/VBox/Additions/linux/sharedfolders/regops.c876
-rw-r--r--src/VBox/Additions/linux/sharedfolders/utils.c883
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vbsfmount.c95
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vbsfmount.h90
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vfsmod.c683
-rw-r--r--src/VBox/Additions/linux/sharedfolders/vfsmod.h181
13 files changed, 4612 insertions, 0 deletions
diff --git a/src/VBox/Additions/linux/sharedfolders/.scm-settings b/src/VBox/Additions/linux/sharedfolders/.scm-settings
new file mode 100644
index 00000000..14d6e145
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/.scm-settings
@@ -0,0 +1,29 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for linux shared folders module.
+#
+
+#
+# Copyright (C) 2010-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+
+/*.c|/*.h: --no-convert-tabs
+/Makefile.module: --treat-as Makefile
+
+# MIT licence to make it easier to re-import code from the in-kernel version.
+/dirops.c: --license-mit
+/lnkops.c: --license-mit
+/regops.c: --license-mit
+/utils.c: --license-mit
+/vbsfmount.h: --license-mit
+/vfsmod.c: --license-mit
+/vfsmod.h: --license-mit
diff --git a/src/VBox/Additions/linux/sharedfolders/Makefile.kmk b/src/VBox/Additions/linux/sharedfolders/Makefile.kmk
new file mode 100644
index 00000000..d520f8df
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/Makefile.kmk
@@ -0,0 +1,45 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the vboxsf (linux shared folders module).
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+#
+# Populate FILES_VBOXSF_NOBIN
+#
+INSTALLS += vboxsf-src
+include $(PATH_SUB_CURRENT)/files_vboxsf
+vboxsf-src_INST = $(INST_ADDITIONS)src/vboxsf/
+vboxsf-src_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXSF_NOBIN))
+vboxsf-src_EXEC_SOURCES = \
+ $(subst $(DQUOTE),,$(FILES_VBOXSF_BIN))
+
+
+#
+# The mount util.
+#
+PROGRAMS += mount.vboxsf
+mount.vboxsf_TEMPLATE = NewVBoxGuestR3Exe
+mount.vboxsf_DEFS = _GNU_SOURCE
+mount.vboxsf_SOURCES = \
+ mount.vboxsf.c \
+ vbsfmount.c
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Additions/linux/sharedfolders/Makefile.module b/src/VBox/Additions/linux/sharedfolders/Makefile.module
new file mode 100644
index 00000000..6c09d5f3
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/Makefile.module
@@ -0,0 +1,91 @@
+# $Id: Makefile.module $
+## @file
+# VBox Linux Shared Folders VFS Module Makefile.
+#
+# (For 2.6.x this file must be 'Makefile'!)
+#
+
+#
+# Copyright (C) 2006-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+# Linux kbuild sets this to our source directory if we are called from there
+obj ?= $(CURDIR)
+include $(obj)/Makefile.include.header
+
+MOD_NAME = vboxsf
+MOD_OBJS = \
+ vfsmod.o \
+ dirops.o \
+ lnkops.o \
+ regops.o \
+ utils.o \
+ VBoxGuestR0LibHGCM.o \
+ VBoxGuestR0LibIdc.o \
+ VBoxGuestR0LibIdc-unix.o \
+ VBoxGuestR0LibSharedFolders.o
+ifeq ($(BUILD_TARGET_ARCH),x86)
+MOD_OBJS += \
+ divdi3.o \
+ moddi3.o \
+ udivdi3.o \
+ udivmoddi4.o \
+ umoddi3.o \
+ qdivrem.o
+endif
+
+MOD_INCL = \
+ $(addprefix -I$(KBUILD_EXTMOD),/ /include /r0drv/linux) \
+ $(addprefix -I$(KBUILD_EXTMOD)/vboxsf,/ /include /r0drv/linux)
+
+ifneq ($(wildcard $(KBUILD_EXTMOD)/vboxsf),)
+ MANGLING := $(KBUILD_EXTMOD)/vboxsf/include/VBox/VBoxGuestMangling.h
+else
+ MANGLING := $(KBUILD_EXTMOD)/include/VBox/VBoxGuestMangling.h
+endif
+
+MOD_DEFS = -DRT_OS_LINUX -DIN_RING0 -DIN_RT_R0 \
+ -DIN_SUP_R0 -DVBOX -DVBOX_WITH_HGCM -DIN_MODULE -DIN_GUEST_R0
+# our module does not export any symbol
+MOD_DEFS += -DRT_NO_EXPORT_SYMBOL
+ifeq ($(BUILD_TARGET_ARCH),amd64)
+ MOD_DEFS += -DRT_ARCH_AMD64 -DVBOX_WITH_64_BITS_GUESTS
+else
+ MOD_DEFS += -DRT_ARCH_X86
+endif
+
+ifeq ($(KERN_VERSION), 24)
+ MOD_CFLAGS =
+else
+ MOD_CFLAGS = -Wno-declaration-after-statement -fshort-wchar -include $(MANGLING) -fno-pie
+
+# special hack for Fedora Core 6 2.6.18 (fc6), rhel5 2.6.18 (el5),
+# ClarkConnect 4.3 (cc4) and ClarkConnect 5 (v5)
+ ifeq ($(KERNELRELEASE),)
+ MOD_EXTRA += $(foreach inc,$(KERN_INCL),\
+ $(if $(wildcard $(inc)/linux/utsrelease.h),\
+ $(if $(shell grep '"2.6.18.*fc6.*"' $(inc)/linux/utsrelease.h; \
+ grep '"2.6.18.*el5.*"' $(inc)/linux/utsrelease.h; \
+ grep '"2.6.18.*v5.*"' $(inc)/linux/utsrelease.h; \
+ grep '"2.6.18.*cc4.*"' $(inc)/linux/utsrelease.h),\
+ -DKERNEL_FC6,),))
+ else
+ MOD_EXTRA += $(if $(shell echo "$(KERNELRELEASE)"|grep '2.6.18.*fc6.*';\
+ echo "$(KERNELRELEASE)"|grep '2.6.18.*el5.*';\
+ echo "$(KERNELRELEASE)"|grep '2.6.18.*v5.*';\
+ echo "$(KERNELRELEASE)"|grep '2.6.18.*cc4.*'),\
+ -DKERNEL_FC6,)
+ endif
+endif
+
+MOD_CLEAN = . linux r0drv r0drv/linux
+
+include $(obj)/Makefile.include.footer
diff --git a/src/VBox/Additions/linux/sharedfolders/dirops.c b/src/VBox/Additions/linux/sharedfolders/dirops.c
new file mode 100644
index 00000000..9b14382f
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/dirops.c
@@ -0,0 +1,893 @@
+/* $Id: dirops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, directory inode and file operations.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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 "vfsmod.h"
+#include <iprt/err.h>
+
+/**
+ * Open a directory. Read the complete content into a buffer.
+ *
+ * @param inode inode
+ * @param file file
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_dir_open(struct inode *inode, struct file *file)
+{
+ int rc;
+ int err;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_dir_info *sf_d;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ SHFLCREATEPARMS params;
+
+ TRACE();
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ if (file->private_data) {
+ LogFunc(("sf_dir_open() called on already opened directory '%s'\n", sf_i->path->String.utf8));
+ return 0;
+ }
+
+ sf_d = sf_dir_info_alloc();
+ if (!sf_d) {
+ LogRelFunc(("could not allocate directory info for '%s'\n",
+ sf_i->path->String.utf8));
+ return -ENOMEM;
+ }
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = 0
+ | SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
+
+ LogFunc(("sf_dir_open(): calling VbglR0SfCreate, folder %s, flags %#x\n", sf_i->path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
+ if (RT_SUCCESS(rc)) {
+ if (params.Result == SHFL_FILE_EXISTS) {
+ err = sf_dir_read_all(sf_g, sf_i, sf_d, params.Handle);
+ if (!err)
+ file->private_data = sf_d;
+ } else
+ err = -ENOENT;
+
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("sf_dir_open(): VbglR0SfClose(%s) after err=%d failed rc=%Rrc\n", sf_i->path->String.utf8, err, rc));
+ } else
+ err = -EPERM;
+
+ if (err)
+ sf_dir_info_free(sf_d);
+
+ return err;
+}
+
+/**
+ * This is called when reference count of [file] goes to zero. Notify
+ * the host that it can free whatever is associated with this directory
+ * and deallocate our own internal buffers
+ *
+ * @param inode inode
+ * @param file file
+ * returns 0 on success, Linux error code otherwise
+ */
+static int sf_dir_release(struct inode *inode, struct file *file)
+{
+ TRACE();
+
+ if (file->private_data)
+ sf_dir_info_free(file->private_data);
+
+ return 0;
+}
+
+/**
+ * Translate RTFMODE into DT_xxx (in conjunction to rtDirType())
+ * @param fMode file mode
+ * returns d_type
+ */
+static int sf_get_d_type(RTFMODE fMode)
+{
+ int d_type;
+ switch (fMode & RTFS_TYPE_MASK) {
+ case RTFS_TYPE_FIFO:
+ d_type = DT_FIFO;
+ break;
+ case RTFS_TYPE_DEV_CHAR:
+ d_type = DT_CHR;
+ break;
+ case RTFS_TYPE_DIRECTORY:
+ d_type = DT_DIR;
+ break;
+ case RTFS_TYPE_DEV_BLOCK:
+ d_type = DT_BLK;
+ break;
+ case RTFS_TYPE_FILE:
+ d_type = DT_REG;
+ break;
+ case RTFS_TYPE_SYMLINK:
+ d_type = DT_LNK;
+ break;
+ case RTFS_TYPE_SOCKET:
+ d_type = DT_SOCK;
+ break;
+ case RTFS_TYPE_WHITEOUT:
+ d_type = DT_WHT;
+ break;
+ default:
+ d_type = DT_UNKNOWN;
+ break;
+ }
+ return d_type;
+}
+
+/**
+ * Extract element ([dir]->f_pos) from the directory [dir] into [d_name].
+ *
+ * @returns 0 for success, 1 for end reached, Linux error code otherwise.
+ */
+static int sf_getdent(struct file *dir, char d_name[NAME_MAX], int *d_type)
+{
+ loff_t cur;
+ struct sf_glob_info *sf_g;
+ struct sf_dir_info *sf_d;
+ struct sf_inode_info *sf_i;
+ struct inode *inode;
+ struct list_head *pos, *list;
+
+ TRACE();
+
+ inode = GET_F_DENTRY(dir)->d_inode;
+ sf_i = GET_INODE_INFO(inode);
+ sf_g = GET_GLOB_INFO(inode->i_sb);
+ sf_d = dir->private_data;
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_d);
+ BUG_ON(!sf_i);
+
+ if (sf_i->force_reread) {
+ int rc;
+ int err;
+ SHFLCREATEPARMS params;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = 0
+ | SHFL_CF_DIRECTORY
+ | SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ;
+
+ LogFunc(("sf_getdent: calling VbglR0SfCreate, folder %s, flags %#x\n", sf_i->path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path,
+ &params);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ return -EPERM;
+ }
+
+ if (params.Result != SHFL_FILE_EXISTS) {
+ LogFunc(("directory %s does not exist\n",
+ sf_i->path->String.utf8));
+ sf_dir_info_free(sf_d);
+ return -ENOENT;
+ }
+
+ sf_dir_info_empty(sf_d);
+ err = sf_dir_read_all(sf_g, sf_i, sf_d, params.Handle);
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ if (err)
+ return err;
+
+ sf_i->force_reread = 0;
+ }
+
+ cur = 0;
+ list = &sf_d->info_list;
+ list_for_each(pos, list) {
+ struct sf_dir_buf *b;
+ SHFLDIRINFO *info;
+ loff_t i;
+
+ b = list_entry(pos, struct sf_dir_buf, head);
+ if (dir->f_pos >= cur + b->cEntries) {
+ cur += b->cEntries;
+ continue;
+ }
+
+ for (i = 0, info = b->buf; i < dir->f_pos - cur; ++i) {
+ size_t size;
+
+ size =
+ offsetof(SHFLDIRINFO,
+ name.String) + info->name.u16Size;
+ info = (SHFLDIRINFO *) ((uintptr_t) info + size);
+ }
+
+ *d_type = sf_get_d_type(info->Info.Attr.fMode);
+
+ return sf_nlscpy(sf_g, d_name, NAME_MAX,
+ info->name.String.utf8, info->name.u16Length);
+ }
+
+ return 1;
+}
+
+/**
+ * This is called when vfs wants to populate internal buffers with
+ * directory [dir]s contents. [opaque] is an argument to the
+ * [filldir]. [filldir] magically modifies it's argument - [opaque]
+ * and takes following additional arguments (which i in turn get from
+ * the host via sf_getdent):
+ *
+ * name : name of the entry (i must also supply it's length huh?)
+ * type : type of the entry (FILE | DIR | etc) (i ellect to use DT_UNKNOWN)
+ * pos : position/index of the entry
+ * ino : inode number of the entry (i fake those)
+ *
+ * [dir] contains:
+ * f_pos : cursor into the directory listing
+ * private_data : mean of communication with the host side
+ *
+ * Extract elements from the directory listing (incrementing f_pos
+ * along the way) and feed them to [filldir] until:
+ *
+ * a. there are no more entries (i.e. sf_getdent set done to 1)
+ * b. failure to compute fake inode number
+ * c. filldir returns an error (see comment on that)
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+static int sf_dir_iterate(struct file *dir, struct dir_context *ctx)
+#else
+static int sf_dir_read(struct file *dir, void *opaque, filldir_t filldir)
+#endif
+{
+ TRACE();
+ for (;;) {
+ int err;
+ ino_t fake_ino;
+ loff_t sanity;
+ char d_name[NAME_MAX];
+ int d_type = DT_UNKNOWN;
+
+ err = sf_getdent(dir, d_name, &d_type);
+ switch (err) {
+ case 1:
+ return 0;
+
+ case 0:
+ break;
+
+ case -1:
+ default:
+ /* skip erroneous entry and proceed */
+ LogFunc(("sf_getdent error %d\n", err));
+ dir->f_pos += 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ ctx->pos += 1;
+#endif
+ continue;
+ }
+
+ /* d_name now contains a valid entry name */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ sanity = ctx->pos + 0xbeef;
+#else
+ sanity = dir->f_pos + 0xbeef;
+#endif
+ fake_ino = sanity;
+ if (sanity - fake_ino) {
+ LogRelFunc(("can not compute ino\n"));
+ return -EINVAL;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ if (!dir_emit(ctx, d_name, strlen(d_name), fake_ino, d_type)) {
+ LogFunc(("dir_emit failed\n"));
+ return 0;
+ }
+#else
+ err =
+ filldir(opaque, d_name, strlen(d_name), dir->f_pos,
+ fake_ino, d_type);
+ if (err) {
+ LogFunc(("filldir returned error %d\n", err));
+ /* Rely on the fact that filldir returns error
+ only when it runs out of space in opaque */
+ return 0;
+ }
+#endif
+
+ dir->f_pos += 1;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ ctx->pos += 1;
+#endif
+ }
+
+ BUG();
+}
+
+struct file_operations sf_dir_fops = {
+ .open = sf_dir_open,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+ .iterate = sf_dir_iterate,
+#else
+ .readdir = sf_dir_read,
+#endif
+ .release = sf_dir_release,
+ .read = generic_read_dir
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
+ , .llseek = generic_file_llseek
+#endif
+};
+
+/* iops */
+
+/**
+ * This is called when vfs failed to locate dentry in the cache. The
+ * job of this function is to allocate inode and link it to dentry.
+ * [dentry] contains the name to be looked in the [parent] directory.
+ * Failure to locate the name is not a "hard" error, in this case NULL
+ * inode is added to [dentry] and vfs should proceed trying to create
+ * the entry via other means. NULL(or "positive" pointer) ought to be
+ * returned in case of success and "negative" pointer on error
+ */
+static struct dentry *sf_lookup(struct inode *parent, struct dentry *dentry
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ , unsigned int flags
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ , struct nameidata *nd
+#endif
+ )
+{
+ int err;
+ struct sf_inode_info *sf_i, *sf_new_i;
+ struct sf_glob_info *sf_g;
+ SHFLSTRING *path;
+ struct inode *inode;
+ ino_t ino;
+ SHFLFSOBJINFO fsinfo;
+
+ TRACE();
+ sf_g = GET_GLOB_INFO(parent->i_sb);
+ sf_i = GET_INODE_INFO(parent);
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ err = sf_stat(__func__, sf_g, path, &fsinfo, 1);
+ if (err) {
+ if (err == -ENOENT) {
+ /* -ENOENT: add NULL inode to dentry so it later can be
+ created via call to create/mkdir/open */
+ kfree(path);
+ inode = NULL;
+ } else
+ goto fail1;
+ } else {
+ sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL);
+ if (!sf_new_i) {
+ LogRelFunc(("could not allocate memory for new inode info\n"));
+ err = -ENOMEM;
+ goto fail1;
+ }
+ sf_new_i->handle = SHFL_HANDLE_NIL;
+ sf_new_i->force_reread = 0;
+
+ ino = iunique(parent->i_sb, 1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ inode = iget_locked(parent->i_sb, ino);
+#else
+ inode = iget(parent->i_sb, ino);
+#endif
+ if (!inode) {
+ LogFunc(("iget failed\n"));
+ err = -ENOMEM; /* XXX: ??? */
+ goto fail2;
+ }
+
+ SET_INODE_INFO(inode, sf_new_i);
+ sf_init_inode(sf_g, inode, &fsinfo);
+ sf_new_i->path = path;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ unlock_new_inode(inode);
+#endif
+ }
+
+ sf_i->force_restat = 0;
+ dentry->d_time = jiffies;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ d_set_d_op(dentry, &sf_dentry_ops);
+#else
+ dentry->d_op = &sf_dentry_ops;
+#endif
+ d_add(dentry, inode);
+ return NULL;
+
+ fail2:
+ kfree(sf_new_i);
+
+ fail1:
+ kfree(path);
+
+ fail0:
+ return ERR_PTR(err);
+}
+
+/**
+ * This should allocate memory for sf_inode_info, compute a unique inode
+ * number, get an inode from vfs, initialize inode info, instantiate
+ * dentry.
+ *
+ * @param parent inode entry of the directory
+ * @param dentry directory cache entry
+ * @param path path name
+ * @param info file information
+ * @param handle handle
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_instantiate(struct inode *parent, struct dentry *dentry,
+ SHFLSTRING * path, PSHFLFSOBJINFO info,
+ SHFLHANDLE handle)
+{
+ int err;
+ ino_t ino;
+ struct inode *inode;
+ struct sf_inode_info *sf_new_i;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb);
+
+ TRACE();
+ BUG_ON(!sf_g);
+
+ sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL);
+ if (!sf_new_i) {
+ LogRelFunc(("could not allocate inode info.\n"));
+ err = -ENOMEM;
+ goto fail0;
+ }
+
+ ino = iunique(parent->i_sb, 1);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ inode = iget_locked(parent->i_sb, ino);
+#else
+ inode = iget(parent->i_sb, ino);
+#endif
+ if (!inode) {
+ LogFunc(("iget failed\n"));
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ sf_init_inode(sf_g, inode, info);
+ sf_new_i->path = path;
+ SET_INODE_INFO(inode, sf_new_i);
+ sf_new_i->force_restat = 1;
+ sf_new_i->force_reread = 0;
+
+ d_instantiate(dentry, inode);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ unlock_new_inode(inode);
+#endif
+
+ /* Store this handle if we leave the handle open. */
+ sf_new_i->handle = handle;
+ return 0;
+
+ fail1:
+ kfree(sf_new_i);
+
+ fail0:
+ return err;
+
+}
+
+/**
+ * Create a new regular file / directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @param fDirectory true if directory, false otherwise
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_create_aux(struct inode *parent, struct dentry *dentry,
+ umode_t mode, int fDirectory)
+{
+ int rc, err;
+ SHFLCREATEPARMS params;
+ SHFLSTRING *path;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(parent);
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb);
+
+ TRACE();
+ BUG_ON(!sf_i);
+ BUG_ON(!sf_g);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = 0
+ | SHFL_CF_ACT_CREATE_IF_NEW
+ | SHFL_CF_ACT_FAIL_IF_EXISTS
+ | SHFL_CF_ACCESS_READWRITE | (fDirectory ? SHFL_CF_DIRECTORY : 0);
+ params.Info.Attr.fMode = 0
+ | (fDirectory ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE)
+ | (mode & S_IRWXUGO);
+ params.Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+
+ LogFunc(("sf_create_aux: calling VbglR0SfCreate, folder %s, flags %#x\n", path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, path, &params);
+ if (RT_FAILURE(rc)) {
+ if (rc == VERR_WRITE_PROTECT) {
+ err = -EROFS;
+ goto fail1;
+ }
+ err = -EPROTO;
+ LogFunc(("(%d): VbglR0SfCreate(%s) failed rc=%Rrc\n",
+ fDirectory, sf_i->path->String.utf8, rc));
+ goto fail1;
+ }
+
+ if (params.Result != SHFL_FILE_CREATED) {
+ err = -EPERM;
+ LogFunc(("(%d): could not create file %s result=%d\n",
+ fDirectory, sf_i->path->String.utf8, params.Result));
+ goto fail1;
+ }
+
+ err = sf_instantiate(parent, dentry, path, &params.Info,
+ fDirectory ? SHFL_HANDLE_NIL : params.Handle);
+ if (err) {
+ LogFunc(("(%d): could not instantiate dentry for %s err=%d\n",
+ fDirectory, sf_i->path->String.utf8, err));
+ goto fail2;
+ }
+
+ /*
+ * Don't close this handle right now. We assume that the same file is
+ * opened with sf_reg_open() and later closed with sf_reg_close(). Save
+ * the handle in between. Does not apply to directories. True?
+ */
+ if (fDirectory) {
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("(%d): VbglR0SfClose failed rc=%Rrc\n",
+ fDirectory, rc));
+ }
+
+ sf_i->force_restat = 1;
+ return 0;
+
+ fail2:
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("(%d): VbglR0SfClose failed rc=%Rrc\n", fDirectory,
+ rc));
+
+ fail1:
+ kfree(path);
+
+ fail0:
+ return err;
+}
+
+/**
+ * Create a new regular file.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @param excl Possible O_EXCL...
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) || defined(DOXYGEN_RUNNING)
+static int sf_create(struct inode *parent, struct dentry *dentry, umode_t mode,
+ bool excl)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+static int sf_create(struct inode *parent, struct dentry *dentry, umode_t mode,
+ struct nameidata *nd)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int sf_create(struct inode *parent, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+#else
+static int sf_create(struct inode *parent, struct dentry *dentry, int mode)
+#endif
+{
+ TRACE();
+ return sf_create_aux(parent, dentry, mode, 0);
+}
+
+/**
+ * Create a new directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param mode file mode
+ * @returns 0 on success, Linux error code otherwise
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
+static int sf_mkdir(struct inode *parent, struct dentry *dentry, umode_t mode)
+#else
+static int sf_mkdir(struct inode *parent, struct dentry *dentry, int mode)
+#endif
+{
+ TRACE();
+ return sf_create_aux(parent, dentry, mode, 1);
+}
+
+/**
+ * Remove a regular file / directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @param fDirectory true if directory, false otherwise
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_unlink_aux(struct inode *parent, struct dentry *dentry,
+ int fDirectory)
+{
+ int rc, err;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(parent);
+ SHFLSTRING *path;
+ uint32_t fFlags;
+
+ TRACE();
+ BUG_ON(!sf_g);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ fFlags = fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE;
+ if (dentry->d_inode && ((dentry->d_inode->i_mode & S_IFLNK) == S_IFLNK))
+ fFlags |= SHFL_REMOVE_SYMLINK;
+ rc = VbglR0SfRemove(&client_handle, &sf_g->map, path, fFlags);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("(%d): VbglR0SfRemove(%s) failed rc=%Rrc\n",
+ fDirectory, path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail1;
+ }
+
+ /* directory access/change time changed */
+ sf_i->force_restat = 1;
+ /* directory content changed */
+ sf_i->force_reread = 1;
+
+ err = 0;
+
+ fail1:
+ kfree(path);
+
+ fail0:
+ return err;
+}
+
+/**
+ * Remove a regular file.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_unlink(struct inode *parent, struct dentry *dentry)
+{
+ TRACE();
+ return sf_unlink_aux(parent, dentry, 0);
+}
+
+/**
+ * Remove a directory.
+ *
+ * @param parent inode of the directory
+ * @param dentry directory cache entry
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_rmdir(struct inode *parent, struct dentry *dentry)
+{
+ TRACE();
+ return sf_unlink_aux(parent, dentry, 1);
+}
+
+/**
+ * Rename a regular file / directory.
+ *
+ * @param old_parent inode of the old parent directory
+ * @param old_dentry old directory cache entry
+ * @param new_parent inode of the new parent directory
+ * @param new_dentry new directory cache entry
+ * @param flags flags
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_rename(struct inode *old_parent, struct dentry *old_dentry,
+ struct inode *new_parent, struct dentry *new_dentry
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+ , unsigned flags
+#endif
+ )
+{
+ int err = 0, rc = VINF_SUCCESS;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(old_parent->i_sb);
+
+ TRACE();
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+ if (flags) {
+ LogFunc(("rename with flags=%x\n", flags));
+ return -EINVAL;
+ }
+#endif
+
+ if (sf_g != GET_GLOB_INFO(new_parent->i_sb)) {
+ LogFunc(("rename with different roots\n"));
+ err = -EINVAL;
+ } else {
+ struct sf_inode_info *sf_old_i = GET_INODE_INFO(old_parent);
+ struct sf_inode_info *sf_new_i = GET_INODE_INFO(new_parent);
+ /* As we save the relative path inside the inode structure, we need to change
+ this if the rename is successful. */
+ struct sf_inode_info *sf_file_i =
+ GET_INODE_INFO(old_dentry->d_inode);
+ SHFLSTRING *old_path;
+ SHFLSTRING *new_path;
+
+ BUG_ON(!sf_old_i);
+ BUG_ON(!sf_new_i);
+ BUG_ON(!sf_file_i);
+
+ old_path = sf_file_i->path;
+ err = sf_path_from_dentry(__func__, sf_g, sf_new_i,
+ new_dentry, &new_path);
+ if (err)
+ LogFunc(("failed to create new path\n"));
+ else {
+ int fDir =
+ ((old_dentry->d_inode->i_mode & S_IFDIR) != 0);
+
+ rc = VbglR0SfRename(&client_handle, &sf_g->map,
+ old_path, new_path,
+ fDir ? 0 : SHFL_RENAME_FILE |
+ SHFL_RENAME_REPLACE_IF_EXISTS);
+ if (RT_SUCCESS(rc)) {
+ kfree(old_path);
+ sf_new_i->force_restat = 1;
+ sf_old_i->force_restat = 1; /* XXX: needed? */
+ /* Set the new relative path in the inode. */
+ sf_file_i->path = new_path;
+ } else {
+ LogFunc(("VbglR0SfRename failed rc=%Rrc\n",
+ rc));
+ err = -RTErrConvertToErrno(rc);
+ kfree(new_path);
+ }
+ }
+ }
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int sf_symlink(struct inode *parent, struct dentry *dentry,
+ const char *symname)
+{
+ int err;
+ int rc;
+ struct sf_inode_info *sf_i;
+ struct sf_glob_info *sf_g;
+ SHFLSTRING *path, *ssymname;
+ SHFLFSOBJINFO info;
+ int symname_len = strlen(symname) + 1;
+
+ TRACE();
+ sf_g = GET_GLOB_INFO(parent->i_sb);
+ sf_i = GET_INODE_INFO(parent);
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path);
+ if (err)
+ goto fail0;
+
+ ssymname =
+ kmalloc(offsetof(SHFLSTRING, String.utf8) + symname_len,
+ GFP_KERNEL);
+ if (!ssymname) {
+ LogRelFunc(("kmalloc failed, caller=sf_symlink\n"));
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ ssymname->u16Length = symname_len - 1;
+ ssymname->u16Size = symname_len;
+ memcpy(ssymname->String.utf8, symname, symname_len);
+
+ rc = VbglR0SfSymlink(&client_handle, &sf_g->map, path, ssymname, &info);
+ kfree(ssymname);
+
+ if (RT_FAILURE(rc)) {
+ if (rc == VERR_WRITE_PROTECT) {
+ err = -EROFS;
+ goto fail1;
+ }
+ LogFunc(("VbglR0SfSymlink(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -EPROTO;
+ goto fail1;
+ }
+
+ err = sf_instantiate(parent, dentry, path, &info, SHFL_HANDLE_NIL);
+ if (err) {
+ LogFunc(("could not instantiate dentry for %s err=%d\n",
+ sf_i->path->String.utf8, err));
+ goto fail1;
+ }
+
+ sf_i->force_restat = 1;
+ return 0;
+
+ fail1:
+ kfree(path);
+ fail0:
+ return err;
+}
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
+
+struct inode_operations sf_dir_iops = {
+ .lookup = sf_lookup,
+ .create = sf_create,
+ .mkdir = sf_mkdir,
+ .rmdir = sf_rmdir,
+ .unlink = sf_unlink,
+ .rename = sf_rename,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+ .revalidate = sf_inode_revalidate
+#else
+ .getattr = sf_getattr,
+ .setattr = sf_setattr,
+ .symlink = sf_symlink
+#endif
+};
diff --git a/src/VBox/Additions/linux/sharedfolders/files_vboxsf b/src/VBox/Additions/linux/sharedfolders/files_vboxsf
new file mode 100755
index 00000000..9660f366
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/files_vboxsf
@@ -0,0 +1,89 @@
+#!/bin/sh
+# $Id: files_vboxsf $
+## @file
+# Shared file between Makefile.kmk and export_modules.sh.
+#
+
+#
+# Copyright (C) 2007-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+FILES_VBOXSF_NOBIN=" \
+ ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \
+ ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \
+ ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \
+ ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \
+ ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \
+ ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \
+ ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \
+ ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \
+ ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \
+ ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \
+ ${PATH_ROOT}/include/iprt/fs.h=>include/iprt/fs.h \
+ ${PATH_ROOT}/include/iprt/latin1.h=>include/iprt/latin1.h \
+ ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \
+ ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.h \
+ ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \
+ ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \
+ ${PATH_ROOT}/include/iprt/path.h=>include/iprt/path.h \
+ ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \
+ ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \
+ ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \
+ ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \
+ ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \
+ ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \
+ ${PATH_ROOT}/include/iprt/uint64.h=>include/iprt/uint64.h \
+ ${PATH_ROOT}/include/iprt/uni.h=>include/iprt/uni.h \
+ ${PATH_ROOT}/include/iprt/utf16.h=>include/iprt/utf16.h \
+ ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \
+ ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \
+ ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \
+ ${PATH_ROOT}/include/VBox/ostypes.h=>include/VBox/ostypes.h \
+ ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \
+ ${PATH_ROOT}/include/VBox/shflsvc.h=>include/VBox/shflsvc.h \
+ ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuest.h=>include/VBox/VBoxGuest.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestCoreTypes.h=>include/VBox/VBoxGuestCoreTypes.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestLib.h=>include/VBox/VBoxGuestLib.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestLibSharedFolders.h=>include/VBox/VBoxGuestLibSharedFolders.h \
+ ${PATH_ROOT}/include/VBox/VBoxGuestMangling.h=>include/VBox/VBoxGuestMangling.h \
+ ${PATH_ROOT}/include/VBox/VMMDev.h=>include/VBox/VMMDev.h \
+ ${PATH_ROOT}/include/VBox/VMMDevCoreTypes.h=>include/VBox/VMMDevCoreTypes.h \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibInternal.h=>VBoxGuestR0LibInternal.h \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp=>VBoxGuestR0LibHGCM.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc.cpp=>VBoxGuestR0LibIdc.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibIdc-unix.cpp=>VBoxGuestR0LibIdc-unix.c \
+ ${PATH_ROOT}/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c=>VBoxGuestR0LibSharedFolders.c \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.header=>Makefile.include.header \
+ ${PATH_ROOT}/src/VBox/Installer/linux/Makefile.include.footer=>Makefile.include.footer \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>divdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>moddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>qdivrem.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>quad.h \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>udivdi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>udivmoddi4.c \
+ ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>umoddi3.c \
+ ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/Makefile.module=>Makefile \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/dirops.c=>dirops.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/lnkops.c=>lnkops.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/regops.c=>regops.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/utils.c=>utils.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/vbsfmount.h=>vbsfmount.h \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/vfsmod.c=>vfsmod.c \
+ ${PATH_ROOT}/src/VBox/Additions/linux/sharedfolders/vfsmod.h=>vfsmod.h \
+ ${PATH_OUT}/version-generated.h=>version-generated.h \
+ ${PATH_OUT}/revision-generated.h=>revision-generated.h \
+ ${PATH_OUT}/product-generated.h=>product-generated.h \
+"
+
+FILES_VBOXSF_BIN=" \
+"
diff --git a/src/VBox/Additions/linux/sharedfolders/lnkops.c b/src/VBox/Additions/linux/sharedfolders/lnkops.c
new file mode 100644
index 00000000..d735b677
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/lnkops.c
@@ -0,0 +1,119 @@
+/* $Id: lnkops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, operations for symbolic links.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * 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 "vfsmod.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+static const char *sf_follow_link(struct dentry *dentry, void **cookie)
+# else
+static void *sf_follow_link(struct dentry *dentry, struct nameidata *nd)
+# endif
+{
+ struct inode *inode = dentry->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ int error = -ENOMEM;
+ char *path = (char *)get_zeroed_page(GFP_KERNEL);
+ int rc;
+
+ if (path) {
+ error = 0;
+ rc = VbglR0SfReadLink(&client_handle, &sf_g->map, sf_i->path,
+ PATH_MAX, path);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n", __func__, rc));
+ free_page((unsigned long)path);
+ error = -EPROTO;
+ }
+ }
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ return error ? ERR_PTR(error) : (*cookie = path);
+# else
+ nd_set_link(nd, error ? ERR_PTR(error) : path);
+ return NULL;
+# endif
+}
+
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+static void sf_put_link(struct dentry *dentry, struct nameidata *nd,
+ void *cookie)
+{
+ char *page = nd_get_link(nd);
+ if (!IS_ERR(page))
+ free_page((unsigned long)page);
+}
+# endif
+
+# else /* LINUX_VERSION_CODE >= 4.5.0 */
+static const char *sf_get_link(struct dentry *dentry, struct inode *inode,
+ struct delayed_call *done)
+{
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ char *path;
+ int rc;
+
+ if (!dentry)
+ return ERR_PTR(-ECHILD);
+ path = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+ rc = VbglR0SfReadLink(&client_handle, &sf_g->map, sf_i->path, PATH_MAX,
+ path);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n",
+ __func__, rc));
+ kfree(path);
+ return ERR_PTR(-EPROTO);
+ }
+ set_delayed_call(done, kfree_link, path);
+ return path;
+}
+# endif /* LINUX_VERSION_CODE >= 4.5.0 */
+
+struct inode_operations sf_lnk_iops = {
+# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
+ .readlink = generic_readlink,
+# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
+ .get_link = sf_get_link
+# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ .follow_link = sf_follow_link,
+ .put_link = free_page_put_link,
+# else
+ .follow_link = sf_follow_link,
+ .put_link = sf_put_link
+# endif
+};
+
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
diff --git a/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c b/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c
new file mode 100644
index 00000000..7392b047
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c
@@ -0,0 +1,538 @@
+/* $Id: mount.vboxsf.c $ */
+/** @file
+ * VirtualBox Guest Additions for Linux - mount(8) helper.
+ *
+ * Parses options provided by mount (or user directly)
+ * Packs them into struct vbsfmount and passes to mount(2)
+ * Optionally adds entries to mtab
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+/* #define DEBUG */
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <mntent.h>
+#include <limits.h>
+#include <iconv.h>
+
+#include "vbsfmount.h"
+
+#include <iprt/assert.h>
+
+
+#define PANIC_ATTR __attribute ((noreturn, __format__ (__printf__, 1, 2)))
+
+static void PANIC_ATTR
+panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+static void PANIC_ATTR
+panic_err(const char *fmt, ...)
+{
+ va_list ap;
+ int errno_code = errno;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, ": %s\n", strerror(errno_code));
+ exit(EXIT_FAILURE);
+}
+
+static int
+safe_atoi(const char *s, size_t size, int base)
+{
+ char *endptr;
+ long long int val = strtoll(s, &endptr, base);
+
+ if (val < INT_MIN || val > INT_MAX || endptr < s + size)
+ {
+ errno = ERANGE;
+ panic_err("could not convert %.*s to integer, result = %d",
+ (int)size, s, (int) val);
+ }
+ return (int)val;
+}
+
+static void
+process_mount_opts(const char *s, struct vbsf_mount_opts *opts)
+{
+ const char *next = s;
+ size_t len;
+ typedef enum handler_opt
+ {
+ HORW,
+ HORO,
+ HOUID,
+ HOGID,
+ HOTTL,
+ HODMODE,
+ HOFMODE,
+ HOUMASK,
+ HODMASK,
+ HOFMASK,
+ HOIOCHARSET,
+ HOCONVERTCP,
+ HONOEXEC,
+ HOEXEC,
+ HONODEV,
+ HODEV,
+ HONOSUID,
+ HOSUID,
+ HOREMOUNT,
+ HONOAUTO,
+ HONIGNORE
+ } handler_opt;
+ struct
+ {
+ const char *name;
+ handler_opt opt;
+ int has_arg;
+ const char *desc;
+ } handlers[] =
+ {
+ {"rw", HORW, 0, "mount read write (default)"},
+ {"ro", HORO, 0, "mount read only"},
+ {"uid", HOUID, 1, "default file owner user id"},
+ {"gid", HOGID, 1, "default file owner group id"},
+ {"ttl", HOTTL, 1, "time to live for dentry"},
+ {"iocharset", HOIOCHARSET, 1, "i/o charset (default utf8)"},
+ {"convertcp", HOCONVERTCP, 1, "convert share name from given charset to utf8"},
+ {"dmode", HODMODE, 1, "mode of all directories"},
+ {"fmode", HOFMODE, 1, "mode of all regular files"},
+ {"umask", HOUMASK, 1, "umask of directories and regular files"},
+ {"dmask", HODMASK, 1, "umask of directories"},
+ {"fmask", HOFMASK, 1, "umask of regular files"},
+ {"noexec", HONOEXEC, 0, 0 }, /* don't document these options directly here */
+ {"exec", HOEXEC, 0, 0 }, /* as they are well known and described in the */
+ {"nodev", HONODEV, 0, 0 }, /* usual manpages */
+ {"dev", HODEV, 0, 0 },
+ {"nosuid", HONOSUID, 0, 0 },
+ {"suid", HOSUID, 0, 0 },
+ {"remount", HOREMOUNT, 0, 0 },
+ {"noauto", HONOAUTO, 0, 0 },
+ {"_netdev", HONIGNORE, 0, 0 },
+ {NULL, 0, 0, NULL}
+ }, *handler;
+
+ while (next)
+ {
+ const char *val;
+ size_t key_len, val_len;
+
+ s = next;
+ next = strchr(s, ',');
+ if (!next)
+ {
+ len = strlen(s);
+ }
+ else
+ {
+ len = next - s;
+ next += 1;
+ if (!*next)
+ next = 0;
+ }
+
+ val = NULL;
+ val_len = 0;
+ for (key_len = 0; key_len < len; ++key_len)
+ {
+ if (s[key_len] == '=')
+ {
+ if (key_len + 1 < len)
+ {
+ val = s + key_len + 1;
+ val_len = len - key_len - 1;
+ }
+ break;
+ }
+ }
+
+ for (handler = handlers; handler->name; ++handler)
+ {
+ size_t j;
+ for (j = 0; j < key_len && handler->name[j] == s[j]; ++j)
+ ;
+
+ if (j == key_len && !handler->name[j])
+ {
+ if (handler->has_arg)
+ {
+ if (!(val && *val))
+ {
+ panic("%.*s requires an argument (i.e. %.*s=<arg>)\n",
+ (int)len, s, (int)len, s);
+ }
+ }
+
+ switch(handler->opt)
+ {
+ case HORW:
+ opts->ronly = 0;
+ break;
+ case HORO:
+ opts->ronly = 1;
+ break;
+ case HONOEXEC:
+ opts->noexec = 1;
+ break;
+ case HOEXEC:
+ opts->noexec = 0;
+ break;
+ case HONODEV:
+ opts->nodev = 1;
+ break;
+ case HODEV:
+ opts->nodev = 0;
+ break;
+ case HONOSUID:
+ opts->nosuid = 1;
+ break;
+ case HOSUID:
+ opts->nosuid = 0;
+ break;
+ case HOREMOUNT:
+ opts->remount = 1;
+ break;
+ case HOUID:
+ /** @todo convert string to id. */
+ opts->uid = safe_atoi(val, val_len, 10);
+ break;
+ case HOGID:
+ /** @todo convert string to id. */
+ opts->gid = safe_atoi(val, val_len, 10);
+ break;
+ case HOTTL:
+ opts->ttl = safe_atoi(val, val_len, 10);
+ break;
+ case HODMODE:
+ opts->dmode = safe_atoi(val, val_len, 8);
+ break;
+ case HOFMODE:
+ opts->fmode = safe_atoi(val, val_len, 8);
+ break;
+ case HOUMASK:
+ opts->dmask = opts->fmask = safe_atoi(val, val_len, 8);
+ break;
+ case HODMASK:
+ opts->dmask = safe_atoi(val, val_len, 8);
+ break;
+ case HOFMASK:
+ opts->fmask = safe_atoi(val, val_len, 8);
+ break;
+ case HOIOCHARSET:
+ if (val_len + 1 > sizeof(opts->nls_name))
+ {
+ panic("iocharset name too long\n");
+ }
+ memcpy(opts->nls_name, val, val_len);
+ opts->nls_name[val_len] = 0;
+ break;
+ case HOCONVERTCP:
+ opts->convertcp = malloc(val_len + 1);
+ if (!opts->convertcp)
+ {
+ panic_err("could not allocate memory");
+ }
+ memcpy(opts->convertcp, val, val_len);
+ opts->convertcp[val_len] = 0;
+ break;
+ case HONOAUTO:
+ case HONIGNORE:
+ break;
+ }
+ break;
+ }
+ continue;
+ }
+
+ if ( !handler->name
+ && !opts->sloppy)
+ {
+ fprintf(stderr, "unknown mount option `%.*s'\n", (int)len, s);
+ fprintf(stderr, "valid options:\n");
+
+ for (handler = handlers; handler->name; ++handler)
+ {
+ if (handler->desc)
+ fprintf(stderr, " %-10s%s %s\n", handler->name,
+ handler->has_arg ? "=<arg>" : "", handler->desc);
+ }
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void
+convertcp(char *in_codeset, char *host_name, struct vbsf_mount_info_new *info)
+{
+ char *i = host_name;
+ char *o = info->name;
+ size_t ib = strlen(host_name);
+ size_t ob = sizeof(info->name) - 1;
+ iconv_t cd;
+
+ cd = iconv_open("UTF-8", in_codeset);
+ if (cd == (iconv_t) -1)
+ {
+ panic_err("could not convert share name, iconv_open `%s' failed",
+ in_codeset);
+ }
+
+ while (ib)
+ {
+ size_t c = iconv(cd, &i, &ib, &o, &ob);
+ if (c == (size_t) -1)
+ {
+ panic_err("could not convert share name(%s) at %d",
+ host_name, (int)(strlen (host_name) - ib));
+ }
+ }
+ *o = 0;
+}
+
+
+/**
+ * Print out a usage message and exit.
+ *
+ * @returns 1
+ * @param argv0 The name of the application
+ */
+static int usage(char *argv0)
+{
+ printf("Usage: %s [OPTIONS] NAME MOUNTPOINT\n"
+ "Mount the VirtualBox shared folder NAME from the host system to MOUNTPOINT.\n"
+ "\n"
+ " -w mount the shared folder writable (the default)\n"
+ " -r mount the shared folder read-only\n"
+ " -n do not create an mtab entry\n"
+ " -s sloppy parsing, ignore unrecognized mount options\n"
+ " -o OPTION[,OPTION...] use the mount options specified\n"
+ "\n", argv0);
+ printf("Available mount options are:\n"
+ " rw mount writable (the default)\n"
+ " ro mount read only\n"
+ " uid=UID set the default file owner user id to UID\n"
+ " gid=GID set the default file owner group id to GID\n"
+ " ttl=TTL set the \"time to live\" to TID for the dentry\n");
+ printf(" dmode=MODE override the mode of all directories to (octal) MODE\n"
+ " fmode=MODE override the mode of all regular files to (octal) MODE\n"
+ " umask=UMASK set the umask to (octal) UMASK\n");
+ printf(" dmask=UMASK set the umask applied to directories only\n"
+ " fmask=UMASK set the umask applied to regular files only\n"
+ " iocharset CHARSET use the character set CHARSET for I/O operations\n"
+ " (default set is utf8)\n"
+ " convertcp CHARSET convert the folder name from CHARSET to utf8\n"
+ "\n");
+ printf("Less common used options:\n"
+ " noexec,exec,nodev,dev,nosuid,suid\n");
+ return EXIT_FAILURE;
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int err;
+ int nomtab = 0;
+ unsigned long flags = MS_NODEV;
+ char *host_name;
+ char *mount_point;
+ struct vbsf_mount_info_new mntinf;
+ struct vbsf_mount_opts opts =
+ {
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* ttl */
+ ~0U, /* dmode */
+ ~0U, /* fmode*/
+ 0, /* dmask */
+ 0, /* fmask */
+ 0, /* ronly */
+ 0, /* sloppy */
+ 0, /* noexec */
+ 0, /* nodev */
+ 0, /* nosuid */
+ 0, /* remount */
+ "\0", /* nls_name */
+ NULL, /* convertcp */
+ };
+ AssertCompile(sizeof(uid_t) == sizeof(int));
+ AssertCompile(sizeof(gid_t) == sizeof(int));
+
+ mntinf.nullchar = '\0';
+ mntinf.signature[0] = VBSF_MOUNT_SIGNATURE_BYTE_0;
+ mntinf.signature[1] = VBSF_MOUNT_SIGNATURE_BYTE_1;
+ mntinf.signature[2] = VBSF_MOUNT_SIGNATURE_BYTE_2;
+ mntinf.length = sizeof(mntinf);
+ mntinf.tag[0] = '\0';
+
+ if (getuid())
+ panic("Only root can mount shared folders from the host.\n");
+
+ if (!argv[0])
+ argv[0] = "mount.vboxsf";
+
+ while ((c = getopt(argc, argv, "rwsno:h")) != -1)
+ {
+ switch (c)
+ {
+ default:
+ fprintf(stderr, "unknown option `%c:%#x'\n", c, c);
+ RT_FALL_THRU();
+ case '?':
+ case 'h':
+ return usage(argv[0]);
+
+ case 'r':
+ opts.ronly = 1;
+ break;
+
+ case 'w':
+ opts.ronly = 0;
+ break;
+
+ case 's':
+ opts.sloppy = 1;
+ break;
+
+ case 'o':
+ process_mount_opts(optarg, &opts);
+ break;
+
+ case 'n':
+ nomtab = 1;
+ break;
+ }
+ }
+
+ if (argc - optind < 2)
+ return usage(argv[0]);
+
+ host_name = argv[optind];
+ mount_point = argv[optind + 1];
+
+ if (opts.convertcp)
+ convertcp(opts.convertcp, host_name, &mntinf);
+ else
+ {
+ if (strlen(host_name) > MAX_HOST_NAME - 1)
+ panic("host name is too big\n");
+
+ strcpy(mntinf.name, host_name);
+ }
+
+ if (strlen(opts.nls_name) > MAX_NLS_NAME - 1)
+ panic("%s: the character set name for I/O is too long.\n", argv[0]);
+
+ strcpy(mntinf.nls_name, opts.nls_name);
+
+ if (opts.ronly)
+ flags |= MS_RDONLY;
+ if (opts.noexec)
+ flags |= MS_NOEXEC;
+ if (opts.nodev)
+ flags |= MS_NODEV;
+ if (opts.remount)
+ flags |= MS_REMOUNT;
+
+ mntinf.uid = opts.uid;
+ mntinf.gid = opts.gid;
+ mntinf.ttl = opts.ttl;
+ mntinf.dmode = opts.dmode;
+ mntinf.fmode = opts.fmode;
+ mntinf.dmask = opts.dmask;
+ mntinf.fmask = opts.fmask;
+
+ /*
+ * Note: When adding and/or modifying parameters of the vboxsf mounting
+ * options you also would have to adjust VBoxServiceAutoMount.cpp
+ * to keep this code here slick without having VbglR3.
+ */
+ err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
+ if (err == -1 && errno == EPROTO)
+ {
+ /* Sometimes the mount utility messes up the share name. Try to
+ * un-mangle it again. */
+ char szCWD[4096];
+ size_t cchCWD;
+ if (!getcwd(szCWD, sizeof(szCWD)))
+ panic_err("%s: failed to get the current working directory", argv[0]);
+ cchCWD = strlen(szCWD);
+ if (!strncmp(host_name, szCWD, cchCWD))
+ {
+ while (host_name[cchCWD] == '/')
+ ++cchCWD;
+ /* We checked before that we have enough space */
+ strcpy(mntinf.name, host_name + cchCWD);
+ }
+ err = mount(host_name, mount_point, "vboxsf", flags, &mntinf);
+ }
+ if (err)
+ panic_err("%s: mounting failed with the error", argv[0]);
+
+ if (!nomtab)
+ {
+ err = vbsfmount_complete(host_name, mount_point, flags, &opts);
+ switch (err)
+ {
+ case 0: /* Success. */
+ break;
+
+ case 1:
+ panic_err("%s: Could not update mount table (failed to create memstream).", argv[0]);
+ break;
+
+ case 2:
+ panic_err("%s: Could not open mount table for update.", argv[0]);
+ break;
+
+ case 3:
+ /* panic_err("%s: Could not add an entry to the mount table.", argv[0]); */
+ break;
+
+ default:
+ panic_err("%s: Unknown error while completing mount operation: %d", argv[0], err);
+ break;
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/src/VBox/Additions/linux/sharedfolders/regops.c b/src/VBox/Additions/linux/sharedfolders/regops.c
new file mode 100644
index 00000000..646d6062
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/regops.c
@@ -0,0 +1,876 @@
+/* $Id: regops.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, regular file inode and file operations.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+/*
+ * Limitations: only COW memory mapping is supported
+ */
+
+#include "vfsmod.h"
+
+static void *alloc_bounce_buffer(size_t * tmp_sizep, PRTCCPHYS physp, size_t
+ xfer_size, const char *caller)
+{
+ size_t tmp_size;
+ void *tmp;
+
+ /* try for big first. */
+ tmp_size = RT_ALIGN_Z(xfer_size, PAGE_SIZE);
+ if (tmp_size > 16U * _1K)
+ tmp_size = 16U * _1K;
+ tmp = kmalloc(tmp_size, GFP_KERNEL);
+ if (!tmp) {
+ /* fall back on a page sized buffer. */
+ tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!tmp) {
+ LogRel(("%s: could not allocate bounce buffer for xfer_size=%zu %s\n", caller, xfer_size));
+ return NULL;
+ }
+ tmp_size = PAGE_SIZE;
+ }
+
+ *tmp_sizep = tmp_size;
+ *physp = virt_to_phys(tmp);
+ return tmp;
+}
+
+static void free_bounce_buffer(void *tmp)
+{
+ kfree(tmp);
+}
+
+/* fops */
+static int sf_reg_read_aux(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_reg_info *sf_r, void *buf,
+ uint32_t * nread, uint64_t pos)
+{
+ /** @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is
+ * contiguous in physical memory (kmalloc or single page), we should
+ * use a physical address here to speed things up. */
+ int rc = VbglR0SfRead(&client_handle, &sf_g->map, sf_r->handle,
+ pos, nread, buf, false /* already locked? */ );
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfRead failed. caller=%s, rc=%Rrc\n", caller,
+ rc));
+ return -EPROTO;
+ }
+ return 0;
+}
+
+static int sf_reg_write_aux(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_reg_info *sf_r, void *buf,
+ uint32_t * nwritten, uint64_t pos)
+{
+ /** @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is
+ * contiguous in physical memory (kmalloc or single page), we should
+ * use a physical address here to speed things up. */
+ int rc = VbglR0SfWrite(&client_handle, &sf_g->map, sf_r->handle,
+ pos, nwritten, buf,
+ false /* already locked? */ );
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfWrite failed. caller=%s, rc=%Rrc\n",
+ caller, rc));
+ return -EPROTO;
+ }
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) \
+ && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+
+void free_pipebuf(struct page *kpage)
+{
+ kunmap(kpage);
+ __free_pages(kpage, 0);
+}
+
+void *sf_pipe_buf_map(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf, int atomic)
+{
+ return 0;
+}
+
+void sf_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *pipe_buf)
+{
+}
+
+void sf_pipe_buf_unmap(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf, void *map_data)
+{
+}
+
+int sf_pipe_buf_steal(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf)
+{
+ return 0;
+}
+
+static void sf_pipe_buf_release(struct pipe_inode_info *pipe,
+ struct pipe_buffer *pipe_buf)
+{
+ free_pipebuf(pipe_buf->page);
+}
+
+int sf_pipe_buf_confirm(struct pipe_inode_info *info,
+ struct pipe_buffer *pipe_buf)
+{
+ return 0;
+}
+
+static struct pipe_buf_operations sf_pipe_buf_ops = {
+ .can_merge = 0,
+ .map = sf_pipe_buf_map,
+ .unmap = sf_pipe_buf_unmap,
+ .confirm = sf_pipe_buf_confirm,
+ .release = sf_pipe_buf_release,
+ .steal = sf_pipe_buf_steal,
+ .get = sf_pipe_buf_get,
+};
+
+#define LOCK_PIPE(pipe) \
+ if (pipe->inode) \
+ mutex_lock(&pipe->inode->i_mutex);
+
+#define UNLOCK_PIPE(pipe) \
+ if (pipe->inode) \
+ mutex_unlock(&pipe->inode->i_mutex);
+
+ssize_t
+sf_splice_read(struct file *in, loff_t * poffset,
+ struct pipe_inode_info *pipe, size_t len, unsigned int flags)
+{
+ size_t bytes_remaining = len;
+ loff_t orig_offset = *poffset;
+ loff_t offset = orig_offset;
+ struct inode *inode = GET_F_DENTRY(in)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = in->private_data;
+ ssize_t retval;
+ struct page *kpage = 0;
+ size_t nsent = 0;
+
+ TRACE();
+ if (!S_ISREG(inode->i_mode)) {
+ LogFunc(("read from non regular file %d\n", inode->i_mode));
+ return -EINVAL;
+ }
+ if (!len) {
+ return 0;
+ }
+
+ LOCK_PIPE(pipe);
+
+ uint32_t req_size = 0;
+ while (bytes_remaining > 0) {
+ kpage = alloc_page(GFP_KERNEL);
+ if (unlikely(kpage == NULL)) {
+ UNLOCK_PIPE(pipe);
+ return -ENOMEM;
+ }
+ req_size = 0;
+ uint32_t nread = req_size =
+ (uint32_t) min(bytes_remaining, (size_t) PAGE_SIZE);
+ uint32_t chunk = 0;
+ void *kbuf = kmap(kpage);
+ while (chunk < req_size) {
+ retval =
+ sf_reg_read_aux(__func__, sf_g, sf_r, kbuf + chunk,
+ &nread, offset);
+ if (retval < 0)
+ goto err;
+ if (nread == 0)
+ break;
+ chunk += nread;
+ offset += nread;
+ nread = req_size - chunk;
+ }
+ if (!pipe->readers) {
+ send_sig(SIGPIPE, current, 0);
+ retval = -EPIPE;
+ goto err;
+ }
+ if (pipe->nrbufs < PIPE_BUFFERS) {
+ struct pipe_buffer *pipebuf =
+ pipe->bufs +
+ ((pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS -
+ 1));
+ pipebuf->page = kpage;
+ pipebuf->ops = &sf_pipe_buf_ops;
+ pipebuf->len = req_size;
+ pipebuf->offset = 0;
+ pipebuf->private = 0;
+ pipebuf->flags = 0;
+ pipe->nrbufs++;
+ nsent += req_size;
+ bytes_remaining -= req_size;
+ if (signal_pending(current))
+ break;
+ } else { /* pipe full */
+
+ if (flags & SPLICE_F_NONBLOCK) {
+ retval = -EAGAIN;
+ goto err;
+ }
+ free_pipebuf(kpage);
+ break;
+ }
+ }
+ UNLOCK_PIPE(pipe);
+ if (!nsent && signal_pending(current))
+ return -ERESTARTSYS;
+ *poffset += nsent;
+ return offset - orig_offset;
+
+ err:
+ UNLOCK_PIPE(pipe);
+ free_pipebuf(kpage);
+ return retval;
+}
+
+#endif /* 2.6.23 <= LINUX_VERSION_CODE < 2.6.31 */
+
+/**
+ * Read from a regular file.
+ *
+ * @param file the file
+ * @param buf the buffer
+ * @param size length of the buffer
+ * @param off offset within the file
+ * @returns the number of read bytes on success, Linux error code otherwise
+ */
+static ssize_t sf_reg_read(struct file *file, char *buf, size_t size,
+ loff_t * off)
+{
+ int err;
+ void *tmp;
+ RTCCPHYS tmp_phys;
+ size_t tmp_size;
+ size_t left = size;
+ ssize_t total_bytes_read = 0;
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ loff_t pos = *off;
+
+ TRACE();
+ if (!S_ISREG(inode->i_mode)) {
+ LogFunc(("read from non regular file %d\n", inode->i_mode));
+ return -EINVAL;
+ }
+
+ /** @todo XXX Check read permission according to inode->i_mode! */
+
+ if (!size)
+ return 0;
+
+ tmp =
+ alloc_bounce_buffer(&tmp_size, &tmp_phys, size,
+ __PRETTY_FUNCTION__);
+ if (!tmp)
+ return -ENOMEM;
+
+ while (left) {
+ uint32_t to_read, nread;
+
+ to_read = tmp_size;
+ if (to_read > left)
+ to_read = (uint32_t) left;
+
+ nread = to_read;
+
+ err = sf_reg_read_aux(__func__, sf_g, sf_r, tmp, &nread, pos);
+ if (err)
+ goto fail;
+
+ if (copy_to_user(buf, tmp, nread)) {
+ err = -EFAULT;
+ goto fail;
+ }
+
+ pos += nread;
+ left -= nread;
+ buf += nread;
+ total_bytes_read += nread;
+ if (nread != to_read)
+ break;
+ }
+
+ *off += total_bytes_read;
+ free_bounce_buffer(tmp);
+ return total_bytes_read;
+
+ fail:
+ free_bounce_buffer(tmp);
+ return err;
+}
+
+/**
+ * Write to a regular file.
+ *
+ * @param file the file
+ * @param buf the buffer
+ * @param size length of the buffer
+ * @param off offset within the file
+ * @returns the number of written bytes on success, Linux error code otherwise
+ */
+static ssize_t sf_reg_write(struct file *file, const char *buf, size_t size,
+ loff_t * off)
+{
+ int err;
+ void *tmp;
+ RTCCPHYS tmp_phys;
+ size_t tmp_size;
+ size_t left = size;
+ ssize_t total_bytes_written = 0;
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ loff_t pos;
+
+ TRACE();
+ BUG_ON(!sf_i);
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_r);
+
+ if (!S_ISREG(inode->i_mode)) {
+ LogFunc(("write to non regular file %d\n", inode->i_mode));
+ return -EINVAL;
+ }
+
+ pos = *off;
+ if (file->f_flags & O_APPEND) {
+ pos = inode->i_size;
+ *off = pos;
+ }
+
+ /** @todo XXX Check write permission according to inode->i_mode! */
+
+ if (!size)
+ return 0;
+
+ tmp =
+ alloc_bounce_buffer(&tmp_size, &tmp_phys, size,
+ __PRETTY_FUNCTION__);
+ if (!tmp)
+ return -ENOMEM;
+
+ while (left) {
+ uint32_t to_write, nwritten;
+
+ to_write = tmp_size;
+ if (to_write > left)
+ to_write = (uint32_t) left;
+
+ nwritten = to_write;
+
+ if (copy_from_user(tmp, buf, to_write)) {
+ err = -EFAULT;
+ goto fail;
+ }
+
+ err =
+ VbglR0SfWritePhysCont(&client_handle, &sf_g->map,
+ sf_r->handle, pos, &nwritten,
+ tmp_phys);
+ err = RT_FAILURE(err) ? -EPROTO : 0;
+ if (err)
+ goto fail;
+
+ pos += nwritten;
+ left -= nwritten;
+ buf += nwritten;
+ total_bytes_written += nwritten;
+ if (nwritten != to_write)
+ break;
+ }
+
+ *off += total_bytes_written;
+ if (*off > inode->i_size)
+ inode->i_size = *off;
+
+ sf_i->force_restat = 1;
+ free_bounce_buffer(tmp);
+ return total_bytes_written;
+
+ fail:
+ free_bounce_buffer(tmp);
+ return err;
+}
+
+/**
+ * Open a regular file.
+ *
+ * @param inode the inode
+ * @param file the file
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_reg_open(struct inode *inode, struct file *file)
+{
+ int rc, rc_linux = 0;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ struct sf_reg_info *sf_r;
+ SHFLCREATEPARMS params;
+
+ TRACE();
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ LogFunc(("open %s\n", sf_i->path->String.utf8));
+
+ sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL);
+ if (!sf_r) {
+ LogRelFunc(("could not allocate reg info\n"));
+ return -ENOMEM;
+ }
+
+ /* Already open? */
+ if (sf_i->handle != SHFL_HANDLE_NIL) {
+ /*
+ * This inode was created with sf_create_aux(). Check the CreateFlags:
+ * O_CREAT, O_TRUNC: inherent true (file was just created). Not sure
+ * about the access flags (SHFL_CF_ACCESS_*).
+ */
+ sf_i->force_restat = 1;
+ sf_r->handle = sf_i->handle;
+ sf_i->handle = SHFL_HANDLE_NIL;
+ sf_i->file = file;
+ file->private_data = sf_r;
+ return 0;
+ }
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ /* We check the value of params.Handle afterwards to find out if
+ * the call succeeded or failed, as the API does not seem to cleanly
+ * distinguish error and informational messages.
+ *
+ * Furthermore, we must set params.Handle to SHFL_HANDLE_NIL to
+ * make the shared folders host service use our fMode parameter */
+
+ if (file->f_flags & O_CREAT) {
+ LogFunc(("O_CREAT set\n"));
+ params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
+ /* We ignore O_EXCL, as the Linux kernel seems to call create
+ beforehand itself, so O_EXCL should always fail. */
+ if (file->f_flags & O_TRUNC) {
+ LogFunc(("O_TRUNC set\n"));
+ 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 (file->f_flags & O_TRUNC) {
+ LogFunc(("O_TRUNC set\n"));
+ params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
+ }
+ }
+
+ switch (file->f_flags & O_ACCMODE) {
+ case O_RDONLY:
+ params.CreateFlags |= SHFL_CF_ACCESS_READ;
+ break;
+
+ case O_WRONLY:
+ params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+ break;
+
+ case O_RDWR:
+ params.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
+ break;
+
+ default:
+ BUG();
+ }
+
+ if (file->f_flags & O_APPEND) {
+ LogFunc(("O_APPEND set\n"));
+ params.CreateFlags |= SHFL_CF_ACCESS_APPEND;
+ }
+
+ params.Info.Attr.fMode = inode->i_mode;
+ LogFunc(("sf_reg_open: calling VbglR0SfCreate, file %s, flags=%#x, %#x\n", sf_i->path->String.utf8, file->f_flags, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate failed flags=%d,%#x rc=%Rrc\n",
+ file->f_flags, params.CreateFlags, rc));
+ kfree(sf_r);
+ return -RTErrConvertToErrno(rc);
+ }
+
+ if (SHFL_HANDLE_NIL == params.Handle) {
+ switch (params.Result) {
+ case SHFL_PATH_NOT_FOUND:
+ case SHFL_FILE_NOT_FOUND:
+ rc_linux = -ENOENT;
+ break;
+ case SHFL_FILE_EXISTS:
+ rc_linux = -EEXIST;
+ break;
+ default:
+ break;
+ }
+ }
+
+ sf_i->force_restat = 1;
+ sf_r->handle = params.Handle;
+ sf_i->file = file;
+ file->private_data = sf_r;
+ return rc_linux;
+}
+
+/**
+ * Close a regular file.
+ *
+ * @param inode the inode
+ * @param file the file
+ * @returns 0 on success, Linux error code otherwise
+ */
+static int sf_reg_release(struct inode *inode, struct file *file)
+{
+ int rc;
+ struct sf_reg_info *sf_r;
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+
+ TRACE();
+ sf_g = GET_GLOB_INFO(inode->i_sb);
+ sf_r = file->private_data;
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_r);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ /* See the smbfs source (file.c). mmap in particular can cause data to be
+ * written to the file after it is closed, which we can't cope with. We
+ * copy and paste the body of filemap_write_and_wait() here as it was not
+ * defined before 2.6.6 and not exported until quite a bit later. */
+ /* filemap_write_and_wait(inode->i_mapping); */
+ if (inode->i_mapping->nrpages
+ && filemap_fdatawrite(inode->i_mapping) != -EIO)
+ filemap_fdatawait(inode->i_mapping);
+#endif
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, sf_r->handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose failed rc=%Rrc\n", rc));
+
+ kfree(sf_r);
+ sf_i->file = NULL;
+ sf_i->handle = SHFL_HANDLE_NIL;
+ file->private_data = NULL;
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+static int sf_reg_fault(struct vm_fault *vmf)
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+static int sf_reg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static struct page *sf_reg_nopage(struct vm_area_struct *vma,
+ unsigned long vaddr, int *type)
+# define SET_TYPE(t) *type = (t)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) */
+static struct page *sf_reg_nopage(struct vm_area_struct *vma,
+ unsigned long vaddr, int unused)
+# define SET_TYPE(t)
+#endif
+{
+ struct page *page;
+ char *buf;
+ loff_t off;
+ uint32_t nread = PAGE_SIZE;
+ int err;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+ struct vm_area_struct *vma = vmf->vma;
+#endif
+ struct file *file = vma->vm_file;
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+
+ TRACE();
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ if (vmf->pgoff > vma->vm_end)
+ return VM_FAULT_SIGBUS;
+#else
+ if (vaddr > vma->vm_end) {
+ SET_TYPE(VM_FAULT_SIGBUS);
+ return NOPAGE_SIGBUS;
+ }
+#endif
+
+ /* Don't use GFP_HIGHUSER as long as sf_reg_read_aux() calls VbglR0SfRead()
+ * which works on virtual addresses. On Linux cannot reliably determine the
+ * physical address for high memory, see rtR0MemObjNativeLockKernel(). */
+ page = alloc_page(GFP_USER);
+ if (!page) {
+ LogRelFunc(("failed to allocate page\n"));
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ return VM_FAULT_OOM;
+#else
+ SET_TYPE(VM_FAULT_OOM);
+ return NOPAGE_OOM;
+#endif
+ }
+
+ buf = kmap(page);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ off = (vmf->pgoff << PAGE_SHIFT);
+#else
+ off = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+#endif
+ err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
+ if (err) {
+ kunmap(page);
+ put_page(page);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ return VM_FAULT_SIGBUS;
+#else
+ SET_TYPE(VM_FAULT_SIGBUS);
+ return NOPAGE_SIGBUS;
+#endif
+ }
+
+ BUG_ON(nread > PAGE_SIZE);
+ if (!nread) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ clear_user_page(page_address(page), vmf->pgoff, page);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ clear_user_page(page_address(page), vaddr, page);
+#else
+ clear_user_page(page_address(page), vaddr);
+#endif
+ } else
+ memset(buf + nread, 0, PAGE_SIZE - nread);
+
+ flush_dcache_page(page);
+ kunmap(page);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ vmf->page = page;
+ return 0;
+#else
+ SET_TYPE(VM_FAULT_MAJOR);
+ return page;
+#endif
+}
+
+static struct vm_operations_struct sf_vma_ops = {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
+ .fault = sf_reg_fault
+#else
+ .nopage = sf_reg_nopage
+#endif
+};
+
+static int sf_reg_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ TRACE();
+ if (vma->vm_flags & VM_SHARED) {
+ LogFunc(("shared mmapping not available\n"));
+ return -EINVAL;
+ }
+
+ vma->vm_ops = &sf_vma_ops;
+ return 0;
+}
+
+struct file_operations sf_reg_fops = {
+ .read = sf_reg_read,
+ .open = sf_reg_open,
+ .write = sf_reg_write,
+ .release = sf_reg_release,
+ .mmap = sf_reg_mmap,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
+/** @todo This code is known to cause caching of data which should not be
+ * cached. Investigate. */
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .splice_read = sf_splice_read,
+# else
+ .sendfile = generic_file_sendfile,
+# endif
+ .aio_read = generic_file_aio_read,
+ .aio_write = generic_file_aio_write,
+# endif
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+ .fsync = noop_fsync,
+# else
+ .fsync = simple_sync_file,
+# endif
+ .llseek = generic_file_llseek,
+#endif
+};
+
+struct inode_operations sf_reg_iops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+ .revalidate = sf_inode_revalidate
+#else
+ .getattr = sf_getattr,
+ .setattr = sf_setattr
+#endif
+};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+
+static int sf_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = GET_F_DENTRY(file)->d_inode;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ uint32_t nread = PAGE_SIZE;
+ char *buf;
+ loff_t off = ((loff_t) page->index) << PAGE_SHIFT;
+ int ret;
+
+ TRACE();
+
+ buf = kmap(page);
+ ret = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
+ if (ret) {
+ kunmap(page);
+ if (PageLocked(page))
+ unlock_page(page);
+ return ret;
+ }
+ BUG_ON(nread > PAGE_SIZE);
+ memset(&buf[nread], 0, PAGE_SIZE - nread);
+ flush_dcache_page(page);
+ kunmap(page);
+ SetPageUptodate(page);
+ unlock_page(page);
+ return 0;
+}
+
+static int sf_writepage(struct page *page, struct writeback_control *wbc)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
+ struct file *file = sf_i->file;
+ struct sf_reg_info *sf_r = file->private_data;
+ char *buf;
+ uint32_t nwritten = PAGE_SIZE;
+ int end_index = inode->i_size >> PAGE_SHIFT;
+ loff_t off = ((loff_t) page->index) << PAGE_SHIFT;
+ int err;
+
+ TRACE();
+
+ if (page->index >= end_index)
+ nwritten = inode->i_size & (PAGE_SIZE - 1);
+
+ buf = kmap(page);
+
+ err = sf_reg_write_aux(__func__, sf_g, sf_r, buf, &nwritten, off);
+ if (err < 0) {
+ ClearPageUptodate(page);
+ goto out;
+ }
+
+ if (off > inode->i_size)
+ inode->i_size = off;
+
+ if (PageError(page))
+ ClearPageError(page);
+ err = 0;
+
+ out:
+ kunmap(page);
+
+ unlock_page(page);
+ return err;
+}
+
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+
+int sf_write_begin(struct file *file, struct address_space *mapping, loff_t pos,
+ unsigned len, unsigned flags, struct page **pagep,
+ void **fsdata)
+{
+ TRACE();
+
+ return simple_write_begin(file, mapping, pos, len, flags, pagep,
+ fsdata);
+}
+
+int sf_write_end(struct file *file, struct address_space *mapping, loff_t pos,
+ unsigned len, unsigned copied, struct page *page, void *fsdata)
+{
+ struct inode *inode = mapping->host;
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
+ struct sf_reg_info *sf_r = file->private_data;
+ void *buf;
+ unsigned from = pos & (PAGE_SIZE - 1);
+ uint32_t nwritten = len;
+ int err;
+
+ TRACE();
+
+ buf = kmap(page);
+ err =
+ sf_reg_write_aux(__func__, sf_g, sf_r, buf + from, &nwritten, pos);
+ kunmap(page);
+
+ if (err >= 0) {
+ if (!PageUptodate(page) && nwritten == PAGE_SIZE)
+ SetPageUptodate(page);
+
+ pos += nwritten;
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ }
+
+ unlock_page(page);
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
+ put_page(page);
+# else
+ page_cache_release(page);
+# endif
+
+ return nwritten;
+}
+
+# endif /* KERNEL_VERSION >= 2.6.24 */
+
+struct address_space_operations sf_reg_aops = {
+ .readpage = sf_readpage,
+ .writepage = sf_writepage,
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ .write_begin = sf_write_begin,
+ .write_end = sf_write_end,
+# else
+ .prepare_write = simple_prepare_write,
+ .commit_write = simple_commit_write,
+# endif
+};
+
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
+
diff --git a/src/VBox/Additions/linux/sharedfolders/utils.c b/src/VBox/Additions/linux/sharedfolders/utils.c
new file mode 100644
index 00000000..d14bbb31
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/utils.c
@@ -0,0 +1,883 @@
+/* $Id: utils.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, utility functions.
+ *
+ * Utility functions (mainly conversion from/to VirtualBox/Linux data structures).
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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 "vfsmod.h"
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <linux/nfs_fs.h>
+#include <linux/vfs.h>
+
+/* #define USE_VMALLOC */
+
+/*
+ * sf_reg_aops and sf_backing_dev_info are just quick implementations to make
+ * sendfile work. For more information have a look at
+ *
+ * http://us1.samba.org/samba/ftp/cifs-cvs/ols2006-fs-tutorial-smf.odp
+ *
+ * and the sample implementation
+ *
+ * http://pserver.samba.org/samba/ftp/cifs-cvs/samplefs.tar.gz
+ */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+static void sf_ftime_from_timespec(time_t * time, RTTIMESPEC * ts)
+{
+ int64_t t = RTTimeSpecGetNano(ts);
+
+ do_div(t, 1000000000);
+ *time = t;
+}
+
+static void sf_timespec_from_ftime(RTTIMESPEC * ts, time_t * time)
+{
+ int64_t t = 1000000000 * *time;
+ RTTimeSpecSetNano(ts, t);
+}
+#else /* >= 2.6.0 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+static void sf_ftime_from_timespec(struct timespec *tv, RTTIMESPEC *ts)
+#else
+static void sf_ftime_from_timespec(struct timespec64 *tv, RTTIMESPEC *ts)
+#endif
+{
+ int64_t t = RTTimeSpecGetNano(ts);
+ int64_t nsec;
+
+ nsec = do_div(t, 1000000000);
+ tv->tv_sec = t;
+ tv->tv_nsec = nsec;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+static void sf_timespec_from_ftime(RTTIMESPEC *ts, struct timespec *tv)
+#else
+static void sf_timespec_from_ftime(RTTIMESPEC *ts, struct timespec64 *tv)
+#endif
+{
+ int64_t t = (int64_t) tv->tv_nsec + (int64_t) tv->tv_sec * 1000000000;
+ RTTimeSpecSetNano(ts, t);
+}
+#endif /* >= 2.6.0 */
+
+/* set [inode] attributes based on [info], uid/gid based on [sf_g] */
+void sf_init_inode(struct sf_glob_info *sf_g, struct inode *inode,
+ PSHFLFSOBJINFO info)
+{
+ PSHFLFSOBJATTR attr;
+ int mode;
+
+ TRACE();
+
+ attr = &info->Attr;
+
+#define mode_set(r) attr->fMode & (RTFS_UNIX_##r) ? (S_##r) : 0;
+ mode = mode_set(IRUSR);
+ mode |= mode_set(IWUSR);
+ mode |= mode_set(IXUSR);
+
+ mode |= mode_set(IRGRP);
+ mode |= mode_set(IWGRP);
+ mode |= mode_set(IXGRP);
+
+ mode |= mode_set(IROTH);
+ mode |= mode_set(IWOTH);
+ mode |= mode_set(IXOTH);
+
+#undef mode_set
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ inode->i_mapping->a_ops = &sf_reg_aops;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
+ /* XXX Was this ever necessary? */
+ inode->i_mapping->backing_dev_info = &sf_g->bdi;
+#endif
+#endif
+
+ if (RTFS_IS_DIRECTORY(attr->fMode)) {
+ inode->i_mode = sf_g->dmode != ~0 ? (sf_g->dmode & 0777) : mode;
+ inode->i_mode &= ~sf_g->dmask;
+ inode->i_mode |= S_IFDIR;
+ inode->i_op = &sf_dir_iops;
+ inode->i_fop = &sf_dir_fops;
+ /* XXX: this probably should be set to the number of entries
+ in the directory plus two (. ..) */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ set_nlink(inode, 1);
+#else
+ inode->i_nlink = 1;
+#endif
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ else if (RTFS_IS_SYMLINK(attr->fMode)) {
+ inode->i_mode = sf_g->fmode != ~0 ? (sf_g->fmode & 0777) : mode;
+ inode->i_mode &= ~sf_g->fmask;
+ inode->i_mode |= S_IFLNK;
+ inode->i_op = &sf_lnk_iops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ set_nlink(inode, 1);
+#else
+ inode->i_nlink = 1;
+#endif
+ }
+#endif
+ else {
+ inode->i_mode = sf_g->fmode != ~0 ? (sf_g->fmode & 0777) : mode;
+ inode->i_mode &= ~sf_g->fmask;
+ inode->i_mode |= S_IFREG;
+ inode->i_op = &sf_reg_iops;
+ inode->i_fop = &sf_reg_fops;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+ set_nlink(inode, 1);
+#else
+ inode->i_nlink = 1;
+#endif
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ inode->i_uid = make_kuid(current_user_ns(), sf_g->uid);
+ inode->i_gid = make_kgid(current_user_ns(), sf_g->gid);
+#else
+ inode->i_uid = sf_g->uid;
+ inode->i_gid = sf_g->gid;
+#endif
+
+ inode->i_size = info->cbObject;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) && !defined(KERNEL_FC6)
+ inode->i_blksize = 4096;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 11)
+ inode->i_blkbits = 12;
+#endif
+ /* i_blocks always in units of 512 bytes! */
+ inode->i_blocks = (info->cbAllocated + 511) / 512;
+
+ sf_ftime_from_timespec(&inode->i_atime, &info->AccessTime);
+ sf_ftime_from_timespec(&inode->i_ctime, &info->ChangeTime);
+ sf_ftime_from_timespec(&inode->i_mtime, &info->ModificationTime);
+}
+
+int sf_stat(const char *caller, struct sf_glob_info *sf_g,
+ SHFLSTRING * path, PSHFLFSOBJINFO result, int ok_to_fail)
+{
+ int rc;
+ SHFLCREATEPARMS params;
+ NOREF(caller);
+
+ TRACE();
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
+ LogFunc(("sf_stat: calling VbglR0SfCreate, file %s, flags %#x\n",
+ path->String.utf8, params.CreateFlags));
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, path, &params);
+ if (rc == VERR_INVALID_NAME) {
+ /* this can happen for names like 'foo*' on a Windows host */
+ return -ENOENT;
+ }
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate(%s) failed. caller=%s, rc=%Rrc\n",
+ path->String.utf8, rc, caller));
+ return -EPROTO;
+ }
+ if (params.Result != SHFL_FILE_EXISTS) {
+ if (!ok_to_fail)
+ LogFunc(("VbglR0SfCreate(%s) file does not exist. caller=%s, result=%d\n", path->String.utf8, params.Result, caller));
+ return -ENOENT;
+ }
+
+ *result = params.Info;
+ return 0;
+}
+
+/* this is called directly as iop on 2.4, indirectly as dop
+ [sf_dentry_revalidate] on 2.4/2.6, indirectly as iop through
+ [sf_getattr] on 2.6. the job is to find out whether dentry/inode is
+ still valid. the test is failed if [dentry] does not have an inode
+ or [sf_stat] is unsuccessful, otherwise we return success and
+ update inode attributes */
+int sf_inode_revalidate(struct dentry *dentry)
+{
+ int err;
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i;
+ SHFLFSOBJINFO info;
+
+ TRACE();
+ if (!dentry || !dentry->d_inode) {
+ LogFunc(("no dentry(%p) or inode(%p)\n", dentry,
+ dentry->d_inode));
+ return -EINVAL;
+ }
+
+ sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb);
+ sf_i = GET_INODE_INFO(dentry->d_inode);
+
+#if 0
+ printk("%s called by %p:%p\n",
+ sf_i->path->String.utf8,
+ __builtin_return_address(0), __builtin_return_address(1));
+#endif
+
+ BUG_ON(!sf_g);
+ BUG_ON(!sf_i);
+
+ if (!sf_i->force_restat) {
+ if (jiffies - dentry->d_time < sf_g->ttl)
+ return 0;
+ }
+
+ err = sf_stat(__func__, sf_g, sf_i->path, &info, 1);
+ if (err)
+ return err;
+
+ dentry->d_time = jiffies;
+ sf_init_inode(sf_g, dentry->d_inode, &info);
+ return 0;
+}
+
+/* this is called during name resolution/lookup to check if the
+ [dentry] in the cache is still valid. the job is handled by
+ [sf_inode_revalidate] */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+sf_dentry_revalidate(struct dentry *dentry, unsigned flags)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+sf_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
+#else
+sf_dentry_revalidate(struct dentry *dentry, int flags)
+#endif
+{
+ TRACE();
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
+ /* see Documentation/filesystems/vfs.txt */
+ if (nd && nd->flags & LOOKUP_RCU)
+ return -ECHILD;
+#endif
+
+ if (sf_inode_revalidate(dentry))
+ return 0;
+
+ return 1;
+}
+
+/* on 2.6 this is a proxy for [sf_inode_revalidate] which (as a side
+ effect) updates inode attributes for [dentry] (given that [dentry]
+ has inode at all) from these new attributes we derive [kstat] via
+ [generic_fillattr] */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+int sf_getattr(const struct path *path, struct kstat *kstat, u32 request_mask,
+ unsigned int flags)
+#else
+int sf_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *kstat)
+#endif
+{
+ int err;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+ struct dentry *dentry = path->dentry;
+#endif
+
+ TRACE();
+ err = sf_inode_revalidate(dentry);
+ if (err)
+ return err;
+
+ generic_fillattr(dentry->d_inode, kstat);
+ return 0;
+}
+
+int sf_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i;
+ SHFLCREATEPARMS params;
+ SHFLFSOBJINFO info;
+ uint32_t cbBuffer;
+ int rc, err;
+
+ TRACE();
+
+ sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb);
+ sf_i = GET_INODE_INFO(dentry->d_inode);
+ err = 0;
+
+ RT_ZERO(params);
+ params.Handle = SHFL_HANDLE_NIL;
+ params.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
+ | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_ATTR_WRITE;
+
+ /* this is at least required for Posix hosts */
+ if (iattr->ia_valid & ATTR_SIZE)
+ params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
+
+ rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfCreate(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail2;
+ }
+ if (params.Result != SHFL_FILE_EXISTS) {
+ LogFunc(("file %s does not exist\n", sf_i->path->String.utf8));
+ err = -ENOENT;
+ goto fail1;
+ }
+
+ /* Setting the file size and setting the other attributes has to be
+ * handled separately, see implementation of vbsfSetFSInfo() in
+ * vbsf.cpp */
+ if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) {
+#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? RTFS_UNIX_##r : 0)
+
+ RT_ZERO(info);
+ if (iattr->ia_valid & ATTR_MODE) {
+ info.Attr.fMode = mode_set(IRUSR);
+ info.Attr.fMode |= mode_set(IWUSR);
+ info.Attr.fMode |= mode_set(IXUSR);
+ info.Attr.fMode |= mode_set(IRGRP);
+ info.Attr.fMode |= mode_set(IWGRP);
+ info.Attr.fMode |= mode_set(IXGRP);
+ info.Attr.fMode |= mode_set(IROTH);
+ info.Attr.fMode |= mode_set(IWOTH);
+ info.Attr.fMode |= mode_set(IXOTH);
+
+ if (iattr->ia_mode & S_IFDIR)
+ info.Attr.fMode |= RTFS_TYPE_DIRECTORY;
+ else
+ info.Attr.fMode |= RTFS_TYPE_FILE;
+ }
+
+ if (iattr->ia_valid & ATTR_ATIME)
+ sf_timespec_from_ftime(&info.AccessTime,
+ &iattr->ia_atime);
+ if (iattr->ia_valid & ATTR_MTIME)
+ sf_timespec_from_ftime(&info.ModificationTime,
+ &iattr->ia_mtime);
+ /* ignore ctime (inode change time) as it can't be set from userland anyway */
+
+ cbBuffer = sizeof(info);
+ rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, params.Handle,
+ SHFL_INFO_SET | SHFL_INFO_FILE, &cbBuffer,
+ (PSHFLDIRINFO) & info);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfFsInfo(%s, FILE) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail1;
+ }
+ }
+
+ if (iattr->ia_valid & ATTR_SIZE) {
+ RT_ZERO(info);
+ info.cbObject = iattr->ia_size;
+ cbBuffer = sizeof(info);
+ rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, params.Handle,
+ SHFL_INFO_SET | SHFL_INFO_SIZE, &cbBuffer,
+ (PSHFLDIRINFO) & info);
+ if (RT_FAILURE(rc)) {
+ LogFunc(("VbglR0SfFsInfo(%s, SIZE) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+ err = -RTErrConvertToErrno(rc);
+ goto fail1;
+ }
+ }
+
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+
+ return sf_inode_revalidate(dentry);
+
+ fail1:
+ rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n",
+ sf_i->path->String.utf8, rc));
+
+ fail2:
+ return err;
+}
+#endif /* >= 2.6.0 */
+
+static int sf_make_path(const char *caller, struct sf_inode_info *sf_i,
+ const char *d_name, size_t d_len, SHFLSTRING ** result)
+{
+ size_t path_len, shflstring_len;
+ SHFLSTRING *tmp;
+ uint16_t p_len;
+ uint8_t *p_name;
+ int fRoot = 0;
+
+ TRACE();
+ p_len = sf_i->path->u16Length;
+ p_name = sf_i->path->String.utf8;
+
+ if (p_len == 1 && *p_name == '/') {
+ path_len = d_len + 1;
+ fRoot = 1;
+ } else {
+ /* lengths of constituents plus terminating zero plus slash */
+ path_len = p_len + d_len + 2;
+ if (path_len > 0xffff) {
+ LogFunc(("path too long. caller=%s, path_len=%zu\n",
+ caller, path_len));
+ return -ENAMETOOLONG;
+ }
+ }
+
+ shflstring_len = offsetof(SHFLSTRING, String.utf8) + path_len;
+ tmp = kmalloc(shflstring_len, GFP_KERNEL);
+ if (!tmp) {
+ LogRelFunc(("kmalloc failed, caller=%s\n", caller));
+ return -ENOMEM;
+ }
+ tmp->u16Length = path_len - 1;
+ tmp->u16Size = path_len;
+
+ if (fRoot)
+ memcpy(&tmp->String.utf8[0], d_name, d_len + 1);
+ else {
+ memcpy(&tmp->String.utf8[0], p_name, p_len);
+ tmp->String.utf8[p_len] = '/';
+ memcpy(&tmp->String.utf8[p_len + 1], d_name, d_len);
+ tmp->String.utf8[p_len + 1 + d_len] = '\0';
+ }
+
+ *result = tmp;
+ return 0;
+}
+
+/**
+ * [dentry] contains string encoded in coding system that corresponds
+ * to [sf_g]->nls, we must convert it to UTF8 here and pass down to
+ * [sf_make_path] which will allocate SHFLSTRING and fill it in
+ */
+int sf_path_from_dentry(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_inode_info *sf_i, struct dentry *dentry,
+ SHFLSTRING ** result)
+{
+ int err;
+ const char *d_name;
+ size_t d_len;
+ const char *name;
+ size_t len = 0;
+
+ TRACE();
+ d_name = dentry->d_name.name;
+ d_len = dentry->d_name.len;
+
+ if (sf_g->nls) {
+ size_t in_len, i, out_bound_len;
+ const char *in;
+ char *out;
+
+ in = d_name;
+ in_len = d_len;
+
+ out_bound_len = PATH_MAX;
+ out = kmalloc(out_bound_len, GFP_KERNEL);
+ name = out;
+
+ for (i = 0; i < d_len; ++i) {
+ /* We renamed the linux kernel wchar_t type to linux_wchar_t in
+ the-linux-kernel.h, as it conflicts with the C++ type of that name. */
+ linux_wchar_t uni;
+ int nb;
+
+ nb = sf_g->nls->char2uni(in, in_len, &uni);
+ if (nb < 0) {
+ LogFunc(("nls->char2uni failed %x %d\n",
+ *in, in_len));
+ err = -EINVAL;
+ goto fail1;
+ }
+ in_len -= nb;
+ in += nb;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+ nb = utf32_to_utf8(uni, out, out_bound_len);
+#else
+ nb = utf8_wctomb(out, uni, out_bound_len);
+#endif
+ if (nb < 0) {
+ LogFunc(("nls->uni2char failed %x %d\n",
+ uni, out_bound_len));
+ err = -EINVAL;
+ goto fail1;
+ }
+ out_bound_len -= nb;
+ out += nb;
+ len += nb;
+ }
+ if (len >= PATH_MAX - 1) {
+ err = -ENAMETOOLONG;
+ goto fail1;
+ }
+
+ LogFunc(("result(%d) = %.*s\n", len, len, name));
+ *out = 0;
+ } else {
+ name = d_name;
+ len = d_len;
+ }
+
+ err = sf_make_path(caller, sf_i, name, len, result);
+ if (name != d_name)
+ kfree(name);
+
+ return err;
+
+ fail1:
+ kfree(name);
+ return err;
+}
+
+int sf_nlscpy(struct sf_glob_info *sf_g,
+ char *name, size_t name_bound_len,
+ const unsigned char *utf8_name, size_t utf8_len)
+{
+ if (sf_g->nls) {
+ const char *in;
+ char *out;
+ size_t out_len;
+ size_t out_bound_len;
+ size_t in_bound_len;
+
+ in = utf8_name;
+ in_bound_len = utf8_len;
+
+ out = name;
+ out_len = 0;
+ out_bound_len = name_bound_len;
+
+ while (in_bound_len) {
+ int nb;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+ unicode_t uni;
+
+ nb = utf8_to_utf32(in, in_bound_len, &uni);
+#else
+ linux_wchar_t uni;
+
+ nb = utf8_mbtowc(&uni, in, in_bound_len);
+#endif
+ if (nb < 0) {
+ LogFunc(("utf8_mbtowc failed(%s) %x:%d\n",
+ (const char *)utf8_name, *in,
+ in_bound_len));
+ return -EINVAL;
+ }
+ in += nb;
+ in_bound_len -= nb;
+
+ nb = sf_g->nls->uni2char(uni, out, out_bound_len);
+ if (nb < 0) {
+ LogFunc(("nls->uni2char failed(%s) %x:%d\n",
+ utf8_name, uni, out_bound_len));
+ return nb;
+ }
+ out += nb;
+ out_bound_len -= nb;
+ out_len += nb;
+ }
+
+ *out = 0;
+ } else {
+ if (utf8_len + 1 > name_bound_len)
+ return -ENAMETOOLONG;
+
+ memcpy(name, utf8_name, utf8_len + 1);
+ }
+ return 0;
+}
+
+static struct sf_dir_buf *sf_dir_buf_alloc(void)
+{
+ struct sf_dir_buf *b;
+
+ TRACE();
+ b = kmalloc(sizeof(*b), GFP_KERNEL);
+ if (!b) {
+ LogRelFunc(("could not alloc directory buffer\n"));
+ return NULL;
+ }
+#ifdef USE_VMALLOC
+ b->buf = vmalloc(DIR_BUFFER_SIZE);
+#else
+ b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL);
+#endif
+ if (!b->buf) {
+ kfree(b);
+ LogRelFunc(("could not alloc directory buffer storage\n"));
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&b->head);
+ b->cEntries = 0;
+ b->cbUsed = 0;
+ b->cbFree = DIR_BUFFER_SIZE;
+ return b;
+}
+
+static void sf_dir_buf_free(struct sf_dir_buf *b)
+{
+ BUG_ON(!b || !b->buf);
+
+ TRACE();
+ list_del(&b->head);
+#ifdef USE_VMALLOC
+ vfree(b->buf);
+#else
+ kfree(b->buf);
+#endif
+ kfree(b);
+}
+
+/**
+ * Free the directory buffer.
+ */
+void sf_dir_info_free(struct sf_dir_info *p)
+{
+ struct list_head *list, *pos, *tmp;
+
+ TRACE();
+ list = &p->info_list;
+ list_for_each_safe(pos, tmp, list) {
+ struct sf_dir_buf *b;
+
+ b = list_entry(pos, struct sf_dir_buf, head);
+ sf_dir_buf_free(b);
+ }
+ kfree(p);
+}
+
+/**
+ * Empty (but not free) the directory buffer.
+ */
+void sf_dir_info_empty(struct sf_dir_info *p)
+{
+ struct list_head *list, *pos, *tmp;
+ TRACE();
+ list = &p->info_list;
+ list_for_each_safe(pos, tmp, list) {
+ struct sf_dir_buf *b;
+ b = list_entry(pos, struct sf_dir_buf, head);
+ b->cEntries = 0;
+ b->cbUsed = 0;
+ b->cbFree = DIR_BUFFER_SIZE;
+ }
+}
+
+/**
+ * Create a new directory buffer descriptor.
+ */
+struct sf_dir_info *sf_dir_info_alloc(void)
+{
+ struct sf_dir_info *p;
+
+ TRACE();
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ LogRelFunc(("could not alloc directory info\n"));
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&p->info_list);
+ return p;
+}
+
+/**
+ * Search for an empty directory content buffer.
+ */
+static struct sf_dir_buf *sf_get_empty_dir_buf(struct sf_dir_info *sf_d)
+{
+ struct list_head *list, *pos;
+
+ list = &sf_d->info_list;
+ list_for_each(pos, list) {
+ struct sf_dir_buf *b;
+
+ b = list_entry(pos, struct sf_dir_buf, head);
+ if (!b)
+ return NULL;
+ else {
+ if (b->cbUsed == 0)
+ return b;
+ }
+ }
+
+ return NULL;
+}
+
+int sf_dir_read_all(struct sf_glob_info *sf_g, struct sf_inode_info *sf_i,
+ struct sf_dir_info *sf_d, SHFLHANDLE handle)
+{
+ int err;
+ SHFLSTRING *mask;
+ struct sf_dir_buf *b;
+
+ TRACE();
+ err = sf_make_path(__func__, sf_i, "*", 1, &mask);
+ if (err)
+ goto fail0;
+
+ for (;;) {
+ int rc;
+ void *buf;
+ uint32_t cbSize;
+ uint32_t cEntries;
+
+ b = sf_get_empty_dir_buf(sf_d);
+ if (!b) {
+ b = sf_dir_buf_alloc();
+ if (!b) {
+ err = -ENOMEM;
+ LogRelFunc(("could not alloc directory buffer\n"));
+ goto fail1;
+ }
+ list_add(&b->head, &sf_d->info_list);
+ }
+
+ buf = b->buf;
+ cbSize = b->cbFree;
+
+ rc = VbglR0SfDirInfo(&client_handle, &sf_g->map, handle, mask,
+ 0, 0, &cbSize, buf, &cEntries);
+ switch (rc) {
+ case VINF_SUCCESS:
+ RT_FALL_THRU();
+ case VERR_NO_MORE_FILES:
+ break;
+ case VERR_NO_TRANSLATION:
+ LogFunc(("host could not translate entry\n"));
+ /* XXX */
+ break;
+ default:
+ err = -RTErrConvertToErrno(rc);
+ LogFunc(("VbglR0SfDirInfo failed rc=%Rrc\n", rc));
+ goto fail1;
+ }
+
+ b->cEntries += cEntries;
+ b->cbFree -= cbSize;
+ b->cbUsed += cbSize;
+
+ if (RT_FAILURE(rc))
+ break;
+ }
+ err = 0;
+
+ fail1:
+ kfree(mask);
+
+ fail0:
+ return err;
+}
+
+int sf_get_volume_info(struct super_block *sb, STRUCT_STATFS * stat)
+{
+ struct sf_glob_info *sf_g;
+ SHFLVOLINFO SHFLVolumeInfo;
+ uint32_t cbBuffer;
+ int rc;
+
+ sf_g = GET_GLOB_INFO(sb);
+ cbBuffer = sizeof(SHFLVolumeInfo);
+ rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, 0,
+ SHFL_INFO_GET | SHFL_INFO_VOLUME, &cbBuffer,
+ (PSHFLDIRINFO) & SHFLVolumeInfo);
+ if (RT_FAILURE(rc))
+ return -RTErrConvertToErrno(rc);
+
+ stat->f_type = NFS_SUPER_MAGIC; /* XXX vboxsf type? */
+ stat->f_bsize = SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_blocks = SHFLVolumeInfo.ullTotalAllocationBytes
+ / SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_bfree = SHFLVolumeInfo.ullAvailableAllocationBytes
+ / SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_bavail = SHFLVolumeInfo.ullAvailableAllocationBytes
+ / SHFLVolumeInfo.ulBytesPerAllocationUnit;
+ stat->f_files = 1000;
+ stat->f_ffree = 1000; /* don't return 0 here since the guest may think
+ * that it is not possible to create any more files */
+ stat->f_fsid.val[0] = 0;
+ stat->f_fsid.val[1] = 0;
+ stat->f_namelen = 255;
+ return 0;
+}
+
+struct dentry_operations sf_dentry_ops = {
+ .d_revalidate = sf_dentry_revalidate
+};
+
+int sf_init_backing_dev(struct sf_glob_info *sf_g)
+{
+ int rc = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
+ /* Each new shared folder map gets a new uint64_t identifier,
+ * allocated in sequence. We ASSUME the sequence will not wrap. */
+ static uint64_t s_u64Sequence = 0;
+ uint64_t u64CurrentSequence = ASMAtomicIncU64(&s_u64Sequence);
+
+ sf_g->bdi.ra_pages = 0; /* No readahead */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 12)
+ sf_g->bdi.capabilities = BDI_CAP_MAP_DIRECT /* MAP_SHARED */
+ | BDI_CAP_MAP_COPY /* MAP_PRIVATE */
+ | BDI_CAP_READ_MAP /* can be mapped for reading */
+ | BDI_CAP_WRITE_MAP /* can be mapped for writing */
+ | BDI_CAP_EXEC_MAP; /* can be mapped for execution */
+#endif /* >= 2.6.12 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ rc = bdi_init(&sf_g->bdi);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ if (!rc)
+ rc = bdi_register(&sf_g->bdi, NULL, "vboxsf-%llu",
+ (unsigned long long)u64CurrentSequence);
+#endif /* >= 2.6.26 */
+#endif /* >= 2.6.24 */
+#endif /* >= 2.6.0 && <= 3.19.0 */
+ return rc;
+}
+
+void sf_done_backing_dev(struct sf_glob_info *sf_g)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
+ bdi_destroy(&sf_g->bdi); /* includes bdi_unregister() */
+#endif
+}
diff --git a/src/VBox/Additions/linux/sharedfolders/vbsfmount.c b/src/VBox/Additions/linux/sharedfolders/vbsfmount.c
new file mode 100644
index 00000000..5d68e931
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vbsfmount.c
@@ -0,0 +1,95 @@
+/* $Id: vbsfmount.c $ */
+/** @file
+ * vbsfmount - Commonly used code to mount shared folders on Linux-based
+ * systems. Currently used by mount.vboxsf and VBoxService.
+ */
+
+/*
+ * Copyright (C) 2010-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include "vbsfmount.h"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <ctype.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+
+
+/** @todo Use defines for return values! */
+int vbsfmount_complete(const char *host_name, const char *mount_point,
+ unsigned long flags, struct vbsf_mount_opts *opts)
+{
+ FILE *f, *m;
+ char *buf;
+ size_t size;
+ struct mntent e;
+ int rc = 0;
+
+ m = open_memstream(&buf, &size);
+ if (!m)
+ return 1; /* Could not update mount table (failed to create memstream). */
+
+ if (opts->uid)
+ fprintf(m, "uid=%d,", opts->uid);
+ if (opts->gid)
+ fprintf(m, "gid=%d,", opts->gid);
+ if (opts->ttl)
+ fprintf(m, "ttl=%d,", opts->ttl);
+ if (*opts->nls_name)
+ fprintf(m, "iocharset=%s,", opts->nls_name);
+ if (flags & MS_NOSUID)
+ fprintf(m, "%s,", MNTOPT_NOSUID);
+ if (flags & MS_RDONLY)
+ fprintf(m, "%s,", MNTOPT_RO);
+ else
+ fprintf(m, "%s,", MNTOPT_RW);
+
+ fclose(m);
+
+ if (size > 0)
+ buf[size - 1] = 0;
+ else
+ buf = "defaults";
+
+ f = setmntent(MOUNTED, "a+");
+ if (!f)
+ {
+ rc = 2; /* Could not open mount table for update. */
+ }
+ else
+ {
+ e.mnt_fsname = (char*)host_name;
+ e.mnt_dir = (char*)mount_point;
+ e.mnt_type = "vboxsf";
+ e.mnt_opts = buf;
+ e.mnt_freq = 0;
+ e.mnt_passno = 0;
+
+ if (addmntent(f, &e))
+ rc = 3; /* Could not add an entry to the mount table. */
+
+ endmntent(f);
+ }
+
+ if (size > 0)
+ {
+ memset(buf, 0, size);
+ free(buf);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Additions/linux/sharedfolders/vbsfmount.h b/src/VBox/Additions/linux/sharedfolders/vbsfmount.h
new file mode 100644
index 00000000..c14e5284
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vbsfmount.h
@@ -0,0 +1,90 @@
+/* $Id: vbsfmount.h $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, mount(2) parameter structure.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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_linux_sharedfolders_vbsfmount_h
+#define GA_INCLUDED_SRC_linux_sharedfolders_vbsfmount_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Linux constraints the size of data mount argument to PAGE_SIZE - 1. */
+#define MAX_HOST_NAME 256
+#define MAX_NLS_NAME 32
+
+#define VBSF_MOUNT_SIGNATURE_BYTE_0 '\377'
+#define VBSF_MOUNT_SIGNATURE_BYTE_1 '\376'
+#define VBSF_MOUNT_SIGNATURE_BYTE_2 '\375'
+
+struct vbsf_mount_info_new {
+ /*
+ * The old version of the mount_info struct started with a
+ * char name[MAX_HOST_NAME] field, where name cannot be '\0'.
+ * So the new version of the mount_info struct starts with a
+ * nullchar field which is always 0 so that we can detect and
+ * reject the old structure being passed.
+ */
+ char nullchar;
+ char signature[3]; /* signature */
+ int length; /* length of the whole structure */
+ char name[MAX_HOST_NAME]; /* share name */
+ char nls_name[MAX_NLS_NAME]; /* name of an I/O charset */
+ int uid; /* user ID for all entries, default 0=root */
+ int gid; /* group ID for all entries, default 0=root */
+ int ttl; /* time to live */
+ int dmode; /* mode for directories if != 0xffffffff */
+ int fmode; /* mode for regular files if != 0xffffffff */
+ int dmask; /* umask applied to directories */
+ int fmask; /* umask applied to regular files */
+ char tag[32]; /**< Mount tag for VBoxService automounter. @since 6.0 */
+};
+
+struct vbsf_mount_opts {
+ int uid;
+ int gid;
+ int ttl;
+ int dmode;
+ int fmode;
+ int dmask;
+ int fmask;
+ int ronly;
+ int sloppy;
+ int noexec;
+ int nodev;
+ int nosuid;
+ int remount;
+ char nls_name[MAX_NLS_NAME];
+ char *convertcp;
+};
+
+/** Completes the mount operation by adding the new mount point to mtab if required. */
+int vbsfmount_complete(const char *host_name, const char *mount_point,
+ unsigned long flags, struct vbsf_mount_opts *opts);
+
+#endif /* !GA_INCLUDED_SRC_linux_sharedfolders_vbsfmount_h */
diff --git a/src/VBox/Additions/linux/sharedfolders/vfsmod.c b/src/VBox/Additions/linux/sharedfolders/vfsmod.c
new file mode 100644
index 00000000..209424a6
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vfsmod.c
@@ -0,0 +1,683 @@
+/* $Id: vfsmod.c $ */
+/** @file
+ * vboxsf - VBox Linux Shared Folders VFS, module init/term, super block management.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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.
+ */
+
+/**
+ * @note Anyone wishing to make changes here might wish to take a look at
+ * https://github.com/torvalds/linux/blob/master/Documentation/filesystems/vfs.txt
+ * which seems to be the closest there is to official documentation on
+ * writing filesystem drivers for Linux.
+ */
+
+#include "vfsmod.h"
+#include "version-generated.h"
+#include "revision-generated.h"
+#include "product-generated.h"
+#include "VBoxGuestR0LibInternal.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+# include <linux/mount.h>
+#endif
+#include <linux/seq_file.h>
+
+MODULE_DESCRIPTION(VBOX_PRODUCT " VFS Module for Host File System Access");
+MODULE_AUTHOR(VBOX_VENDOR);
+MODULE_LICENSE("GPL and additional rights");
+#ifdef MODULE_ALIAS_FS
+MODULE_ALIAS_FS("vboxsf");
+#endif
+#ifdef MODULE_VERSION
+MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
+#endif
+
+/* globals */
+VBGLSFCLIENT client_handle;
+
+/* forward declarations */
+static struct super_operations sf_super_ops;
+
+/* allocate global info, try to map host share */
+static int sf_glob_alloc(struct vbsf_mount_info_new *info,
+ struct sf_glob_info **sf_gp)
+{
+ int err, rc;
+ SHFLSTRING *str_name;
+ size_t name_len, str_len;
+ struct sf_glob_info *sf_g;
+
+ TRACE();
+ sf_g = kmalloc(sizeof(*sf_g), GFP_KERNEL);
+ if (!sf_g) {
+ err = -ENOMEM;
+ LogRelFunc(("could not allocate memory for global info\n"));
+ goto fail0;
+ }
+
+ RT_ZERO(*sf_g);
+
+ if (info->nullchar != '\0'
+ || info->signature[0] != VBSF_MOUNT_SIGNATURE_BYTE_0
+ || info->signature[1] != VBSF_MOUNT_SIGNATURE_BYTE_1
+ || info->signature[2] != VBSF_MOUNT_SIGNATURE_BYTE_2) {
+ err = -EINVAL;
+ goto fail1;
+ }
+
+ info->name[sizeof(info->name) - 1] = 0;
+ info->nls_name[sizeof(info->nls_name) - 1] = 0;
+
+ name_len = strlen(info->name);
+ str_len = offsetof(SHFLSTRING, String.utf8) + name_len + 1;
+ str_name = kmalloc(str_len, GFP_KERNEL);
+ if (!str_name) {
+ err = -ENOMEM;
+ LogRelFunc(("could not allocate memory for host name\n"));
+ goto fail1;
+ }
+
+ str_name->u16Length = name_len;
+ str_name->u16Size = name_len + 1;
+ memcpy(str_name->String.utf8, info->name, name_len + 1);
+
+#define _IS_UTF8(_str) \
+ (strcmp(_str, "utf8") == 0)
+#define _IS_EMPTY(_str) \
+ (strcmp(_str, "") == 0)
+
+ /* Check if NLS charset is valid and not points to UTF8 table */
+ if (info->nls_name[0]) {
+ if (_IS_UTF8(info->nls_name))
+ sf_g->nls = NULL;
+ else {
+ sf_g->nls = load_nls(info->nls_name);
+ if (!sf_g->nls) {
+ err = -EINVAL;
+ LogFunc(("failed to load nls %s\n",
+ info->nls_name));
+ kfree(str_name);
+ goto fail1;
+ }
+ }
+ } else {
+#ifdef CONFIG_NLS_DEFAULT
+ /* If no NLS charset specified, try to load the default
+ * one if it's not points to UTF8. */
+ if (!_IS_UTF8(CONFIG_NLS_DEFAULT)
+ && !_IS_EMPTY(CONFIG_NLS_DEFAULT))
+ sf_g->nls = load_nls_default();
+ else
+ sf_g->nls = NULL;
+#else
+ sf_g->nls = NULL;
+#endif
+
+#undef _IS_UTF8
+#undef _IS_EMPTY
+ }
+
+ rc = VbglR0SfMapFolder(&client_handle, str_name, &sf_g->map);
+ kfree(str_name);
+
+ if (RT_FAILURE(rc)) {
+ err = -EPROTO;
+ LogFunc(("VbglR0SfMapFolder failed rc=%d\n", rc));
+ goto fail2;
+ }
+
+ sf_g->ttl = info->ttl;
+ sf_g->uid = info->uid;
+ sf_g->gid = info->gid;
+
+ if ((unsigned)info->length >= RT_UOFFSETOF(struct vbsf_mount_info_new, tag)) {
+ /* new fields */
+ sf_g->dmode = info->dmode;
+ sf_g->fmode = info->fmode;
+ sf_g->dmask = info->dmask;
+ sf_g->fmask = info->fmask;
+ } else {
+ sf_g->dmode = ~0;
+ sf_g->fmode = ~0;
+ }
+
+ if ((unsigned)info->length >= sizeof(struct vbsf_mount_info_new)) {
+ AssertCompile(sizeof(sf_g->tag) >= sizeof(info->tag));
+ memcpy(sf_g->tag, info->tag, sizeof(info->tag));
+ sf_g->tag[sizeof(sf_g->tag) - 1] = '\0';
+ } else {
+ sf_g->tag[0] = '\0';
+ }
+
+ *sf_gp = sf_g;
+ return 0;
+
+ fail2:
+ if (sf_g->nls)
+ unload_nls(sf_g->nls);
+
+ fail1:
+ kfree(sf_g);
+
+ fail0:
+ return err;
+}
+
+/* unmap the share and free global info [sf_g] */
+static void sf_glob_free(struct sf_glob_info *sf_g)
+{
+ int rc;
+
+ TRACE();
+ rc = VbglR0SfUnmapFolder(&client_handle, &sf_g->map);
+ if (RT_FAILURE(rc))
+ LogFunc(("VbglR0SfUnmapFolder failed rc=%d\n", rc));
+
+ if (sf_g->nls)
+ unload_nls(sf_g->nls);
+
+ kfree(sf_g);
+}
+
+/**
+ * This is called (by sf_read_super_[24|26] when vfs mounts the fs and
+ * wants to read super_block.
+ *
+ * calls [sf_glob_alloc] to map the folder and allocate global
+ * information structure.
+ *
+ * initializes [sb], initializes root inode and dentry.
+ *
+ * should respect [flags]
+ */
+static int sf_read_super_aux(struct super_block *sb, void *data, int flags)
+{
+ int err;
+ struct dentry *droot;
+ struct inode *iroot;
+ struct sf_inode_info *sf_i;
+ struct sf_glob_info *sf_g;
+ SHFLFSOBJINFO fsinfo;
+ struct vbsf_mount_info_new *info;
+ bool fInodePut = true;
+
+ TRACE();
+ if (!data) {
+ LogFunc(("no mount info specified\n"));
+ return -EINVAL;
+ }
+
+ info = data;
+
+ if (flags & MS_REMOUNT) {
+ LogFunc(("remounting is not supported\n"));
+ return -ENOSYS;
+ }
+
+ err = sf_glob_alloc(info, &sf_g);
+ if (err)
+ goto fail0;
+
+ sf_i = kmalloc(sizeof(*sf_i), GFP_KERNEL);
+ if (!sf_i) {
+ err = -ENOMEM;
+ LogRelFunc(("could not allocate memory for root inode info\n"));
+ goto fail1;
+ }
+
+ sf_i->handle = SHFL_HANDLE_NIL;
+ sf_i->path = kmalloc(sizeof(SHFLSTRING) + 1, GFP_KERNEL);
+ if (!sf_i->path) {
+ err = -ENOMEM;
+ LogRelFunc(("could not allocate memory for root inode path\n"));
+ goto fail2;
+ }
+
+ sf_i->path->u16Length = 1;
+ sf_i->path->u16Size = 2;
+ sf_i->path->String.utf8[0] = '/';
+ sf_i->path->String.utf8[1] = 0;
+ sf_i->force_reread = 0;
+
+ err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0);
+ if (err) {
+ LogFunc(("could not stat root of share\n"));
+ goto fail3;
+ }
+
+ sb->s_magic = 0xface;
+ sb->s_blocksize = 1024;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 3)
+ /* Required for seek/sendfile.
+ *
+ * Must by less than or equal to INT64_MAX despite the fact that the
+ * declaration of this variable is unsigned long long. See determination
+ * of 'loff_t max' in fs/read_write.c / do_sendfile(). I don't know the
+ * correct limit but MAX_LFS_FILESIZE (8TB-1 on 32-bit boxes) takes the
+ * page cache into account and is the suggested limit. */
+# if defined MAX_LFS_FILESIZE
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+# else
+ sb->s_maxbytes = 0x7fffffffffffffffULL;
+# endif
+#endif
+ sb->s_op = &sf_super_ops;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ iroot = iget_locked(sb, 0);
+#else
+ iroot = iget(sb, 0);
+#endif
+ if (!iroot) {
+ err = -ENOMEM; /* XXX */
+ LogFunc(("could not get root inode\n"));
+ goto fail3;
+ }
+
+ if (sf_init_backing_dev(sf_g)) {
+ err = -EINVAL;
+ LogFunc(("could not init bdi\n"));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ unlock_new_inode(iroot);
+#endif
+ goto fail4;
+ }
+
+ sf_init_inode(sf_g, iroot, &fsinfo);
+ SET_INODE_INFO(iroot, sf_i);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
+ unlock_new_inode(iroot);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ droot = d_make_root(iroot);
+#else
+ droot = d_alloc_root(iroot);
+#endif
+ if (!droot) {
+ err = -ENOMEM; /* XXX */
+ LogFunc(("d_alloc_root failed\n"));
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+ fInodePut = false;
+#endif
+ goto fail5;
+ }
+
+ sb->s_root = droot;
+ SET_GLOB_INFO(sb, sf_g);
+ return 0;
+
+ fail5:
+ sf_done_backing_dev(sf_g);
+
+ fail4:
+ if (fInodePut)
+ iput(iroot);
+
+ fail3:
+ kfree(sf_i->path);
+
+ fail2:
+ kfree(sf_i);
+
+ fail1:
+ sf_glob_free(sf_g);
+
+ fail0:
+ return err;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+static struct super_block *sf_read_super_24(struct super_block *sb, void *data,
+ int flags)
+{
+ int err;
+
+ TRACE();
+ err = sf_read_super_aux(sb, data, flags);
+ if (err)
+ return NULL;
+
+ return sb;
+}
+#endif
+
+/* this is called when vfs is about to destroy the [inode]. all
+ resources associated with this [inode] must be cleared here */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+static void sf_clear_inode(struct inode *inode)
+{
+ struct sf_inode_info *sf_i;
+
+ TRACE();
+ sf_i = GET_INODE_INFO(inode);
+ if (!sf_i)
+ return;
+
+ BUG_ON(!sf_i->path);
+ kfree(sf_i->path);
+ kfree(sf_i);
+ SET_INODE_INFO(inode, NULL);
+}
+#else /* LINUX_VERSION_CODE >= 2.6.36 */
+static void sf_evict_inode(struct inode *inode)
+{
+ struct sf_inode_info *sf_i;
+
+ TRACE();
+ truncate_inode_pages(&inode->i_data, 0);
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
+ clear_inode(inode);
+# else
+ end_writeback(inode);
+# endif
+
+ sf_i = GET_INODE_INFO(inode);
+ if (!sf_i)
+ return;
+
+ BUG_ON(!sf_i->path);
+ kfree(sf_i->path);
+ kfree(sf_i);
+ SET_INODE_INFO(inode, NULL);
+}
+#endif /* LINUX_VERSION_CODE >= 2.6.36 */
+
+/* this is called by vfs when it wants to populate [inode] with data.
+ the only thing that is known about inode at this point is its index
+ hence we can't do anything here, and let lookup/whatever with the
+ job to properly fill then [inode] */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+static void sf_read_inode(struct inode *inode)
+{
+}
+#endif
+
+/* vfs is done with [sb] (umount called) call [sf_glob_free] to unmap
+ the folder and free [sf_g] */
+static void sf_put_super(struct super_block *sb)
+{
+ struct sf_glob_info *sf_g;
+
+ sf_g = GET_GLOB_INFO(sb);
+ BUG_ON(!sf_g);
+ sf_done_backing_dev(sf_g);
+ sf_glob_free(sf_g);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+static int sf_statfs(struct super_block *sb, STRUCT_STATFS * stat)
+{
+ return sf_get_volume_info(sb, stat);
+}
+#else
+static int sf_statfs(struct dentry *dentry, STRUCT_STATFS * stat)
+{
+ struct super_block *sb = dentry->d_inode->i_sb;
+ return sf_get_volume_info(sb, stat);
+}
+#endif
+
+static int sf_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 23)
+ struct sf_glob_info *sf_g;
+ struct sf_inode_info *sf_i;
+ struct inode *iroot;
+ SHFLFSOBJINFO fsinfo;
+ int err;
+
+ sf_g = GET_GLOB_INFO(sb);
+ BUG_ON(!sf_g);
+ if (data && data[0] != 0) {
+ struct vbsf_mount_info_new *info =
+ (struct vbsf_mount_info_new *)data;
+ if (info->signature[0] == VBSF_MOUNT_SIGNATURE_BYTE_0
+ && info->signature[1] == VBSF_MOUNT_SIGNATURE_BYTE_1
+ && info->signature[2] == VBSF_MOUNT_SIGNATURE_BYTE_2) {
+ sf_g->uid = info->uid;
+ sf_g->gid = info->gid;
+ sf_g->ttl = info->ttl;
+ if ((unsigned)info->length >= RT_UOFFSETOF(struct vbsf_mount_info_new, tag)) {
+ sf_g->dmode = info->dmode;
+ sf_g->fmode = info->fmode;
+ sf_g->dmask = info->dmask;
+ sf_g->fmask = info->fmask;
+ } else {
+ sf_g->dmode = ~0;
+ sf_g->fmode = ~0;
+ }
+ if ((unsigned)info->length >= sizeof(struct vbsf_mount_info_new)) {
+ AssertCompile(sizeof(sf_g->tag) >= sizeof(info->tag));
+ memcpy(sf_g->tag, info->tag, sizeof(info->tag));
+ sf_g->tag[sizeof(sf_g->tag) - 1] = '\0';
+ } else {
+ sf_g->tag[0] = '\0';
+ }
+ }
+ }
+
+ iroot = ilookup(sb, 0);
+ if (!iroot)
+ return -ENOSYS;
+
+ sf_i = GET_INODE_INFO(iroot);
+ err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0);
+ BUG_ON(err != 0);
+ sf_init_inode(sf_g, iroot, &fsinfo);
+ /*unlock_new_inode(iroot); */
+ return 0;
+#else /* LINUX_VERSION_CODE < 2.4.23 */
+ return -ENOSYS;
+#endif /* LINUX_VERSION_CODE < 2.4.23 */
+}
+
+/** Show mount options. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+static int sf_show_options(struct seq_file *m, struct vfsmount *mnt)
+#else
+static int sf_show_options(struct seq_file *m, struct dentry *root)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+ struct super_block *sb = mnt->mnt_sb;
+#else
+ struct super_block *sb = root->d_sb;
+#endif
+ struct sf_glob_info *sf_g = GET_GLOB_INFO(sb);
+ if (sf_g) {
+ seq_printf(m, ",uid=%u,gid=%u,ttl=%u,dmode=0%o,fmode=0%o,dmask=0%o,fmask=0%o",
+ sf_g->uid, sf_g->gid, sf_g->ttl, sf_g->dmode, sf_g->fmode, sf_g->dmask, sf_g->fmask);
+ if (sf_g->tag[0] != '\0') {
+ seq_puts(m, ",tag=");
+ seq_escape(m, sf_g->tag, " \t\n\\");
+ }
+ }
+
+ return 0;
+}
+
+static struct super_operations sf_super_ops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+ .clear_inode = sf_clear_inode,
+#else
+ .evict_inode = sf_evict_inode,
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
+ .read_inode = sf_read_inode,
+#endif
+ .put_super = sf_put_super,
+ .statfs = sf_statfs,
+ .remount_fs = sf_remount_fs,
+ .show_options = sf_show_options
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+static DECLARE_FSTYPE(vboxsf_fs_type, "vboxsf", sf_read_super_24, 0);
+#else
+static int sf_read_super_26(struct super_block *sb, void *data, int flags)
+{
+ int err;
+
+ TRACE();
+ err = sf_read_super_aux(sb, data, flags);
+ if (err)
+ printk(KERN_DEBUG "sf_read_super_aux err=%d\n", err);
+
+ return err;
+}
+
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+static struct super_block *sf_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data)
+{
+ TRACE();
+ return get_sb_nodev(fs_type, flags, data, sf_read_super_26);
+}
+# elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+static int sf_get_sb(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ TRACE();
+ return get_sb_nodev(fs_type, flags, data, sf_read_super_26, mnt);
+}
+# else /* LINUX_VERSION_CODE >= 2.6.39 */
+static struct dentry *sf_mount(struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data)
+{
+ TRACE();
+ return mount_nodev(fs_type, flags, data, sf_read_super_26);
+}
+# endif /* LINUX_VERSION_CODE >= 2.6.39 */
+
+static struct file_system_type vboxsf_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "vboxsf",
+# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
+ .get_sb = sf_get_sb,
+# else
+ .mount = sf_mount,
+# endif
+ .kill_sb = kill_anon_super
+};
+
+#endif /* LINUX_VERSION_CODE >= 2.6.0 */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+static int follow_symlinks = 0;
+module_param(follow_symlinks, int, 0);
+MODULE_PARM_DESC(follow_symlinks,
+ "Let host resolve symlinks rather than showing them");
+#endif
+
+/* Module initialization/finalization handlers */
+static int __init init(void)
+{
+ int rcVBox;
+ int rcRet = 0;
+ int err;
+
+ TRACE();
+
+ if (sizeof(struct vbsf_mount_info_new) > PAGE_SIZE) {
+ printk(KERN_ERR
+ "Mount information structure is too large %lu\n"
+ "Must be less than or equal to %lu\n",
+ (unsigned long)sizeof(struct vbsf_mount_info_new),
+ (unsigned long)PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ err = register_filesystem(&vboxsf_fs_type);
+ if (err) {
+ LogFunc(("register_filesystem err=%d\n", err));
+ return err;
+ }
+
+ rcVBox = VbglR0HGCMInit();
+ if (RT_FAILURE(rcVBox)) {
+ LogRelFunc(("VbglR0HGCMInit failed, rc=%d\n", rcVBox));
+ rcRet = -EPROTO;
+ goto fail0;
+ }
+
+ rcVBox = VbglR0SfConnect(&client_handle);
+ if (RT_FAILURE(rcVBox)) {
+ LogRelFunc(("VbglR0SfConnect failed, rc=%d\n", rcVBox));
+ rcRet = -EPROTO;
+ goto fail1;
+ }
+
+ rcVBox = VbglR0SfSetUtf8(&client_handle);
+ if (RT_FAILURE(rcVBox)) {
+ LogRelFunc(("VbglR0SfSetUtf8 failed, rc=%d\n", rcVBox));
+ rcRet = -EPROTO;
+ goto fail2;
+ }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ if (!follow_symlinks) {
+ rcVBox = VbglR0SfSetSymlinks(&client_handle);
+ if (RT_FAILURE(rcVBox)) {
+ printk(KERN_WARNING
+ "vboxsf: Host unable to show symlinks, rc=%d\n",
+ rcVBox);
+ }
+ }
+#endif
+
+ printk(KERN_DEBUG
+ "vboxsf: Successfully loaded version " VBOX_VERSION_STRING
+ " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
+
+ return 0;
+
+ fail2:
+ VbglR0SfDisconnect(&client_handle);
+
+ fail1:
+ VbglR0HGCMTerminate();
+
+ fail0:
+ unregister_filesystem(&vboxsf_fs_type);
+ return rcRet;
+}
+
+static void __exit fini(void)
+{
+ TRACE();
+
+ VbglR0SfDisconnect(&client_handle);
+ VbglR0HGCMTerminate();
+ unregister_filesystem(&vboxsf_fs_type);
+}
+
+module_init(init);
+module_exit(fini);
+
+/* C++ hack */
+int __gxx_personality_v0 = 0xdeadbeef;
diff --git a/src/VBox/Additions/linux/sharedfolders/vfsmod.h b/src/VBox/Additions/linux/sharedfolders/vfsmod.h
new file mode 100644
index 00000000..f7b71831
--- /dev/null
+++ b/src/VBox/Additions/linux/sharedfolders/vfsmod.h
@@ -0,0 +1,181 @@
+/* $Id: vfsmod.h $ */
+/** @file
+ * vboxsf - Linux Shared Folders VFS, internal header.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * 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_linux_sharedfolders_vfsmod_h
+#define GA_INCLUDED_SRC_linux_sharedfolders_vfsmod_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
+#include "the-linux-kernel.h"
+#include <VBox/log.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# include <linux/backing-dev.h>
+#endif
+
+#include <VBox/VBoxGuestLibSharedFolders.h>
+#include "vbsfmount.h"
+
+#define DIR_BUFFER_SIZE (16*_1K)
+
+/* per-shared folder information */
+struct sf_glob_info {
+ VBGLSFMAP map;
+ struct nls_table *nls;
+ int ttl;
+ int uid;
+ int gid;
+ int dmode;
+ int fmode;
+ int dmask;
+ int fmask;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+ struct backing_dev_info bdi;
+#endif
+ char tag[32]; /**< Mount tag for VBoxService automounter. @since 6.0 */
+};
+
+/* per-inode information */
+struct sf_inode_info {
+ /* which file */
+ SHFLSTRING *path;
+ /* some information was changed, update data on next revalidate */
+ int force_restat;
+ /* directory content changed, update the whole directory on next sf_getdent */
+ int force_reread;
+ /* file structure, only valid between open() and release() */
+ struct file *file;
+ /* handle valid if a file was created with sf_create_aux until it will
+ * be opened with sf_reg_open() */
+ SHFLHANDLE handle;
+};
+
+struct sf_dir_info {
+ struct list_head info_list;
+};
+
+struct sf_dir_buf {
+ size_t cEntries;
+ size_t cbFree;
+ size_t cbUsed;
+ void *buf;
+ struct list_head head;
+};
+
+struct sf_reg_info {
+ SHFLHANDLE handle;
+};
+
+/* globals */
+extern VBGLSFCLIENT client_handle;
+
+/* forward declarations */
+extern struct inode_operations sf_dir_iops;
+extern struct inode_operations sf_lnk_iops;
+extern struct inode_operations sf_reg_iops;
+extern struct file_operations sf_dir_fops;
+extern struct file_operations sf_reg_fops;
+extern struct dentry_operations sf_dentry_ops;
+extern struct address_space_operations sf_reg_aops;
+
+extern void sf_init_inode(struct sf_glob_info *sf_g, struct inode *inode,
+ PSHFLFSOBJINFO info);
+extern int sf_stat(const char *caller, struct sf_glob_info *sf_g,
+ SHFLSTRING * path, PSHFLFSOBJINFO result, int ok_to_fail);
+extern int sf_inode_revalidate(struct dentry *dentry);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+extern int sf_getattr(const struct path *path, struct kstat *kstat,
+ u32 request_mask, unsigned int query_flags);
+# else
+extern int sf_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *kstat);
+# endif
+extern int sf_setattr(struct dentry *dentry, struct iattr *iattr);
+#endif
+extern int sf_path_from_dentry(const char *caller, struct sf_glob_info *sf_g,
+ struct sf_inode_info *sf_i,
+ struct dentry *dentry, SHFLSTRING ** result);
+extern int sf_nlscpy(struct sf_glob_info *sf_g, char *name,
+ size_t name_bound_len, const unsigned char *utf8_name,
+ size_t utf8_len);
+extern void sf_dir_info_free(struct sf_dir_info *p);
+extern void sf_dir_info_empty(struct sf_dir_info *p);
+extern struct sf_dir_info *sf_dir_info_alloc(void);
+extern int sf_dir_read_all(struct sf_glob_info *sf_g,
+ struct sf_inode_info *sf_i, struct sf_dir_info *sf_d,
+ SHFLHANDLE handle);
+extern int sf_init_backing_dev(struct sf_glob_info *sf_g);
+extern void sf_done_backing_dev(struct sf_glob_info *sf_g);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+# define STRUCT_STATFS struct statfs
+#else
+# define STRUCT_STATFS struct kstatfs
+#endif
+int sf_get_volume_info(struct super_block *sb, STRUCT_STATFS * stat);
+
+#ifdef __cplusplus
+# define CMC_API __attribute__ ((cdecl, regparm (0)))
+#else
+# define CMC_API __attribute__ ((regparm (0)))
+#endif
+
+#define TRACE() LogFunc(("tracepoint\n"))
+
+/* Following casts are here to prevent assignment of void * to
+ pointers of arbitrary type */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
+# define GET_GLOB_INFO(sb) ((struct sf_glob_info *) (sb)->u.generic_sbp)
+# define SET_GLOB_INFO(sb, sf_g) (sb)->u.generic_sbp = sf_g
+#else
+# define GET_GLOB_INFO(sb) ((struct sf_glob_info *) (sb)->s_fs_info)
+# define SET_GLOB_INFO(sb, sf_g) (sb)->s_fs_info = sf_g
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) || defined(KERNEL_FC6)
+/* FC6 kernel 2.6.18, vanilla kernel 2.6.19+ */
+# define GET_INODE_INFO(i) ((struct sf_inode_info *) (i)->i_private)
+# define SET_INODE_INFO(i, sf_i) (i)->i_private = sf_i
+#else
+/* vanilla kernel up to 2.6.18 */
+# define GET_INODE_INFO(i) ((struct sf_inode_info *) (i)->u.generic_ip)
+# define SET_INODE_INFO(i, sf_i) (i)->u.generic_ip = sf_i
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+# define GET_F_DENTRY(f) (f->f_path.dentry)
+#else
+# define GET_F_DENTRY(f) (f->f_dentry)
+#endif
+
+#endif /* !GA_INCLUDED_SRC_linux_sharedfolders_vfsmod_h */