From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756946AbdHYOiT (ORCPT ); Fri, 25 Aug 2017 10:38:19 -0400 Received: from mx1.redhat.com ([209.132.183.28]:58802 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756900AbdHYOhm (ORCPT ); Fri, 25 Aug 2017 10:37:42 -0400 DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com C906F61483 Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=fail smtp.mailfrom=hdegoede@redhat.com From: Hans de Goede To: Arnd Bergmann , Greg Kroah-Hartman Cc: Hans de Goede , Michael Thayer , "Knut St . Osmundsen" , Larry Finger , linux-kernel@vger.kernel.org Subject: [RFC v2 2/2] fs: Add VirtualBox guest shared folder (vboxsf) support Date: Fri, 25 Aug 2017 16:37:32 +0200 Message-Id: <20170825143732.10836-3-hdegoede@redhat.com> In-Reply-To: <20170825143732.10836-1-hdegoede@redhat.com> References: <20170825143732.10836-1-hdegoede@redhat.com> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Fri, 25 Aug 2017 14:37:41 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org VirtualBox hosts can share folders with guests, this commit adds a VFS driver implementing the Linux-guest side of this, allowing folders exported by the host to be mounted under Linux. This driver depends on the guest <-> host IPC functions exported by the vboxguest driver. Signed-off-by: Hans de Goede --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/vboxsf/Kconfig | 9 + fs/vboxsf/Makefile | 3 + fs/vboxsf/dirops.c | 705 ++++++++++++++++++++++ fs/vboxsf/lnkops.c | 37 ++ fs/vboxsf/regops.c | 558 +++++++++++++++++ fs/vboxsf/shfl_hostintf.h | 1283 ++++++++++++++++++++++++++++++++++++++++ fs/vboxsf/utils.c | 674 +++++++++++++++++++++ fs/vboxsf/vboxsf_wrappers.c | 519 ++++++++++++++++ fs/vboxsf/vboxsf_wrappers.h | 98 +++ fs/vboxsf/vfsmod.c | 386 ++++++++++++ fs/vboxsf/vfsmod.h | 113 ++++ include/uapi/linux/vbsfmount.h | 62 ++ 14 files changed, 4449 insertions(+) create mode 100644 fs/vboxsf/Kconfig create mode 100644 fs/vboxsf/Makefile create mode 100644 fs/vboxsf/dirops.c create mode 100644 fs/vboxsf/lnkops.c create mode 100644 fs/vboxsf/regops.c create mode 100644 fs/vboxsf/shfl_hostintf.h create mode 100644 fs/vboxsf/utils.c create mode 100644 fs/vboxsf/vboxsf_wrappers.c create mode 100644 fs/vboxsf/vboxsf_wrappers.h create mode 100644 fs/vboxsf/vfsmod.c create mode 100644 fs/vboxsf/vfsmod.h create mode 100644 include/uapi/linux/vbsfmount.h diff --git a/fs/Kconfig b/fs/Kconfig index 7aee6d699fd6..7f80ad1cf591 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -248,6 +248,7 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" +source "fs/vboxsf/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index 7bbaca9c67b1..6b5039e3ade5 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -128,3 +128,4 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ +obj-$(CONFIG_VBOXSF_FS) += vboxsf/ diff --git a/fs/vboxsf/Kconfig b/fs/vboxsf/Kconfig new file mode 100644 index 000000000000..620e2232969c --- /dev/null +++ b/fs/vboxsf/Kconfig @@ -0,0 +1,9 @@ +config VBOXSF_FS + tristate "VirtualBox guest shared folder (vboxsf) support" + depends on VBOXGUEST + help + VirtualBox hosts can share folders with guests, this driver + implements the Linux-guest side of this allowing folders exported + by the host to be mounted under Linux. + + If you want to use shared folders in VirtualBox guests, answer Y or M. diff --git a/fs/vboxsf/Makefile b/fs/vboxsf/Makefile new file mode 100644 index 000000000000..f3529ea02d5c --- /dev/null +++ b/fs/vboxsf/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_VBOXSF_FS) += vboxsf.o + +vboxsf-objs := dirops.o lnkops.o regops.o utils.o vboxsf_wrappers.o vfsmod.o diff --git a/fs/vboxsf/dirops.c b/fs/vboxsf/dirops.c new file mode 100644 index 000000000000..20b9a5b14dbb --- /dev/null +++ b/fs/vboxsf/dirops.c @@ -0,0 +1,705 @@ +/* + * VirtualBox Guest Shared Folders support: Directory inode and file operations + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "vfsmod.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); + struct shfl_createparms params = {}; + + if (file->private_data) + return 0; + + sf_d = sf_dir_info_alloc(); + if (!sf_d) + return -ENOMEM; + + params.handle = SHFL_HANDLE_NIL; + params.create_flags = 0 + | SHFL_CF_DIRECTORY + | SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ; + + rc = vboxsf_create(sf_g->root, sf_i->path, ¶ms); + if (rc >= 0) { + 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; + + vboxsf_close(sf_g->root, params.handle); + } 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) +{ + if (file->private_data) + sf_dir_info_free(file->private_data); + + return 0; +} + +/** + * Translate RTFMODE into DT_xxx (in conjunction to rtDirType()) + * @param mode file mode + * returns d_type + */ +static int sf_get_d_type(u32 mode) +{ + int d_type; + + switch (mode & SHFL_TYPE_MASK) { + case SHFL_TYPE_FIFO: + d_type = DT_FIFO; + break; + case SHFL_TYPE_DEV_CHAR: + d_type = DT_CHR; + break; + case SHFL_TYPE_DIRECTORY: + d_type = DT_DIR; + break; + case SHFL_TYPE_DEV_BLOCK: + d_type = DT_BLK; + break; + case SHFL_TYPE_FILE: + d_type = DT_REG; + break; + case SHFL_TYPE_SYMLINK: + d_type = DT_LNK; + break; + case SHFL_TYPE_SOCKET: + d_type = DT_SOCK; + break; + case SHFL_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; + + 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; + + if (sf_i->force_reread) { + int rc; + int err; + struct shfl_createparms params = {}; + + params.handle = SHFL_HANDLE_NIL; + params.create_flags = 0 + | SHFL_CF_DIRECTORY + | SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ; + + rc = vboxsf_create(sf_g->root, sf_i->path, ¶ms); + if (rc < 0) + return -EPERM; + + if (params.result != SHFL_FILE_EXISTS) { + 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); + vboxsf_close(sf_g->root, params.handle); + 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; + struct shfl_dirinfo *info; + loff_t i; + + b = list_entry(pos, struct sf_dir_buf, head); + if (dir->f_pos >= cur + b->entries) { + cur += b->entries; + continue; + } + + for (i = 0, info = b->buf; i < dir->f_pos - cur; ++i) { + size_t size; + + size = offsetof(struct shfl_dirinfo, name.string) + + info->name.size; + info = (struct shfl_dirinfo *)((uintptr_t) info + size); + } + + *d_type = sf_get_d_type(info->info.attr.mode); + + return sf_nlscpy(sf_g, d_name, NAME_MAX, + info->name.string.utf8, info->name.length); + } + + 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) + */ +static int sf_dir_iterate(struct file *dir, struct dir_context *ctx) +{ + 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 */ + dir->f_pos += 1; + ctx->pos += 1; + continue; + } + + /* d_name now contains a valid entry name */ + sanity = ctx->pos + 0xbeef; + fake_ino = sanity; + /* + * On 32 bit systems pos is 64 signed, while ino is 32 bit + * unsigned so fake_ino may overflow, check for this. + */ + if (sanity - fake_ino) { + vbg_err("vboxsf: can not compute ino\n"); + return -EINVAL; + } + if (!dir_emit(ctx, d_name, strlen(d_name), fake_ino, d_type)) + return 0; + + dir->f_pos += 1; + ctx->pos += 1; + } +} + +const struct file_operations sf_dir_fops = { + .open = sf_dir_open, + .iterate = sf_dir_iterate, + .release = sf_dir_release, + .read = generic_read_dir, + .llseek = generic_file_llseek, +}; + +/* 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, + unsigned int flags) +{ + int err; + struct sf_inode_info *sf_i, *sf_new_i; + struct sf_glob_info *sf_g; + struct shfl_string *path; + struct inode *inode; + ino_t ino; + struct shfl_fsobjinfo fsinfo; + + sf_g = GET_GLOB_INFO(parent->i_sb); + sf_i = GET_INODE_INFO(parent); + + 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) { + err = -ENOMEM; + goto fail1; + } + sf_new_i->handle = SHFL_HANDLE_NIL; + sf_new_i->force_reread = 0; + + ino = iunique(parent->i_sb, 1); + inode = iget_locked(parent->i_sb, ino); + if (!inode) { + err = -ENOMEM; /* XXX: ??? */ + goto fail2; + } + + SET_INODE_INFO(inode, sf_new_i); + sf_init_inode(sf_g, inode, &fsinfo); + sf_new_i->path = path; + + unlock_new_inode(inode); + } + + sf_i->force_restat = 0; + dentry->d_time = jiffies; + d_set_d_op(dentry, &sf_dentry_ops); + 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, + struct shfl_string *path, struct shfl_fsobjinfo *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); + + sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL); + if (!sf_new_i) { + err = -ENOMEM; + goto fail0; + } + + ino = iunique(parent->i_sb, 1); + inode = iget_locked(parent->i_sb, ino); + if (!inode) { + 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); + unlock_new_inode(inode); + + /* 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; + struct shfl_createparms params = {}; + struct shfl_string *path; + struct sf_inode_info *sf_i = GET_INODE_INFO(parent); + struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb); + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + params.handle = SHFL_HANDLE_NIL; + params.create_flags = 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.mode = 0 + | (fDirectory ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE) + | (mode & 0777); + params.info.attr.additional = SHFLFSOBJATTRADD_NOTHING; + + rc = vboxsf_create(sf_g->root, path, ¶ms); + if (rc < 0) { + if (rc == VERR_WRITE_PROTECT) { + err = -EROFS; + goto fail1; + } + err = -EPROTO; + goto fail1; + } + + if (params.result != SHFL_FILE_CREATED) { + err = -EPERM; + goto fail1; + } + + err = sf_instantiate(parent, dentry, path, ¶ms.info, + fDirectory ? SHFL_HANDLE_NIL : params.handle); + if (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) + vboxsf_close(sf_g->root, params.handle); + + sf_i->force_restat = 1; + return 0; + +fail2: + vboxsf_close(sf_g->root, params.handle); + +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 + */ +static int sf_create(struct inode *parent, struct dentry *dentry, umode_t mode, + bool excl) +{ + 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 + */ +static int sf_mkdir(struct inode *parent, struct dentry *dentry, umode_t mode) +{ + 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); + struct shfl_string *path; + uint32_t flags; + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + flags = fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE; + if (dentry + && dentry->d_inode + && ((dentry->d_inode->i_mode & S_IFLNK) == S_IFLNK)) + flags |= SHFL_REMOVE_SYMLINK; + rc = vboxsf_remove(sf_g->root, path, flags); + if (rc < 0) { + err = vbg_status_code_to_errno(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) +{ + 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) +{ + 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, + unsigned int flags) +{ + int err = 0, rc = VINF_SUCCESS; + struct sf_glob_info *sf_g = GET_GLOB_INFO(old_parent->i_sb); + u32 shfl_flags = SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS; + + if (flags) + return -EINVAL; + + if (sf_g != GET_GLOB_INFO(new_parent->i_sb)) { + 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); + struct shfl_string *old_path; + struct shfl_string *new_path; + + old_path = sf_file_i->path; + err = sf_path_from_dentry(__func__, sf_g, sf_new_i, + new_dentry, &new_path); + if (err == 0) { + if (old_dentry->d_inode->i_mode & S_IFDIR) + shfl_flags = 0; + + rc = vboxsf_rename(sf_g->root, old_path, new_path, + shfl_flags); + if (rc >= 0) { + 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 { + err = vbg_status_code_to_errno(rc); + kfree(new_path); + } + } + } + return err; +} + +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; + struct shfl_string *path, *ssymname; + struct shfl_fsobjinfo info; + int symname_len = strlen(symname) + 1; + + sf_g = GET_GLOB_INFO(parent->i_sb); + sf_i = GET_INODE_INFO(parent); + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + ssymname = + kmalloc(offsetof(struct shfl_string, string.utf8) + symname_len, + GFP_KERNEL); + if (!ssymname) { + err = -ENOMEM; + goto fail1; + } + + ssymname->length = symname_len - 1; + ssymname->size = symname_len; + memcpy(ssymname->string.utf8, symname, symname_len); + + rc = vboxsf_symlink(sf_g->root, path, ssymname, &info); + kfree(ssymname); + + if (rc < 0) { + if (rc == VERR_WRITE_PROTECT) { + err = -EROFS; + goto fail1; + } + err = -EPROTO; + goto fail1; + } + + err = sf_instantiate(parent, dentry, path, &info, SHFL_HANDLE_NIL); + if (err) + goto fail1; + + sf_i->force_restat = 1; + return 0; + +fail1: + kfree(path); +fail0: + return err; +} + +const struct inode_operations sf_dir_iops = { + .lookup = sf_lookup, + .create = sf_create, + .mkdir = sf_mkdir, + .rmdir = sf_rmdir, + .unlink = sf_unlink, + .rename = sf_rename, + .getattr = sf_getattr, + .setattr = sf_setattr, + .symlink = sf_symlink +}; diff --git a/fs/vboxsf/lnkops.c b/fs/vboxsf/lnkops.c new file mode 100644 index 000000000000..da4cbfa3c886 --- /dev/null +++ b/fs/vboxsf/lnkops.c @@ -0,0 +1,37 @@ +/* + * VirtualBox Guest Shared Folders: Operations for symbolic links. + * + * Copyright (C) 2010-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "vfsmod.h" + +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 = vboxsf_readlink(sf_g->root, sf_i->path, PATH_MAX, path); + if (rc < 0) { + kfree(path); + return ERR_PTR(-EPROTO); + } + set_delayed_call(done, kfree_link, path); + return path; +} + +const struct inode_operations sf_lnk_iops = { + .get_link = sf_get_link +}; diff --git a/fs/vboxsf/regops.c b/fs/vboxsf/regops.c new file mode 100644 index 000000000000..879793065f7e --- /dev/null +++ b/fs/vboxsf/regops.c @@ -0,0 +1,558 @@ +/* + * VirtualBox Guest Shared Folders support: Regular file inode and file ops. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Limitations: only COW memory mapping is supported + */ + +#include +#include "vfsmod.h" + +static void *alloc_bounce_buffer(size_t *tmp_sizep, u64 *physp, size_t + xfer_size, const char *caller) +{ + size_t tmp_size; + void *tmp; + + /* try for big first. */ + tmp_size = PAGE_ALIGN(xfer_size); + if (tmp_size > SZ_16K) + tmp_size = SZ_16K; + tmp = kmalloc(tmp_size, GFP_KERNEL); + if (!tmp) { + /* fall back on a page sized buffer. */ + tmp = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp) + 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, + u32 *nread, u64 pos) +{ + int rc; + + /** + * @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. + */ + rc = vboxsf_read(sf_g->root, sf_r->handle, pos, nread, buf); + if (rc < 0) + 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, + u32 *nwritten, u64 pos) +{ + int rc; + + /** @todo bird: see sf_reg_read_aux */ + rc = vboxsf_write(sf_g->root, sf_r->handle, pos, nwritten, buf); + if (rc < 0) + return -EPROTO; + + return 0; +} + +/** + * 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; + u64 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; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + /** 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) { + u32 to_read, nread; + + to_read = tmp_size; + if (to_read > left) + to_read = 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; + u64 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; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + pos = *off; + if (file->f_flags & O_APPEND) { + pos = inode->i_size; + *off = pos; + } + + /** 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) { + u32 to_write, nwritten; + + to_write = tmp_size; + if (to_write > left) + to_write = (u32) left; + + nwritten = to_write; + + if (copy_from_user(tmp, buf, to_write)) { + err = -EFAULT; + goto fail; + } + + err = vboxsf_write_physcont(sf_g->root, sf_r->handle, pos, + &nwritten, tmp_phys); + if (err < 0) { + err = -EPROTO; + 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; + struct shfl_createparms params = {}; + + sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL); + if (!sf_r) + return -ENOMEM; + + /* Already open? */ + if (sf_i->handle != SHFL_HANDLE_NIL) { + /* + * This inode was created with sf_create_aux(). Check the + * create_flags: 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; + } + + 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 mode parameter. + */ + if (file->f_flags & O_CREAT) { + params.create_flags |= 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) + params.create_flags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS; + else + params.create_flags |= SHFL_CF_ACT_OPEN_IF_EXISTS; + } else { + params.create_flags |= SHFL_CF_ACT_FAIL_IF_NEW; + if (file->f_flags & O_TRUNC) + params.create_flags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS; + } + + switch (file->f_flags & O_ACCMODE) { + case O_RDONLY: + params.create_flags |= SHFL_CF_ACCESS_READ; + break; + + case O_WRONLY: + params.create_flags |= SHFL_CF_ACCESS_WRITE; + break; + + case O_RDWR: + params.create_flags |= SHFL_CF_ACCESS_READWRITE; + break; + + default: + WARN_ON(1); + } + + if (file->f_flags & O_APPEND) + params.create_flags |= SHFL_CF_ACCESS_APPEND; + + params.info.attr.mode = inode->i_mode; + rc = vboxsf_create(sf_g->root, sf_i->path, ¶ms); + if (rc < 0) { + kfree(sf_r); + return vbg_status_code_to_errno(rc); + } + + if (params.handle == SHFL_HANDLE_NIL) { + 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) +{ + struct sf_reg_info *sf_r; + struct sf_glob_info *sf_g; + struct sf_inode_info *sf_i = GET_INODE_INFO(inode); + + sf_g = GET_GLOB_INFO(inode->i_sb); + sf_r = file->private_data; + + filemap_write_and_wait(inode->i_mapping); + + vboxsf_close(sf_g->root, sf_r->handle); + + kfree(sf_r); + sf_i->file = NULL; + sf_i->handle = SHFL_HANDLE_NIL; + file->private_data = NULL; + return 0; +} + +static int sf_reg_fault(struct vm_fault *vmf) +{ + struct page *page; + char *buf; + loff_t off; + u32 nread = PAGE_SIZE; + int err; + struct vm_area_struct *vma = vmf->vma; + 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; + + if (vmf->pgoff > vma->vm_end) + return VM_FAULT_SIGBUS; + + /* + * Don't use GFP_HIGHUSER as long as sf_reg_read_aux() calls + * vboxsf_read() which works on virtual addresses. + */ + page = alloc_page(GFP_USER); + if (!page) + return VM_FAULT_OOM; + + buf = kmap(page); + off = (vmf->pgoff << PAGE_SHIFT); + err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off); + if (err) { + kunmap(page); + put_page(page); + return VM_FAULT_SIGBUS; + } + + if (!nread) + clear_user_page(page_address(page), vmf->pgoff, page); + else + memset(buf + nread, 0, PAGE_SIZE - nread); + + flush_dcache_page(page); + kunmap(page); + vmf->page = page; + return 0; +} + +static const struct vm_operations_struct sf_vma_ops = { + .fault = sf_reg_fault +}; + +static int sf_reg_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_SHARED) + return -EINVAL; + + vma->vm_ops = &sf_vma_ops; + return 0; +} + +const 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, + .splice_read = generic_file_splice_read, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, + .fsync = noop_fsync, + .llseek = generic_file_llseek, +}; + +const struct inode_operations sf_reg_iops = { + .getattr = sf_getattr, + .setattr = sf_setattr +}; + +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; + u32 nread = PAGE_SIZE; + char *buf; + loff_t off = ((loff_t) page->index) << PAGE_SHIFT; + int err; + + buf = kmap(page); + err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off); + if (err) { + kunmap(page); + if (PageLocked(page)) + unlock_page(page); + return err; + } + 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; + u32 nwritten = PAGE_SIZE; + int end_index = inode->i_size >> PAGE_SHIFT; + loff_t off = ((loff_t) page->index) << PAGE_SHIFT; + int err; + + 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; +} + +int sf_write_begin(struct file *file, struct address_space *mapping, loff_t pos, + unsigned int len, unsigned int flags, struct page **pagep, + void **fsdata) +{ + 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 int len, unsigned int 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; + unsigned int from = pos & (PAGE_SIZE - 1); + u32 nwritten = len; + void *buf; + int err; + + buf = kmap(page); + err = + sf_reg_write_aux(__func__, sf_g, sf_r, buf + from, &nwritten, pos); + kunmap(page); + + if (!PageUptodate(page) && err == PAGE_SIZE) + SetPageUptodate(page); + + if (err >= 0) { + pos += nwritten; + if (pos > inode->i_size) + inode->i_size = pos; + } + + unlock_page(page); + put_page(page); + + return nwritten; +} + +const struct address_space_operations sf_reg_aops = { + .readpage = sf_readpage, + .writepage = sf_writepage, + .write_begin = sf_write_begin, + .write_end = sf_write_end, +}; diff --git a/fs/vboxsf/shfl_hostintf.h b/fs/vboxsf/shfl_hostintf.h new file mode 100644 index 000000000000..42e5509ceb1c --- /dev/null +++ b/fs/vboxsf/shfl_hostintf.h @@ -0,0 +1,1283 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * VirtualBox Shared Folders: host interface definition. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef SHFL_HOSTINTF_H +#define SHFL_HOSTINTF_H + +#include + +/* + * We cannot use linux' compiletime_assert here because it expects to be used + * inside a function only. Use a typedef to a char array with a negative size. + */ +#define VBOXSF_ASSERT_SIZE(type, size) \ + typedef char type ## _asrt_size[1 - 2*!!(sizeof(struct type) != (size))] +#define VBOXSF_ASSERT_MEMBER_OFFSET(type, member, offset) \ + typedef char type ## _ ## member ## _asrt_member_offset \ + [1 - 2*!!(offsetof(struct type, member) != (offset))] + +/** + * Structures shared between guest and the service + * can be relocated and use offsets to point to variable + * length parts. + */ + +/** + * Shared folders protocol works with handles. + * Before doing any action on a file system object, + * one have to obtain the object handle via a SHFL_FN_CREATE + * request. A handle must be closed with SHFL_FN_CLOSE. + */ + +/** + * Shared Folders service functions. (guest) + * @{ + */ + +/** Query mappings changes. */ +#define SHFL_FN_QUERY_MAPPINGS (1) +/** Query mappings changes. */ +#define SHFL_FN_QUERY_MAP_NAME (2) +/** Open/create object. */ +#define SHFL_FN_CREATE (3) +/** Close object handle. */ +#define SHFL_FN_CLOSE (4) +/** Read object content. */ +#define SHFL_FN_READ (5) +/** Write new object content. */ +#define SHFL_FN_WRITE (6) +/** Lock/unlock a range in the object. */ +#define SHFL_FN_LOCK (7) +/** List object content. */ +#define SHFL_FN_LIST (8) +/** Query/set object information. */ +#define SHFL_FN_INFORMATION (9) +/** Remove object */ +#define SHFL_FN_REMOVE (11) +/** Map folder (legacy) */ +#define SHFL_FN_MAP_FOLDER_OLD (12) +/** Unmap folder */ +#define SHFL_FN_UNMAP_FOLDER (13) +/** Rename object (possibly moving it to another directory) */ +#define SHFL_FN_RENAME (14) +/** Flush file */ +#define SHFL_FN_FLUSH (15) +/** @todo macl, a description, please. */ +#define SHFL_FN_SET_UTF8 (16) +#define SHFL_CPARMS_SET_UTF8 0 +/** Map folder */ +#define SHFL_FN_MAP_FOLDER (17) +/** Read symlink destination (as of VBox 4.0) */ +#define SHFL_FN_READLINK (18) +/** Create symlink (as of VBox 4.0) */ +#define SHFL_FN_SYMLINK (19) +/** Ask host to show symlinks (as of VBox 4.0) */ +#define SHFL_FN_SET_SYMLINKS (20) +#define SHFL_CPARMS_SET_SYMLINKS 0 + +/** @} */ + +/** + * Shared Folders service functions. (host) + * @{ + */ + +/** Add shared folder mapping. */ +#define SHFL_FN_ADD_MAPPING (1) +/** Remove shared folder mapping. */ +#define SHFL_FN_REMOVE_MAPPING (2) +/** Set the led status light address. */ +#define SHFL_FN_SET_STATUS_LED (3) +/** Allow the guest to create symbolic links (as of VBox 4.0) */ +#define SHFL_FN_ALLOW_SYMLINKS_CREATE (4) +/** @} */ + +/** + * Root handle for a mapping. Root handles are unique. + * @note + * Function parameters structures consider + * the root handle as 32 bit value. If the typedef + * will be changed, then function parameters must be + * changed accordingly. All those parameters are marked + * with SHFLROOT in comments. + */ +#define SHFLROOT u32 + +#define SHFL_ROOT_NIL ((SHFLROOT)~0) + +/** A shared folders handle for an opened object. */ +#define SHFLHANDLE u64 + +#define SHFL_HANDLE_NIL ((SHFLHANDLE)~0LL) +#define SHFL_HANDLE_ROOT ((SHFLHANDLE)0LL) + +/** Hardcoded maximum length (in chars) of a shared folder name. */ +#define SHFL_MAX_LEN (256) +/** Hardcoded maximum number of shared folder mapping available to the guest. */ +#define SHFL_MAX_MAPPINGS (64) + +/** + * @name Shared Folders strings. They can be either UTF-8 or UTF-16. + * @{ + */ + +/** Shared folder string buffer structure. */ +struct shfl_string { + /** Allocated size of the string member in bytes. */ + u16 size; + + /** Length of string without trailing nul in bytes. */ + u16 length; + + /** UTF-8 or UTF-16 string. Nul terminated. */ + union { + u8 utf8[1]; + u16 utf16[1]; + u16 ucs2[1]; /**< misnomer, use utf16. */ + } string; +}; +VBOXSF_ASSERT_SIZE(shfl_string, 6); +VBOXSF_ASSERT_MEMBER_OFFSET(shfl_string, string, 4); + +/** The size of shfl_string w/o the string part. */ +#define SHFLSTRING_HEADER_SIZE 4 + +/** Calculate size of the string. */ +static inline u32 shfl_string_buf_size(const struct shfl_string *string) +{ + return string ? SHFLSTRING_HEADER_SIZE + string->size : 0; +} + +/** @} */ + +/** Set user id on execution (S_ISUID). */ +#define SHFL_UNIX_ISUID 0004000U +/** Set group id on execution (S_ISGID). */ +#define SHFL_UNIX_ISGID 0002000U +/** Sticky bit (S_ISVTX / S_ISTXT). */ +#define SHFL_UNIX_ISTXT 0001000U + +/** Owner readable (S_IRUSR). */ +#define SHFL_UNIX_IRUSR 0000400U +/** Owner writable (S_IWUSR). */ +#define SHFL_UNIX_IWUSR 0000200U +/** Owner executable (S_IXUSR). */ +#define SHFL_UNIX_IXUSR 0000100U + +/** Group readable (S_IRGRP). */ +#define SHFL_UNIX_IRGRP 0000040U +/** Group writable (S_IWGRP). */ +#define SHFL_UNIX_IWGRP 0000020U +/** Group executable (S_IXGRP). */ +#define SHFL_UNIX_IXGRP 0000010U + +/** Other readable (S_IROTH). */ +#define SHFL_UNIX_IROTH 0000004U +/** Other writable (S_IWOTH). */ +#define SHFL_UNIX_IWOTH 0000002U +/** Other executable (S_IXOTH). */ +#define SHFL_UNIX_IXOTH 0000001U + +/** Named pipe (fifo) (S_IFIFO). */ +#define SHFL_TYPE_FIFO 0010000U +/** Character device (S_IFCHR). */ +#define SHFL_TYPE_DEV_CHAR 0020000U +/** Directory (S_IFDIR). */ +#define SHFL_TYPE_DIRECTORY 0040000U +/** Block device (S_IFBLK). */ +#define SHFL_TYPE_DEV_BLOCK 0060000U +/** Regular file (S_IFREG). */ +#define SHFL_TYPE_FILE 0100000U +/** Symbolic link (S_IFLNK). */ +#define SHFL_TYPE_SYMLINK 0120000U +/** Socket (S_IFSOCK). */ +#define SHFL_TYPE_SOCKET 0140000U +/** Whiteout (S_IFWHT). */ +#define SHFL_TYPE_WHITEOUT 0160000U +/** Type mask (S_IFMT). */ +#define SHFL_TYPE_MASK 0170000U + +/** Checks the mode flags indicate a directory (S_ISDIR). */ +#define SHFL_IS_DIRECTORY(m) (((m) & SHFL_TYPE_MASK) == SHFL_TYPE_DIRECTORY) +/** Checks the mode flags indicate a symbolic link (S_ISLNK). */ +#define SHFL_IS_SYMLINK(m) (((m) & SHFL_TYPE_MASK) == SHFL_TYPE_SYMLINK) + +/** + * The available additional information in a shfl_fsobjattr object. + */ +enum shfl_fsobjattr_add { + /** No additional information is available / requested. */ + SHFLFSOBJATTRADD_NOTHING = 1, + /** + * The additional unix attributes (shfl_fsobjattr::u::unix_attr) are + * available / requested. + */ + SHFLFSOBJATTRADD_UNIX, + /** + * The additional extended attribute size (shfl_fsobjattr::u::size) is + * available / requested. + */ + SHFLFSOBJATTRADD_EASIZE, + /** + * The last valid item (inclusive). + * The valid range is SHFLFSOBJATTRADD_NOTHING thru + * SHFLFSOBJATTRADD_LAST. + */ + SHFLFSOBJATTRADD_LAST = SHFLFSOBJATTRADD_EASIZE, + + /** The usual 32-bit hack. */ + SHFLFSOBJATTRADD_32BIT_SIZE_HACK = 0x7fffffff +}; + +/** + * Additional unix Attributes, these are available when + * shfl_fsobjattr.additional == SHFLFSOBJATTRADD_UNIX. + */ +struct shfl_fsobjattr_unix { + /** + * The user owning the filesystem object (st_uid). + * This field is ~0U if not supported. + */ + u32 uid; + + /** + * The group the filesystem object is assigned (st_gid). + * This field is ~0U if not supported. + */ + u32 gid; + + /** + * Number of hard links to this filesystem object (st_nlink). + * This field is 1 if the filesystem doesn't support hardlinking or + * the information isn't available. + */ + u32 hardlinks; + + /** + * The device number of the device which this filesystem object resides + * on (st_dev). This field is 0 if this information is not available. + */ + u32 inode_id_device; + + /** + * The unique identifier (within the filesystem) of this filesystem + * object (st_ino). Together with inode_id_device, this field can be + * used as a OS wide unique id, when both their values are not 0. + * This field is 0 if the information is not available. + */ + u64 inode_id; + + /** + * User flags (st_flags). + * This field is 0 if this information is not available. + */ + u32 flags; + + /** + * The current generation number (st_gen). + * This field is 0 if this information is not available. + */ + u32 generation_id; + + /** + * The device number of a char. or block device type object (st_rdev). + * This field is 0 if the file isn't a char. or block device or when + * the OS doesn't use the major+minor device idenfication scheme. + */ + u32 device; +} __packed; + +/** Extended attribute size. */ +struct shfl_fsobjattr_easize { + s64 cb; /**< Size of EAs. */ +} __packed; + +/** Shared folder filesystem object attributes. */ +struct shfl_fsobjattr { + /** + * Mode flags (st_mode). SHFL_UNIX_*, SHFL_TYPE_*, and SHFL_DOS_*. + * @remarks We depend on a number of SHFL_ defines to remain unchanged. + * Fortuntately, these are depending on windows, dos and unix + * standard values, so this shouldn't be much of a pain. + */ + u32 mode; + + /** The additional attributes available. */ + enum shfl_fsobjattr_add additional; + + /** + * Additional attributes. + * + * Unless explicitly specified to an API, the API can provide additional + * data as it is provided by the underlying OS. + */ + union { + struct shfl_fsobjattr_unix unix_attr; + struct shfl_fsobjattr_easize size; + } __packed u; +} __packed; +VBOXSF_ASSERT_SIZE(shfl_fsobjattr, 44); + +struct shfl_timespec { + s64 ns_relative_to_unix_epoch; +}; + +/** + * Filesystem object information structure. + */ +struct shfl_fsobjinfo { + /** + * Logical size (st_size). + * For normal files this is the size of the file. + * For symbolic links, this is the length of the path name contained + * in the symbolic link. + * For other objects this fields needs to be specified. + */ + s64 size; + + /** Disk allocation size (st_blocks * DEV_BSIZE). */ + s64 allocated; + + /** + * Time of last access (st_atime). + * @remarks Here (and other places) we depend on the IPRT timespec to + * remain unchanged. + */ + struct shfl_timespec access_time; + + /** Time of last data modification (st_mtime). */ + struct shfl_timespec modification_time; + + /** + * Time of last status change (st_ctime). + * If not available this is set to modification_time. + */ + struct shfl_timespec change_time; + + /** + * Time of file birth (st_birthtime). + * If not available this is set to change_time. + */ + struct shfl_timespec birth_time; + + /** Attributes. */ + struct shfl_fsobjattr attr; + +} __packed; +VBOXSF_ASSERT_SIZE(shfl_fsobjinfo, 92); + +/** + * result of an open/create request. + * Along with handle value the result code + * identifies what has happened while + * trying to open the object. + */ +enum shfl_create_result { + SHFL_NO_RESULT, + /** Specified path does not exist. */ + SHFL_PATH_NOT_FOUND, + /** Path to file exists, but the last component does not. */ + SHFL_FILE_NOT_FOUND, + /** File already exists and either has been opened or not. */ + SHFL_FILE_EXISTS, + /** New file was created. */ + SHFL_FILE_CREATED, + /** Existing file was replaced or overwritten. */ + SHFL_FILE_REPLACED +}; + +/** + * Open/create flags. + * @{ + */ + +/** No flags. Initialization value. */ +#define SHFL_CF_NONE (0x00000000) + +/** + * Only lookup the object, do not return a handle. When this is set all other + * flags are ignored. + */ +#define SHFL_CF_LOOKUP (0x00000001) + +/** + * Open parent directory of specified object. + * Useful for the corresponding Windows FSD flag + * and for opening paths like \\dir\\*.* to search the 'dir'. + * @todo possibly not needed??? + */ +#define SHFL_CF_OPEN_TARGET_DIRECTORY (0x00000002) + +/** Create/open a directory. */ +#define SHFL_CF_DIRECTORY (0x00000004) + +/** + * Open/create action to do if object exists + * and if the object does not exists. + * REPLACE file means atomically DELETE and CREATE. + * OVERWRITE file means truncating the file to 0 and + * setting new size. + * When opening an existing directory REPLACE and OVERWRITE + * actions are considered invalid, and cause returning + * FILE_EXISTS with NIL handle. + */ +#define SHFL_CF_ACT_MASK_IF_EXISTS (0x000000f0) +#define SHFL_CF_ACT_MASK_IF_NEW (0x00000f00) + +/** What to do if object exists. */ +#define SHFL_CF_ACT_OPEN_IF_EXISTS (0x00000000) +#define SHFL_CF_ACT_FAIL_IF_EXISTS (0x00000010) +#define SHFL_CF_ACT_REPLACE_IF_EXISTS (0x00000020) +#define SHFL_CF_ACT_OVERWRITE_IF_EXISTS (0x00000030) + +/** What to do if object does not exist. */ +#define SHFL_CF_ACT_CREATE_IF_NEW (0x00000000) +#define SHFL_CF_ACT_FAIL_IF_NEW (0x00000100) + +/** Read/write requested access for the object. */ +#define SHFL_CF_ACCESS_MASK_RW (0x00003000) + +/** No access requested. */ +#define SHFL_CF_ACCESS_NONE (0x00000000) +/** Read access requested. */ +#define SHFL_CF_ACCESS_READ (0x00001000) +/** Write access requested. */ +#define SHFL_CF_ACCESS_WRITE (0x00002000) +/** Read/Write access requested. */ +#define SHFL_CF_ACCESS_READWRITE (0x00003000) + +/** Requested share access for the object. */ +#define SHFL_CF_ACCESS_MASK_DENY (0x0000c000) + +/** Allow any access. */ +#define SHFL_CF_ACCESS_DENYNONE (0x00000000) +/** Do not allow read. */ +#define SHFL_CF_ACCESS_DENYREAD (0x00004000) +/** Do not allow write. */ +#define SHFL_CF_ACCESS_DENYWRITE (0x00008000) +/** Do not allow access. */ +#define SHFL_CF_ACCESS_DENYALL (0x0000c000) + +/** Requested access to attributes of the object. */ +#define SHFL_CF_ACCESS_MASK_ATTR (0x00030000) + +/** No access requested. */ +#define SHFL_CF_ACCESS_ATTR_NONE (0x00000000) +/** Read access requested. */ +#define SHFL_CF_ACCESS_ATTR_READ (0x00010000) +/** Write access requested. */ +#define SHFL_CF_ACCESS_ATTR_WRITE (0x00020000) +/** Read/Write access requested. */ +#define SHFL_CF_ACCESS_ATTR_READWRITE (0x00030000) + +/** + * The file is opened in append mode. + * Ignored if SHFL_CF_ACCESS_WRITE is not set. + */ +#define SHFL_CF_ACCESS_APPEND (0x00040000) + +/** @} */ + +struct shfl_createparms { + /* Returned handle of opened object. */ + SHFLHANDLE handle; + + /* Returned result of the operation */ + enum shfl_create_result result; + + /* SHFL_CF_* */ + u32 create_flags; + + /* + * Attributes of object to create and + * returned actual attributes of opened/created object. + */ + struct shfl_fsobjinfo info; +} __packed; + +/** + * Shared Folders mappings. + * @{ + */ + +/** The mapping has been added since last query. */ +#define SHFL_MS_NEW (1) +/** The mapping has been deleted since last query. */ +#define SHFL_MS_DELETED (2) + +struct shfl_mapping { + /** Mapping status. */ + u32 status; + /** Root handle. */ + SHFLROOT root; +}; + +/** @} */ + +/** + * Shared Folder directory information + * @{ + */ + +struct shfl_dirinfo { + /** Full information about the object. */ + struct shfl_fsobjinfo info; + /** + * The length of the short field (number of UTF16 chars). + * It is 16-bit for reasons of alignment. + */ + u16 short_name_len; + /** + * The short name for 8.3 compatibility. + * Empty string if not available. + */ + u16 short_name[14]; + /** @todo malc, a description, please. */ + struct shfl_string name; +}; + +/** Shared folder filesystem properties. */ +struct shfl_fsproperties { + /** + * The maximum size of a filesystem object name. + * This does not include the '\\0'. + */ + u32 max_component_len; + + /** + * True if the filesystem is remote. + * False if the filesystem is local. + */ + bool remote; + + /** + * True if the filesystem is case sensitive. + * False if the filesystem is case insensitive. + */ + bool case_sensitive; + + /** + * True if the filesystem is mounted read only. + * False if the filesystem is mounted read write. + */ + bool read_only; + + /** + * True if the filesystem can encode unicode object names. + * False if it can't. + */ + bool supports_unicode; + + /** + * True if the filesystem is compresses. + * False if it isn't or we don't know. + */ + bool compressed; + + /** + * True if the filesystem compresses of individual files. + * False if it doesn't or we don't know. + */ + bool file_compression; + + /** @todo more? */ +}; +VBOXSF_ASSERT_SIZE(shfl_fsproperties, 12); + +struct shfl_volinfo { + s64 total_allocation_bytes; + s64 available_allocation_bytes; + u32 bytes_per_allocation_unit; + u32 bytes_per_sector; + u32 serial; + struct shfl_fsproperties properties; +}; + +/** @} */ + +/** + * Function parameter structures. + * @{ + */ + +/** SHFL_FN_QUERY_MAPPINGS */ + +/** + * Validation mask. Needs to be adjusted + * whenever a new SHFL_MF_ flag is added. + */ +#define SHFL_MF_MASK (0x00000011) +/** UC2 enconded strings. */ +#define SHFL_MF_UCS2 (0x00000000) +/** Guest uses UTF8 strings, if not set then the strings are unicode (UCS2). */ +#define SHFL_MF_UTF8 (0x00000001) +/** Just handle the auto-mounted folders. */ +#define SHFL_MF_AUTOMOUNT (0x00000010) + +/** Type of guest system. For future system dependent features. */ +#define SHFL_MF_SYSTEM_MASK (0x0000ff00) +#define SHFL_MF_SYSTEM_NONE (0x00000000) +#define SHFL_MF_SYSTEM_WINDOWS (0x00000100) +#define SHFL_MF_SYSTEM_LINUX (0x00000200) + +/** Parameters structure. */ +struct shfl_query_mappings { + VBoxGuestHGCMCallInfo call_info; + + /** + * 32bit, in: + * Flags describing various client needs. + */ + HGCMFunctionParameter flags; + + /** + * 32bit, in/out: + * Number of mappings the client expects. + * This is the number of elements in the + * mappings array. + */ + HGCMFunctionParameter number_of_mappings; + + /** + * pointer, in/out: + * Points to array of struct shfl_mapping structures. + */ + HGCMFunctionParameter mappings; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_QUERY_MAPPINGS (3) + +/** + * SHFL_FN_QUERY_MAP_NAME + */ + +/** Parameters structure. */ +struct shfl_query_map_name { + VBoxGuestHGCMCallInfo call_info; + + /** + * 32bit, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in/out: + * Points to struct shfl_string buffer. + */ + HGCMFunctionParameter name; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_QUERY_MAP_NAME (2) + +/** + * SHFL_FN_MAP_FOLDER + */ + +/** Parameters structure. */ +struct shfl_map_folder { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: + * Points to struct shfl_string buffer. + */ + HGCMFunctionParameter path; + + /** + * pointer, out: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: UTF16 + * Path delimiter + */ + HGCMFunctionParameter delimiter; + + /** + * pointer, in: SHFLROOT + * Case senstive flag + */ + HGCMFunctionParameter case_sensitive; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_MAP_FOLDER (4) + +/** + * SHFL_FN_UNMAP_FOLDER + */ + +/** Parameters structure. */ +struct shfl_unmap_folder { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_UNMAP_FOLDER (1) + +/** + * SHFL_FN_CREATE + */ + +/** Parameters structure. */ +struct shfl_create { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to struct shfl_string buffer. + */ + HGCMFunctionParameter path; + + /** + * pointer, in/out: + * Points to SHFLCREATEPARMS buffer. + */ + HGCMFunctionParameter parms; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_CREATE (3) + +/** + * SHFL_FN_CLOSE + */ + +/** Parameters structure. */ +struct shfl_close { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to close. + */ + HGCMFunctionParameter handle; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_CLOSE (2) + +/** + * SHFL_FN_READ + */ + +/** Parameters structure. */ +struct shfl_read { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to read from. + */ + HGCMFunctionParameter handle; + + /** + * value64, in: + * Offset to read from. + */ + HGCMFunctionParameter offset; + + /** + * value64, in/out: + * Bytes to read/How many were read. + */ + HGCMFunctionParameter cb; + + /** + * pointer, out: + * Buffer to place data to. + */ + HGCMFunctionParameter buffer; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_READ (5) + +/** + * SHFL_FN_WRITE + */ + +/** Parameters structure. */ +struct shfl_write { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to write to. + */ + HGCMFunctionParameter handle; + + /** + * value64, in: + * Offset to write to. + */ + HGCMFunctionParameter offset; + + /** + * value64, in/out: + * Bytes to write/How many were written. + */ + HGCMFunctionParameter cb; + + /** + * pointer, in: + * Data to write. + */ + HGCMFunctionParameter buffer; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_WRITE (5) + +/** + * SHFL_FN_LOCK + */ + +/** Lock owner is the HGCM client. */ + +/** Lock mode bit mask. */ +#define SHFL_LOCK_MODE_MASK (0x3) +/** Cancel lock on the given range. */ +#define SHFL_LOCK_CANCEL (0x0) +/** Acquire read only lock. Prevent write to the range. */ +#define SHFL_LOCK_SHARED (0x1) +/** Acquire write lock. Prevent both write and read to the range. */ +#define SHFL_LOCK_EXCLUSIVE (0x2) + +/** Do not wait for lock if it can not be acquired at the time. */ +#define SHFL_LOCK_NOWAIT (0x0) +/** Wait and acquire lock. */ +#define SHFL_LOCK_WAIT (0x4) + +/** Lock the specified range. */ +#define SHFL_LOCK_PARTIAL (0x0) +/** Lock entire object. */ +#define SHFL_LOCK_ENTIRE (0x8) + +/** Parameters structure. */ +struct shfl_lock { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to be locked. + */ + HGCMFunctionParameter handle; + + /** + * value64, in: + * Starting offset of lock range. + */ + HGCMFunctionParameter offset; + + /** + * value64, in: + * Length of range. + */ + HGCMFunctionParameter length; + + /** + * value32, in: + * Lock flags SHFL_LOCK_*. + */ + HGCMFunctionParameter flags; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_LOCK (5) + +/** + * SHFL_FN_FLUSH + */ + +/** Parameters structure. */ +struct shfl_flush { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to be locked. + */ + HGCMFunctionParameter handle; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_FLUSH (2) + +/** + * SHFL_FN_LIST + */ + +/** Listing information includes variable length RTDIRENTRY[EX] structures. */ + +/** @todo might be necessary for future. */ +#define SHFL_LIST_NONE 0 +#define SHFL_LIST_RETURN_ONE 1 + +/** Parameters structure. */ +struct shfl_list { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to be listed. + */ + HGCMFunctionParameter handle; + + /** + * value32, in: + * List flags SHFL_LIST_*. + */ + HGCMFunctionParameter flags; + + /** + * value32, in/out: + * Bytes to be used for listing information/How many bytes were used. + */ + HGCMFunctionParameter cb; + + /** + * pointer, in/optional + * Points to struct shfl_string buffer that specifies a search path. + */ + HGCMFunctionParameter path; + + /** + * pointer, out: + * Buffer to place listing information to. (struct shfl_dirinfo) + */ + HGCMFunctionParameter buffer; + + /** + * value32, in/out: + * Indicates a key where the listing must be resumed. + * in: 0 means start from begin of object. + * out: 0 means listing completed. + */ + HGCMFunctionParameter resume_point; + + /** + * pointer, out: + * Number of files returned + */ + HGCMFunctionParameter file_count; +}; + +/** Number of parameters */ +#define SHFL_CPARMS_LIST (8) + +/** + * SHFL_FN_READLINK + */ + +/** Parameters structure. */ +struct shfl_readLink { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to struct shfl_string buffer. + */ + HGCMFunctionParameter path; + + /** + * pointer, out: + * Buffer to place data to. + */ + HGCMFunctionParameter buffer; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_READLINK (3) + +/** + * SHFL_FN_INFORMATION + */ + +/** Mask of Set/Get bit. */ +#define SHFL_INFO_MODE_MASK (0x1) +/** Get information */ +#define SHFL_INFO_GET (0x0) +/** Set information */ +#define SHFL_INFO_SET (0x1) + +/** Get name of the object. */ +#define SHFL_INFO_NAME (0x2) +/** Set size of object (extend/trucate); only applies to file objects */ +#define SHFL_INFO_SIZE (0x4) +/** Get/Set file object info. */ +#define SHFL_INFO_FILE (0x8) +/** Get volume information. */ +#define SHFL_INFO_VOLUME (0x10) + +/** @todo different file info structures */ + +/** Parameters structure. */ +struct shfl_information { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to be listed. + */ + HGCMFunctionParameter handle; + + /** + * value32, in: + * SHFL_INFO_* + */ + HGCMFunctionParameter flags; + + /** + * value32, in/out: + * Bytes to be used for information/How many bytes were used. + */ + HGCMFunctionParameter cb; + + /** + * pointer, in/out: + * Information to be set/get (shfl_fsobjinfo or shfl_string). Do not + * forget to set the shfl_fsobjinfo::attr::additional for a get + * operation as well. + */ + HGCMFunctionParameter info; + +}; + +/** Number of parameters */ +#define SHFL_CPARMS_INFORMATION (5) + +/** + * SHFL_FN_REMOVE + */ + +#define SHFL_REMOVE_FILE (0x1) +#define SHFL_REMOVE_DIR (0x2) +#define SHFL_REMOVE_SYMLINK (0x4) + +/** Parameters structure. */ +struct shfl_remove { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to struct shfl_string buffer. + */ + HGCMFunctionParameter path; + + /** + * value32, in: + * remove flags (file/directory) + */ + HGCMFunctionParameter flags; + +}; + +#define SHFL_CPARMS_REMOVE (3) + +/** + * SHFL_FN_RENAME + */ + +#define SHFL_RENAME_FILE (0x1) +#define SHFL_RENAME_DIR (0x2) +#define SHFL_RENAME_REPLACE_IF_EXISTS (0x4) + +/** Parameters structure. */ +struct shfl_rename { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to struct shfl_string src. + */ + HGCMFunctionParameter src; + + /** + * pointer, in: + * Points to struct shfl_string dest. + */ + HGCMFunctionParameter dest; + + /** + * value32, in: + * rename flags (file/directory) + */ + HGCMFunctionParameter flags; + +}; + +#define SHFL_CPARMS_RENAME (4) + +/** + * SHFL_FN_SYMLINK + */ + +/** Parameters structure. */ +struct shfl_symlink { + VBoxGuestHGCMCallInfo call_info; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to struct shfl_string of path for the new symlink. + */ + HGCMFunctionParameter new_path; + + /** + * pointer, in: + * Points to struct shfl_string of destination for symlink. + */ + HGCMFunctionParameter old_path; + + /** + * pointer, out: + * Information about created symlink. + */ + HGCMFunctionParameter info; + +}; + +#define SHFL_CPARMS_SYMLINK (4) + +/** + * SHFL_FN_ADD_MAPPING + * Host call, no guest structure is used. + */ + +/** mapping is writable */ +#define SHFL_ADD_MAPPING_F_WRITABLE BIT(0) +/** mapping is automounted by the guest */ +#define SHFL_ADD_MAPPING_F_AUTOMOUNT BIT(1) +/** allow the guest to create symlinks */ +#define SHFL_ADD_MAPPING_F_CREATE_SYMLINKS BIT(2) +/** mapping is actually missing on the host */ +#define SHFL_ADD_MAPPING_F_MISSING BIT(3) + +#define SHFL_CPARMS_ADD_MAPPING (3) + +/** + * SHFL_FN_REMOVE_MAPPING + * Host call, no guest structure is used. + */ + +#define SHFL_CPARMS_REMOVE_MAPPING (1) + +/** + * SHFL_FN_SET_STATUS_LED + * Host call, no guest structure is used. + */ + +#define SHFL_CPARMS_SET_STATUS_LED (1) + +/** @} */ + +#endif diff --git a/fs/vboxsf/utils.c b/fs/vboxsf/utils.c new file mode 100644 index 000000000000..861afb421b5e --- /dev/null +++ b/fs/vboxsf/utils.c @@ -0,0 +1,674 @@ +/* + * VirtualBox Guest Shared Folders support: Utility functions. + * Mainly conversion from/to VirtualBox/Linux data structures. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "vfsmod.h" + +/* + * 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 + */ + +static void sf_timespec_from_vbox(struct timespec *tv, + const struct shfl_timespec *ts) +{ + s64 nsec, t = ts->ns_relative_to_unix_epoch; + + nsec = do_div(t, 1000000000); + tv->tv_sec = t; + tv->tv_nsec = nsec; +} + +static void sf_timespec_to_vbox(struct shfl_timespec *ts, + const struct timespec *tv) +{ + s64 t = (s64) tv->tv_nsec + (s64) tv->tv_sec * 1000000000; + + ts->ns_relative_to_unix_epoch = t; +} + +/* 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, + const struct shfl_fsobjinfo *info) +{ + const struct shfl_fsobjattr *attr; + s64 allocated; + int mode; + + attr = &info->attr; + +#define mode_set(r) ((attr->mode & (SHFL_UNIX_##r)) ? (S_##r) : 0) + + mode = mode_set(ISUID); + mode |= mode_set(ISGID); + + 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 + + inode->i_mapping->a_ops = &sf_reg_aops; + + if (SHFL_IS_DIRECTORY(attr->mode)) { + 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 (. ..) + */ + set_nlink(inode, 1); + } else if (SHFL_IS_SYMLINK(attr->mode)) { + 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; + set_nlink(inode, 1); + } 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; + set_nlink(inode, 1); + } + + inode->i_uid = make_kuid(current_user_ns(), sf_g->uid); + inode->i_gid = make_kgid(current_user_ns(), sf_g->gid); + + inode->i_size = info->size; + inode->i_blkbits = 12; + /* i_blocks always in units of 512 bytes! */ + allocated = info->allocated + 511; + do_div(allocated, 512); + inode->i_blocks = allocated; + + sf_timespec_from_vbox(&inode->i_atime, &info->access_time); + sf_timespec_from_vbox(&inode->i_ctime, &info->change_time); + sf_timespec_from_vbox(&inode->i_mtime, &info->modification_time); +} + +int sf_stat(const char *caller, struct sf_glob_info *sf_g, + struct shfl_string *path, struct shfl_fsobjinfo *result, + int ok_to_fail) +{ + struct shfl_createparms params = {}; + int rc; + + params.handle = SHFL_HANDLE_NIL; + params.create_flags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW; + rc = vboxsf_create(sf_g->root, path, ¶ms); + if (rc == VERR_INVALID_NAME) { + /* this can happen for names like 'foo*' on a Windows host */ + return -ENOENT; + } + if (rc < 0) + return -EPROTO; + + if (params.result != SHFL_FILE_EXISTS) + return -ENOENT; + + *result = params.info; + return 0; +} + +/* + * This is called indirectly as iop through [sf_getattr]. 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; + struct shfl_fsobjinfo info; + + if (!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 (!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 sf_dentry_revalidate(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + if (sf_inode_revalidate(dentry)) + return 0; + + return 1; +} + +int sf_getattr(const struct path *path, struct kstat *kstat, u32 request_mask, + unsigned int flags) +{ + int err; + struct dentry *dentry = path->dentry; + + 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; + struct shfl_createparms params = {}; + struct shfl_fsobjinfo info = {}; + uint32_t buf_len; + int rc, err; + + sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb); + sf_i = GET_INODE_INFO(dentry->d_inode); + err = 0; + + params.handle = SHFL_HANDLE_NIL; + params.create_flags = 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.create_flags |= SHFL_CF_ACCESS_WRITE; + + rc = vboxsf_create(sf_g->root, sf_i->path, ¶ms); + if (rc < 0) { + err = vbg_status_code_to_errno(rc); + goto fail2; + } + if (params.result != SHFL_FILE_EXISTS) { + err = -ENOENT; + goto fail1; + } +#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? SHFL_UNIX_##r : 0) + + /* + * Setting the file size and setting the other attributes has to + * be handled separately. + */ + if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) { + if (iattr->ia_valid & ATTR_MODE) { + info.attr.mode = mode_set(ISUID); + info.attr.mode |= mode_set(ISGID); + info.attr.mode |= mode_set(IRUSR); + info.attr.mode |= mode_set(IWUSR); + info.attr.mode |= mode_set(IXUSR); + info.attr.mode |= mode_set(IRGRP); + info.attr.mode |= mode_set(IWGRP); + info.attr.mode |= mode_set(IXGRP); + info.attr.mode |= mode_set(IROTH); + info.attr.mode |= mode_set(IWOTH); + info.attr.mode |= mode_set(IXOTH); + + if (iattr->ia_mode & S_IFDIR) + info.attr.mode |= SHFL_TYPE_DIRECTORY; + else + info.attr.mode |= SHFL_TYPE_FILE; + } + + if (iattr->ia_valid & ATTR_ATIME) + sf_timespec_to_vbox(&info.access_time, + &iattr->ia_atime); + + if (iattr->ia_valid & ATTR_MTIME) + sf_timespec_to_vbox(&info.modification_time, + &iattr->ia_mtime); + + /* + * Ignore ctime (inode change time) as it can't be set + * from userland anyway. + */ + + buf_len = sizeof(info); + rc = vboxsf_fsinfo(sf_g->root, params.handle, + SHFL_INFO_SET | SHFL_INFO_FILE, &buf_len, + &info); + if (rc < 0) { + err = vbg_status_code_to_errno(rc); + goto fail1; + } + } +#undef mode_set + + if (iattr->ia_valid & ATTR_SIZE) { + memset(&info, 0, sizeof(info)); + info.size = iattr->ia_size; + buf_len = sizeof(info); + rc = vboxsf_fsinfo(sf_g->root, params.handle, + SHFL_INFO_SET | SHFL_INFO_SIZE, &buf_len, + &info); + if (rc < 0) { + err = vbg_status_code_to_errno(rc); + goto fail1; + } + } + + vboxsf_close(sf_g->root, params.handle); + + return sf_inode_revalidate(dentry); + +fail1: + vboxsf_close(sf_g->root, params.handle); +fail2: + return err; +} + +static int sf_make_path(const char *caller, struct sf_inode_info *sf_i, + const char *d_name, size_t d_len, + struct shfl_string **result) +{ + size_t path_len, shflstring_len; + struct shfl_string *tmp; + uint16_t p_len; + uint8_t *p_name; + int fRoot = 0; + + p_len = sf_i->path->length; + 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) + return -ENAMETOOLONG; + } + + shflstring_len = offsetof(struct shfl_string, string.utf8) + path_len; + tmp = kmalloc(shflstring_len, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + tmp->length = path_len - 1; + tmp->size = 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 struct shfl_string 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, + struct shfl_string **result) +{ + int err; + const char *d_name; + size_t d_len; + const char *name; + size_t len = 0; + + 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) { + wchar_t uni; + int nb; + + nb = sf_g->nls->char2uni(in, in_len, &uni); + if (nb < 0) { + err = -EINVAL; + goto fail1; + } + in_len -= nb; + in += nb; + + nb = utf32_to_utf8(uni, out, out_bound_len); + if (nb < 0) { + err = -EINVAL; + goto fail1; + } + out_bound_len -= nb; + out += nb; + len += nb; + } + if (len >= PATH_MAX - 1) { + err = -ENAMETOOLONG; + goto fail1; + } + + *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; + unicode_t uni; + + nb = utf8_to_utf32(in, in_bound_len, &uni); + if (nb < 0) + return -EINVAL; + + in += nb; + in_bound_len -= nb; + + nb = sf_g->nls->uni2char(uni, out, out_bound_len); + if (nb < 0) + 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; + + b = kmalloc(sizeof(*b), GFP_KERNEL); + if (!b) + return NULL; + + b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL); + if (!b->buf) { + kfree(b); + return NULL; + } + + INIT_LIST_HEAD(&b->head); + b->entries = 0; + b->used = 0; + b->free = DIR_BUFFER_SIZE; + + return b; +} + +static void sf_dir_buf_free(struct sf_dir_buf *b) +{ + list_del(&b->head); + kfree(b->buf); + kfree(b); +} + +/** + * Free the directory buffer. + */ +void sf_dir_info_free(struct sf_dir_info *p) +{ + struct list_head *list, *pos, *tmp; + + 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; + struct sf_dir_buf *b; + + list = &p->info_list; + list_for_each_safe(pos, tmp, list) { + b = list_entry(pos, struct sf_dir_buf, head); + b->entries = 0; + b->used = 0; + b->free = DIR_BUFFER_SIZE; + } +} + +/** + * Create a new directory buffer descriptor. + */ +struct sf_dir_info *sf_dir_info_alloc(void) +{ + struct sf_dir_info *p; + + p = kmalloc(sizeof(*p), GFP_KERNEL); + if (!p) + 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; + + if (b->used == 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; + struct shfl_string *mask; + struct sf_dir_buf *b; + + err = sf_make_path(__func__, sf_i, "*", 1, &mask); + if (err) + goto fail0; + + for (;;) { + int rc; + void *buf; + u32 entries, size; + + b = sf_get_empty_dir_buf(sf_d); + if (!b) { + b = sf_dir_buf_alloc(); + if (!b) { + err = -ENOMEM; + goto fail1; + } + list_add(&b->head, &sf_d->info_list); + } + + buf = b->buf; + size = b->free; + + rc = vboxsf_dirinfo(sf_g->root, handle, mask, 0, 0, + &size, buf, &entries); + switch (rc) { + case VINF_SUCCESS: + /* fallthrough */ + case VERR_NO_MORE_FILES: + break; + case VERR_NO_TRANSLATION: + /* XXX */ + break; + default: + err = vbg_status_code_to_errno(rc); + goto fail1; + } + + b->entries += entries; + b->free -= size; + b->used += size; + + if (rc < 0) + break; + } + err = 0; + +fail1: + kfree(mask); + +fail0: + return err; +} + +int sf_get_volume_info(struct super_block *sb, struct kstatfs *stat) +{ + struct sf_glob_info *sf_g; + struct shfl_volinfo SHFLVolumeInfo; + u32 buf_len; + int rc; + + sf_g = GET_GLOB_INFO(sb); + buf_len = sizeof(SHFLVolumeInfo); + rc = vboxsf_fsinfo(sf_g->root, 0, + SHFL_INFO_GET | SHFL_INFO_VOLUME, &buf_len, + &SHFLVolumeInfo); + if (rc < 0) + return vbg_status_code_to_errno(rc); + + stat->f_type = NFS_SUPER_MAGIC; /* XXX vboxsf type? */ + stat->f_bsize = SHFLVolumeInfo.bytes_per_allocation_unit; + + do_div(SHFLVolumeInfo.total_allocation_bytes, + SHFLVolumeInfo.bytes_per_allocation_unit); + stat->f_blocks = SHFLVolumeInfo.total_allocation_bytes; + + do_div(SHFLVolumeInfo.available_allocation_bytes, + SHFLVolumeInfo.bytes_per_allocation_unit); + stat->f_bfree = SHFLVolumeInfo.available_allocation_bytes; + stat->f_bavail = SHFLVolumeInfo.available_allocation_bytes; + + stat->f_files = 1000; + /* + * Don't return 0 here since the guest may then think that it is not + * possible to create any more files. + */ + stat->f_ffree = 1000; + stat->f_fsid.val[0] = 0; + stat->f_fsid.val[1] = 0; + stat->f_namelen = 255; + return 0; +} + +const struct dentry_operations sf_dentry_ops = { + .d_revalidate = sf_dentry_revalidate +}; diff --git a/fs/vboxsf/vboxsf_wrappers.c b/fs/vboxsf/vboxsf_wrappers.c new file mode 100644 index 000000000000..64ee30894ddf --- /dev/null +++ b/fs/vboxsf/vboxsf_wrappers.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * Wrapper functions for the shfl host calls. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#include +#include +#include +#include +#include "vboxsf_wrappers.h" + +#define VBOX_INIT_CALL(a, b) \ +do { \ + (a)->result = VINF_SUCCESS; \ + (a)->u32ClientID = vboxsf_client_id; \ + (a)->u32Function = SHFL_FN_##b; \ + (a)->cParms = SHFL_CPARMS_##b; \ +} while (0) + +/* globals */ +static u32 vboxsf_client_id; + +int vboxsf_connect(void) +{ + struct vbg_dev *gdev; + HGCMServiceLocation loc; + int rc; + + loc.type = VMMDevHGCMLoc_LocalHost_Existing; + strcpy(loc.u.host.achName, "VBoxSharedFolders"); + + gdev = vbg_get_gdev(); + if (IS_ERR(gdev)) + return VERR_NOT_SUPPORTED; /* No guest-device */ + + rc = vbg_hgcm_connect(gdev, &loc, &vboxsf_client_id); + vbg_put_gdev(gdev); + + return rc; +} + +void vboxsf_disconnect(void) +{ + struct vbg_dev *gdev; + + gdev = vbg_get_gdev(); + if (IS_ERR(gdev)) + return; /* guest-device is gone, already disconnected */ + + vbg_hgcm_disconnect(gdev, vboxsf_client_id); + vbg_put_gdev(gdev); +} + +static int vboxsf_hgcm_call(void *data, u32 len) +{ + VBoxGuestHGCMCallInfo *info = data; + struct vbg_dev *gdev; + int rc; + + gdev = vbg_get_gdev(); + if (IS_ERR(gdev)) + return VERR_DEV_IO_ERROR; /* guest-dev removed underneath us */ + + rc = vbg_hgcm_call(gdev, info, len, U32_MAX, false); + vbg_put_gdev(gdev); + + if (rc >= 0) + rc = info->result; + + return rc; +} + +int vboxsf_query_mappings(struct shfl_mapping mappings[], u32 *mappings_len) +{ + int rc; + struct shfl_query_mappings data; + + VBOX_INIT_CALL(&data.call_info, QUERY_MAPPINGS); + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = SHFL_MF_UCS2; + + data.number_of_mappings.type = VMMDevHGCMParmType_32bit; + data.number_of_mappings.u.value32 = *mappings_len; + + data.mappings.type = VMMDevHGCMParmType_LinAddr; + data.mappings.u.Pointer.size = sizeof(struct shfl_mapping) * + *mappings_len; + data.mappings.u.Pointer.u.linearAddr = (uintptr_t)&mappings[0]; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *mappings_len = data.number_of_mappings.u.value32; + + return rc; +} + +int vboxsf_query_mapname(SHFLROOT root, struct shfl_string *string, u32 size) +{ + struct shfl_query_map_name data; + + VBOX_INIT_CALL(&data.call_info, QUERY_MAP_NAME); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.name.type = VMMDevHGCMParmType_LinAddr; + data.name.u.Pointer.size = size; + data.name.u.Pointer.u.linearAddr = (uintptr_t)string; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_map_folder(struct shfl_string *folder_name, SHFLROOT *root) +{ + int rc; + struct shfl_map_folder data; + + VBOX_INIT_CALL(&data.call_info, MAP_FOLDER); + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = shfl_string_buf_size(folder_name); + data.path.u.Pointer.u.linearAddr = (uintptr_t)folder_name; + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = 0; + + data.delimiter.type = VMMDevHGCMParmType_32bit; + data.delimiter.u.value32 = '/'; + + data.case_sensitive.type = VMMDevHGCMParmType_32bit; + data.case_sensitive.u.value32 = 1; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *root = data.root.u.value32; + else if (rc == VERR_NOT_IMPLEMENTED) + vbg_err("%s: Error host is too old\n", __func__); + + return rc; +} + +int vboxsf_unmap_folder(SHFLROOT root) +{ + struct shfl_unmap_folder data; + + VBOX_INIT_CALL(&data.call_info, UNMAP_FOLDER); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_create(SHFLROOT root, struct shfl_string *parsed_path, + struct shfl_createparms *create_parms) +{ + /** @todo copy buffers to physical or mapped memory. */ + struct shfl_create data; + + VBOX_INIT_CALL(&data.call_info, CREATE); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = shfl_string_buf_size(parsed_path); + data.path.u.Pointer.u.linearAddr = (uintptr_t)parsed_path; + + data.parms.type = VMMDevHGCMParmType_LinAddr; + data.parms.u.Pointer.size = sizeof(struct shfl_createparms); + data.parms.u.Pointer.u.linearAddr = (uintptr_t)create_parms; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_close(SHFLROOT root, SHFLHANDLE file) +{ + struct shfl_close data; + + VBOX_INIT_CALL(&data.call_info, CLOSE); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_remove(SHFLROOT root, struct shfl_string *parsed_path, u32 flags) +{ + struct shfl_remove data; + + VBOX_INIT_CALL(&data.call_info, REMOVE); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = shfl_string_buf_size(parsed_path); + data.path.u.Pointer.u.linearAddr = (uintptr_t)parsed_path; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_rename(SHFLROOT root, struct shfl_string *src_path, + struct shfl_string *dest_path, u32 flags) +{ + struct shfl_rename data; + + VBOX_INIT_CALL(&data.call_info, RENAME); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.src.type = VMMDevHGCMParmType_LinAddr_In; + data.src.u.Pointer.size = shfl_string_buf_size(src_path); + data.src.u.Pointer.u.linearAddr = (uintptr_t)src_path; + + data.dest.type = VMMDevHGCMParmType_LinAddr_In; + data.dest.u.Pointer.size = shfl_string_buf_size(dest_path); + data.dest.u.Pointer.u.linearAddr = (uintptr_t)dest_path; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_read(SHFLROOT root, SHFLHANDLE file, u64 offset, + u32 *buf_len, u8 *buf) +{ + int rc; + struct shfl_read data; + + VBOX_INIT_CALL(&data.call_info, READ); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *buf_len; + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *buf_len; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)buf; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *buf_len = data.cb.u.value32; + + return rc; +} + +int vboxsf_write(SHFLROOT root, SHFLHANDLE file, u64 offset, + u32 *buf_len, u8 *buf) +{ + int rc; + struct shfl_write data; + + VBOX_INIT_CALL(&data.call_info, WRITE); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *buf_len; + data.buffer.type = VMMDevHGCMParmType_LinAddr_In; + data.buffer.u.Pointer.size = *buf_len; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)buf; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *buf_len = data.cb.u.value32; + + return rc; +} + +int vboxsf_write_physcont(SHFLROOT root, SHFLHANDLE file, u64 offset, + u32 *buf_len, u64 phys_buf) +{ + HGCMPageListInfo *pg_lst; + u32 i, pages, data_len; + struct shfl_write *data; + int rc; + + pages = PAGE_ALIGN((phys_buf & ~PAGE_MASK) + *buf_len) >> PAGE_SHIFT; + data_len = sizeof(struct shfl_write) + + offsetof(HGCMPageListInfo, aPages[pages]); + + data = kmalloc(data_len, GFP_KERNEL); + if (!data) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL(&data->call_info, WRITE); + + data->root.type = VMMDevHGCMParmType_32bit; + data->root.u.value32 = root; + + data->handle.type = VMMDevHGCMParmType_64bit; + data->handle.u.value64 = file; + data->offset.type = VMMDevHGCMParmType_64bit; + data->offset.u.value64 = offset; + data->cb.type = VMMDevHGCMParmType_32bit; + data->cb.u.value32 = *buf_len; + data->buffer.type = VMMDevHGCMParmType_PageList; + data->buffer.u.PageList.size = *buf_len; + data->buffer.u.PageList.offset = sizeof(struct shfl_write); + + pg_lst = (HGCMPageListInfo *)(data + 1); + pg_lst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pg_lst->offFirstPage = (u16)(phys_buf & ~PAGE_MASK); + pg_lst->cPages = pages; + phys_buf = ALIGN_DOWN(phys_buf, PAGE_SIZE); + for (i = 0; i < pages; i++, phys_buf += PAGE_SIZE) + pg_lst->aPages[i] = phys_buf; + + rc = vboxsf_hgcm_call(data, data_len); + if (rc >= 0) + *buf_len = data->cb.u.value32; + + kfree(data); + return rc; +} + +int vboxsf_flush(SHFLROOT root, SHFLHANDLE file) +{ + struct shfl_flush data; + + VBOX_INIT_CALL(&data.call_info, FLUSH); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_dirinfo(SHFLROOT root, SHFLHANDLE file, + struct shfl_string *parsed_path, u32 flags, u32 index, + u32 *buf_len, struct shfl_dirinfo *buf, u32 *file_count) +{ + int rc; + struct shfl_list data; + + VBOX_INIT_CALL(&data.call_info, LIST); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *buf_len; + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = + parsed_path ? shfl_string_buf_size(parsed_path) : 0; + data.path.u.Pointer.u.linearAddr = (uintptr_t)parsed_path; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *buf_len; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)buf; + + data.resume_point.type = VMMDevHGCMParmType_32bit; + data.resume_point.u.value32 = index; + data.file_count.type = VMMDevHGCMParmType_32bit; + data.file_count.u.value32 = 0; /* out parameters only */ + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + + *buf_len = data.cb.u.value32; + *file_count = data.file_count.u.value32; + + return rc; +} + +int vboxsf_fsinfo(SHFLROOT root, SHFLHANDLE file, u32 flags, + u32 *buf_len, void *buf) +{ + int rc; + struct shfl_information data; + + VBOX_INIT_CALL(&data.call_info, INFORMATION); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *buf_len; + data.info.type = VMMDevHGCMParmType_LinAddr; + data.info.u.Pointer.size = *buf_len; + data.info.u.Pointer.u.linearAddr = (uintptr_t)buf; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *buf_len = data.cb.u.value32; + + return rc; +} + +int vboxsf_lock(SHFLROOT root, SHFLHANDLE file, u64 offset, + u64 size, u32 lock) +{ + struct shfl_lock data; + + VBOX_INIT_CALL(&data.call_info, LOCK); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = file; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.length.type = VMMDevHGCMParmType_64bit; + data.length.u.value64 = size; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = lock; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_set_utf8(void) +{ + VBoxGuestHGCMCallInfo info; + + VBOX_INIT_CALL(&info, SET_UTF8); + + return vboxsf_hgcm_call(&info, sizeof(info)); +} + +int vboxsf_readlink(SHFLROOT root, struct shfl_string *parsed_path, + u32 buf_len, u8 *buf) +{ + struct shfl_readLink data; + + VBOX_INIT_CALL(&data.call_info, READLINK); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = shfl_string_buf_size(parsed_path); + data.path.u.Pointer.u.linearAddr = (uintptr_t)parsed_path; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = buf_len; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)buf; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_symlink(SHFLROOT root, struct shfl_string *new_path, + struct shfl_string *old_path, struct shfl_fsobjinfo *buf) +{ + struct shfl_symlink data; + + VBOX_INIT_CALL(&data.call_info, SYMLINK); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.new_path.type = VMMDevHGCMParmType_LinAddr_In; + data.new_path.u.Pointer.size = shfl_string_buf_size(new_path); + data.new_path.u.Pointer.u.linearAddr = (uintptr_t)new_path; + + data.old_path.type = VMMDevHGCMParmType_LinAddr_In; + data.old_path.u.Pointer.size = shfl_string_buf_size(old_path); + data.old_path.u.Pointer.u.linearAddr = (uintptr_t)old_path; + + data.info.type = VMMDevHGCMParmType_LinAddr_Out; + data.info.u.Pointer.size = sizeof(struct shfl_fsobjinfo); + data.info.u.Pointer.u.linearAddr = (uintptr_t)buf; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_set_symlinks(void) +{ + VBoxGuestHGCMCallInfo info; + + VBOX_INIT_CALL(&info, SET_SYMLINKS); + + return vboxsf_hgcm_call(&info, sizeof(info)); +} diff --git a/fs/vboxsf/vboxsf_wrappers.h b/fs/vboxsf/vboxsf_wrappers.h new file mode 100644 index 000000000000..97e17a6997c6 --- /dev/null +++ b/fs/vboxsf/vboxsf_wrappers.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * Protype declarations for the wrapper functions for the shfl host calls. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef VBOXSF_WRAPPERS_H +#define VBOXSF_WRAPPERS_H + +#include /* For VBoxGuestHGCMCallInfo */ +#include "shfl_hostintf.h" + +/** + * @addtogroup grp_vboxguest_lib_r0 + * + * Note all these functions (all functions prefixed with vboxsf_) + * return a vbox status code rather then a negative errno on error. + * @{ + */ + +int vboxsf_connect(void); +void vboxsf_disconnect(void); + +int vboxsf_query_mappings(struct shfl_mapping mappings[], u32 *mappings_len); +int vboxsf_query_mapname(SHFLROOT root, struct shfl_string *string, u32 size); + +/** + * Create a new file or folder or open an existing one in a shared folder. + * Proxies to vbsfCreate in the host shared folder service. + * + * @returns VBox status code, but see note below + * @param root Root of the shared folder in which to create the file + * @param parsed_path The path of the file or folder relative to the shared + * folder + * @param create_parms Parameters for file/folder creation. See the + * structure description in shflsvc.h + * @retval create_parms See the structure description in shflsvc.h + * + * @note This function reports errors as follows. The return value is always + * VINF_SUCCESS unless an exceptional condition occurs - out of + * memory, invalid arguments, etc. If the file or folder could not be + * opened or created, create_parms->handle will be set to + * SHFL_HANDLE_NIL on return. In this case the value in + * create_parms->result provides information as to why (e.g. + * SHFL_FILE_EXISTS). create_parms->result is also set on success + * as additional information. + */ +int vboxsf_create(SHFLROOT root, struct shfl_string *parsed_path, + struct shfl_createparms *create_parms); + +int vboxsf_close(SHFLROOT root, SHFLHANDLE file); +int vboxsf_remove(SHFLROOT root, struct shfl_string *parsed_path, u32 flags); +int vboxsf_rename(SHFLROOT root, struct shfl_string *src_path, + struct shfl_string *dest_path, u32 flags); +int vboxsf_flush(SHFLROOT root, SHFLHANDLE file); + +int vboxsf_read(SHFLROOT root, SHFLHANDLE file, u64 offset, + u32 *buf_len, u8 *buf); +int vboxsf_write(SHFLROOT root, SHFLHANDLE file, u64 offset, + u32 *buf_len, u8 *buf); +int vboxsf_write_physcont(SHFLROOT root, SHFLHANDLE file, u64 offset, + u32 *buf_len, u64 phys_buf); + +int vboxsf_lock(SHFLROOT root, SHFLHANDLE file, u64 offset, + u64 size, u32 lock); + +int vboxsf_dirinfo(SHFLROOT root, SHFLHANDLE file, + struct shfl_string *parsed_path, u32 flags, u32 index, + u32 *buf_len, struct shfl_dirinfo *buf, u32 *file_count); +int vboxsf_fsinfo(SHFLROOT root, SHFLHANDLE file, u32 flags, + u32 *buf_len, void *buf); + +int vboxsf_map_folder(struct shfl_string *folder_name, SHFLROOT *root); +int vboxsf_unmap_folder(SHFLROOT root); + +int vboxsf_readlink(SHFLROOT root, struct shfl_string *parsed_path, + u32 buf_len, u8 *buf); +int vboxsf_symlink(SHFLROOT root, struct shfl_string *new_path, + struct shfl_string *old_path, struct shfl_fsobjinfo *buf); + +int vboxsf_set_utf8(void); +int vboxsf_set_symlinks(void); + +/** @} */ + +#endif diff --git a/fs/vboxsf/vfsmod.c b/fs/vboxsf/vfsmod.c new file mode 100644 index 000000000000..ee861e4c0375 --- /dev/null +++ b/fs/vboxsf/vfsmod.c @@ -0,0 +1,386 @@ +/* + * VirtualBox Guest Shared Folders support: Virtual File System. + * + * Module initialization/finalization + * File system registration/deregistration + * Superblock reading + * Few utility functions + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/** + * @note Anyone wishing to make changes here might wish to take a look at + * http://www.atnf.csiro.au/people/rgooch/linux/vfs.txt + * which seems to be the closest there is to official documentation on + * writing filesystem drivers for Linux. + */ + +#include +#include +#include +#include +#include "vfsmod.h" + +MODULE_DESCRIPTION("Oracle VM VirtualBox Module for Host File System Access"); +MODULE_AUTHOR("Oracle Corporation"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_FS("vboxsf"); + +/* 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; + size_t name_len, str_len; + struct sf_glob_info *sf_g; + struct shfl_string *str_name = NULL; +#ifdef CONFIG_NLS_DEFAULT + const char *nls_name = CONFIG_NLS_DEFAULT; +#else + const char *nls_name = ""; +#endif + + 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) + return -EINVAL; + + sf_g = kzalloc(sizeof(*sf_g), GFP_KERNEL); + if (!sf_g) + return -ENOMEM; + + info->name[sizeof(info->name) - 1] = 0; + info->nls_name[sizeof(info->nls_name) - 1] = 0; + + name_len = strlen(info->name); + str_len = offsetof(struct shfl_string, string.utf8) + name_len + 1; + + str_name = kmalloc(str_len, GFP_KERNEL); + if (!str_name) { + err = -ENOMEM; + goto fail; + } + + str_name->length = name_len; + str_name->size = name_len + 1; + memcpy(str_name->string.utf8, info->name, name_len + 1); + + if (info->nls_name[0]) + nls_name = info->nls_name; + + /* Load nls if not utf8 */ + if (nls_name[0] && strcmp(nls_name, "utf8") != 0) { + sf_g->nls = load_nls(info->nls_name); + if (!sf_g->nls) { + err = -EINVAL; + goto fail; + } + } else { + sf_g->nls = NULL; + } + + rc = vboxsf_map_folder(str_name, &sf_g->root); + if (rc < 0) { + err = -EPROTO; + goto fail; + } + + kfree(str_name); + + sf_g->ttl = info->ttl; + sf_g->uid = info->uid; + sf_g->gid = info->gid; + + if ((size_t)info->length >= sizeof(struct vbsf_mount_info_new)) { + /* 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; + } + + *sf_gp = sf_g; + return 0; + +fail: + if (sf_g->nls) + unload_nls(sf_g->nls); + + kfree(str_name); + kfree(sf_g); + + return err; +} + +/* unmap the share and free global info [sf_g] */ +static void sf_glob_free(struct sf_glob_info *sf_g) +{ + vboxsf_unmap_folder(sf_g->root); + + if (sf_g->nls) + unload_nls(sf_g->nls); + + kfree(sf_g); +} + +/** + * This is called when vfs mounts the fs and wants to read the 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(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; + struct shfl_fsobjinfo fsinfo; + struct vbsf_mount_info_new *info; + + if (!data) + return -EINVAL; + + info = data; + + if (flags & MS_REMOUNT) + return -EINVAL; + + err = sf_glob_alloc(info, &sf_g); + if (err) + goto fail0; + + sf_i = kmalloc(sizeof(*sf_i), GFP_KERNEL); + if (!sf_i) { + err = -ENOMEM; + goto fail1; + } + + sf_i->handle = SHFL_HANDLE_NIL; + sf_i->path = kmalloc(sizeof(struct shfl_string) + 1, GFP_KERNEL); + if (!sf_i->path) { + err = -ENOMEM; + goto fail2; + } + + sf_i->path->length = 1; + sf_i->path->size = 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) + goto fail3; + + sb->s_magic = 0xface; + sb->s_blocksize = 1024; + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_op = &sf_super_ops; + + iroot = iget_locked(sb, 0); + if (!iroot) { + err = -ENOMEM; /* XXX */ + goto fail3; + } + + sf_init_inode(sf_g, iroot, &fsinfo); + SET_INODE_INFO(iroot, sf_i); + + unlock_new_inode(iroot); + + droot = d_make_root(iroot); + if (!droot) { + err = -ENOMEM; + goto fail3; + } + + sb->s_root = droot; + SET_GLOB_INFO(sb, sf_g); + return 0; + +fail3: + kfree(sf_i->path); + +fail2: + kfree(sf_i); + +fail1: + sf_glob_free(sf_g); + +fail0: + return err; +} + +/* + * This is called when vfs is about to destroy the [inode]. all + * resources associated with this [inode] must be cleared here. + */ +static void sf_evict_inode(struct inode *inode) +{ + struct sf_inode_info *sf_i; + + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); + + sf_i = GET_INODE_INFO(inode); + if (!sf_i) + return; + + kfree(sf_i->path); + kfree(sf_i); + SET_INODE_INFO(inode, NULL); +} + +/* + * 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); + sf_glob_free(sf_g); +} + +static int sf_statfs(struct dentry *dentry, struct kstatfs *stat) +{ + struct super_block *sb = dentry->d_inode->i_sb; + + return sf_get_volume_info(sb, stat); +} + +static int sf_remount_fs(struct super_block *sb, int *flags, char *data) +{ + struct sf_glob_info *sf_g; + struct sf_inode_info *sf_i; + struct inode *iroot; + struct shfl_fsobjinfo fsinfo; + int err; + + sf_g = GET_GLOB_INFO(sb); + 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; + sf_g->dmode = info->dmode; + sf_g->fmode = info->fmode; + sf_g->dmask = info->dmask; + sf_g->fmask = info->fmask; + } + } + + iroot = ilookup(sb, 0); + if (!iroot) + return -ENOENT; + + sf_i = GET_INODE_INFO(iroot); + err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0); + sf_init_inode(sf_g, iroot, &fsinfo); + + return 0; +} + +static struct super_operations sf_super_ops = { + .evict_inode = sf_evict_inode, + .put_super = sf_put_super, + .statfs = sf_statfs, + .remount_fs = sf_remount_fs +}; + +static struct dentry *sf_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data) +{ + return mount_nodev(fs_type, flags, data, sf_read_super); +} + +static struct file_system_type vboxsf_fs_type = { + .owner = THIS_MODULE, + .name = "vboxsf", + .mount = sf_mount, + .kill_sb = kill_anon_super +}; + +static int follow_symlinks; +module_param(follow_symlinks, int, 0444); +MODULE_PARM_DESC(follow_symlinks, + "Let host resolve symlinks rather than showing them"); + +/* Module initialization/finalization handlers */ +static int __init init(void) +{ + int rc, err; + + if (sizeof(struct vbsf_mount_info_new) > PAGE_SIZE) { + vbg_err("vboxsf: Mount information structure is too large %zd; Must be less than or equal to %ld\n", + sizeof(struct vbsf_mount_info_new), PAGE_SIZE); + return -EINVAL; + } + + err = register_filesystem(&vboxsf_fs_type); + if (err) + return err; + + rc = vboxsf_connect(); + if (rc < 0) { + vbg_err("vboxsf_Connect failed, rc=%d\n", rc); + err = -EPROTO; + goto fail1; + } + + rc = vboxsf_set_utf8(); + if (rc < 0) { + vbg_err("vboxsf_setutf8 failed, rc=%d\n", rc); + err = -EPROTO; + goto fail2; + } + + if (!follow_symlinks) { + rc = vboxsf_set_symlinks(); + if (rc < 0) { + vbg_warn("vboxsf: Host unable to show symlinks, rc=%d\n", + rc); + } + } + + return 0; + +fail2: + vboxsf_disconnect(); + +fail1: + unregister_filesystem(&vboxsf_fs_type); + return err; +} + +static void __exit fini(void) +{ + vboxsf_disconnect(); + unregister_filesystem(&vboxsf_fs_type); +} + +module_init(init); +module_exit(fini); diff --git a/fs/vboxsf/vfsmod.h b/fs/vboxsf/vfsmod.h new file mode 100644 index 000000000000..93e8791eea9a --- /dev/null +++ b/fs/vboxsf/vfsmod.h @@ -0,0 +1,113 @@ +/* + * VirtualBox Guest Shared Folders support: module header. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VFSMOD_H +#define VFSMOD_H + +#include +#include +#include "vboxsf_wrappers.h" + +#define DIR_BUFFER_SIZE SZ_16K + +/* per-shared folder information */ +struct sf_glob_info { + SHFLROOT root; + struct nls_table *nls; + int ttl; + int uid; + int gid; + int dmode; + int fmode; + int dmask; + int fmask; +}; + +/* per-inode information */ +struct sf_inode_info { + /* which file */ + struct shfl_string *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 entries; + size_t free; + size_t used; + void *buf; + struct list_head head; +}; + +struct sf_reg_info { + SHFLHANDLE handle; +}; + +/* globals */ +extern const struct inode_operations sf_dir_iops; +extern const struct inode_operations sf_lnk_iops; +extern const struct inode_operations sf_reg_iops; +extern const struct file_operations sf_dir_fops; +extern const struct file_operations sf_reg_fops; +extern const struct dentry_operations sf_dentry_ops; +extern const struct address_space_operations sf_reg_aops; + +void sf_init_inode(struct sf_glob_info *sf_g, struct inode *inode, + const struct shfl_fsobjinfo *info); +int sf_stat(const char *caller, struct sf_glob_info *sf_g, + struct shfl_string *path, struct shfl_fsobjinfo *result, + int ok_to_fail); +int sf_inode_revalidate(struct dentry *dentry); +int sf_getattr(const struct path *path, struct kstat *kstat, + u32 request_mask, unsigned int query_flags); +int sf_setattr(struct dentry *dentry, struct iattr *iattr); +int sf_path_from_dentry(const char *caller, struct sf_glob_info *sf_g, + struct sf_inode_info *sf_i, + struct dentry *dentry, struct shfl_string **result); +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); +void sf_dir_info_free(struct sf_dir_info *p); +void sf_dir_info_empty(struct sf_dir_info *p); +struct sf_dir_info *sf_dir_info_alloc(void); +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 sf_get_volume_info(struct super_block *sb, struct kstatfs *stat); + +/* + * Following casts are here to prevent assignment of void * to + * pointers of arbitrary type. + */ +#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)) + +#define GET_INODE_INFO(i) ((struct sf_inode_info *)(i)->i_private) +#define SET_INODE_INFO(i, sf_i) ((i)->i_private = (sf_i)) + +#define GET_F_DENTRY(f) (f->f_path.dentry) + +#endif diff --git a/include/uapi/linux/vbsfmount.h b/include/uapi/linux/vbsfmount.h new file mode 100644 index 000000000000..21aa546a2a64 --- /dev/null +++ b/include/uapi/linux/vbsfmount.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * VirtualBox Guest Shared Folders: mount(2) parameter structure. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef VBFS_MOUNT_H +#define VBFS_MOUNT_H + +/* 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 */ +}; + +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; +}; + +#endif /* vbsfmount.h */ -- 2.13.4