diff options
Diffstat (limited to 'src/VBox/Additions/linux/sharedfolders')
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/.scm-settings | 29 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/Makefile.kmk | 45 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/Makefile.module | 91 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/dirops.c | 893 | ||||
-rwxr-xr-x | src/VBox/Additions/linux/sharedfolders/files_vboxsf | 89 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/lnkops.c | 119 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/mount.vboxsf.c | 538 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/regops.c | 876 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/utils.c | 883 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/vbsfmount.c | 95 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/vbsfmount.h | 90 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/vfsmod.c | 683 | ||||
-rw-r--r-- | src/VBox/Additions/linux/sharedfolders/vfsmod.h | 181 |
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, ¶ms); + 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, + ¶ms); + 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, ¶ms); + 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, ¶ms.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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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 */ |