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 <hdego...@redhat.com> --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/vboxsf/Kconfig | 9 + fs/vboxsf/Makefile | 3 + fs/vboxsf/dirops.c | 730 ++++++++++++++++++++++++ fs/vboxsf/lnkops.c | 41 ++ fs/vboxsf/regops.c | 557 ++++++++++++++++++ fs/vboxsf/shfl_hostintf.h | 1318 +++++++++++++++++++++++++++++++++++++++++++ fs/vboxsf/utils.c | 671 ++++++++++++++++++++++ fs/vboxsf/vboxsf_wrappers.c | 542 ++++++++++++++++++ fs/vboxsf/vboxsf_wrappers.h | 104 ++++ fs/vboxsf/vbsfmount.h | 69 +++ fs/vboxsf/vfsmod.c | 412 ++++++++++++++ fs/vboxsf/vfsmod.h | 111 ++++ 14 files changed, 4569 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/vbsfmount.h create mode 100644 fs/vboxsf/vfsmod.c create mode 100644 fs/vboxsf/vfsmod.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..e32040abca98 --- /dev/null +++ b/fs/vboxsf/dirops.c @@ -0,0 +1,730 @@ +/* + * VirtualBox Guest Shared Folders support: Directory inode and file operations + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <linux/vbox_err.h> +#include <linux/vbox_utils.h> +#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 = {}; + + BUG_ON(!sf_g); + BUG_ON(!sf_i); + + if (file->private_data) + return 0; + + sf_d = sf_dir_info_alloc(); + if (!sf_d) + return -ENOMEM; + + params.Handle = SHFL_HANDLE_NIL; + params.CreateFlags = 0 + | SHFL_CF_DIRECTORY + | SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ; + + 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 fMode file mode + * returns d_type + */ +static int sf_get_d_type(u32 fMode) +{ + int d_type; + switch (fMode & 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; + + BUG_ON(!sf_g); + BUG_ON(!sf_d); + BUG_ON(!sf_i); + + if (sf_i->force_reread) { + int rc; + int err; + struct shfl_createparms params = {}; + + params.Handle = SHFL_HANDLE_NIL; + params.CreateFlags = 0 + | SHFL_CF_DIRECTORY + | SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ; + + 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; + SHFLDIRINFO *info; + loff_t i; + + b = list_entry(pos, struct sf_dir_buf, head); + if (dir->f_pos >= cur + b->cEntries) { + cur += b->cEntries; + continue; + } + + for (i = 0, info = b->buf; i < dir->f_pos - cur; ++i) { + size_t size; + + size = + offsetof(SHFLDIRINFO, + name.String) + info->name.u16Size; + info = (SHFLDIRINFO *) ((uintptr_t) info + size); + } + + *d_type = sf_get_d_type(info->Info.Attr.fMode); + + return sf_nlscpy(sf_g, d_name, NAME_MAX, + info->name.String.utf8, info->name.u16Length); + } + + return 1; +} + +/** + * This is called when vfs wants to populate internal buffers with + * directory [dir]s contents. [opaque] is an argument to the + * [filldir]. [filldir] magically modifies it's argument - [opaque] + * and takes following additional arguments (which i in turn get from + * the host via sf_getdent): + * + * name : name of the entry (i must also supply it's length huh?) + * type : type of the entry (FILE | DIR | etc) (i ellect to use DT_UNKNOWN) + * pos : position/index of the entry + * ino : inode number of the entry (i fake those) + * + * [dir] contains: + * f_pos : cursor into the directory listing + * private_data : mean of communication with the host side + * + * Extract elements from the directory listing (incrementing f_pos + * along the way) and feed them to [filldir] until: + * + * a. there are no more entries (i.e. sf_getdent set done to 1) + * b. failure to compute fake inode number + * c. filldir returns an error (see comment on that) + */ +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; + } +} + +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; + SHFLSTRING *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); + + BUG_ON(!sf_g); + BUG_ON(!sf_i); + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + err = sf_stat(__func__, sf_g, path, &fsinfo, 1); + if (err) { + if (err == -ENOENT) { + /* -ENOENT: add NULL inode to dentry so it later can be + created via call to create/mkdir/open */ + kfree(path); + inode = NULL; + } else + goto fail1; + } else { + sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL); + if (!sf_new_i) { + 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, + SHFLSTRING * 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); + + BUG_ON(!sf_g); + + 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 = {}; + SHFLSTRING *path; + struct sf_inode_info *sf_i = GET_INODE_INFO(parent); + struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb); + + BUG_ON(!sf_i); + BUG_ON(!sf_g); + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + params.Handle = SHFL_HANDLE_NIL; + params.CreateFlags = 0 + | SHFL_CF_ACT_CREATE_IF_NEW + | SHFL_CF_ACT_FAIL_IF_EXISTS + | SHFL_CF_ACCESS_READWRITE | (fDirectory ? SHFL_CF_DIRECTORY : 0); + params.Info.Attr.fMode = 0 + | (fDirectory ? SHFL_TYPE_DIRECTORY : SHFL_TYPE_FILE) + | (mode & S_IRWXUGO); + params.Info.Attr.enmAdditional = 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); + SHFLSTRING *path; + uint32_t fFlags; + + BUG_ON(!sf_g); + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + fFlags = fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE; + if (dentry + && dentry->d_inode + && ((dentry->d_inode->i_mode & S_IFLNK) == S_IFLNK)) + fFlags |= SHFL_REMOVE_SYMLINK; + rc = vboxsf_remove(sf_g->root, path, fFlags); + 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); + SHFLSTRING *old_path; + SHFLSTRING *new_path; + + BUG_ON(!sf_old_i); + BUG_ON(!sf_new_i); + BUG_ON(!sf_file_i); + + old_path = sf_file_i->path; + err = sf_path_from_dentry(__func__, sf_g, sf_new_i, + new_dentry, &new_path); + if (err == 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; + SHFLSTRING *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); + + BUG_ON(!sf_g); + BUG_ON(!sf_i); + + err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); + if (err) + goto fail0; + + ssymname = + kmalloc(offsetof(SHFLSTRING, String.utf8) + symname_len, + GFP_KERNEL); + if (!ssymname) { + err = -ENOMEM; + goto fail1; + } + + ssymname->u16Length = symname_len - 1; + ssymname->u16Size = 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; +} + +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..546b2a83166a --- /dev/null +++ b/fs/vboxsf/lnkops.c @@ -0,0 +1,41 @@ +/* + * VirtualBox Guest Shared Folders: Operations for symbolic links. + * + * Copyright (C) 2010-2016 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "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; +} + +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..c3ee582df2a2 --- /dev/null +++ b/fs/vboxsf/regops.c @@ -0,0 +1,557 @@ +/* + * VirtualBox Guest Shared Folders support: Regular file inode and file ops. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * Limitations: only COW memory mapping is supported + */ + +#include <linux/vbox_utils.h> +#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, + uint32_t * nread, uint64_t pos) +{ + /** + * @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is + * contiguous in physical memory (kmalloc or single page), we should + * use a physical address here to speed things up. + */ + int rc = 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, + uint32_t * nwritten, uint64_t pos) +{ + /** + * @todo bird: yes, kmap() and kmalloc() input only. Since the buffer + * is contiguous in physical memory (kmalloc or single page), we should + * use a physical address here to speed things up. + */ + int rc = 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) { + uint32_t to_read, nread; + + to_read = tmp_size; + if (to_read > left) + to_read = (uint32_t) left; + + nread = to_read; + + err = sf_reg_read_aux(__func__, sf_g, sf_r, tmp, &nread, pos); + if (err) + goto fail; + + if (copy_to_user(buf, tmp, nread)) { + err = -EFAULT; + goto fail; + } + + pos += nread; + left -= nread; + buf += nread; + total_bytes_read += nread; + if (nread != to_read) + break; + } + + *off += total_bytes_read; + free_bounce_buffer(tmp); + return total_bytes_read; + +fail: + free_bounce_buffer(tmp); + return err; +} + +/** + * Write to a regular file. + * + * @param file the file + * @param buf the buffer + * @param size length of the buffer + * @param off offset within the file + * @returns the number of written bytes on success, Linux error code otherwise + */ +static ssize_t sf_reg_write(struct file *file, const char *buf, size_t size, + loff_t * off) +{ + int err; + void *tmp; + 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) { + uint32_t to_write, nwritten; + + to_write = tmp_size; + if (to_write > left) + to_write = (uint32_t) left; + + nwritten = to_write; + + if (copy_from_user(tmp, buf, to_write)) { + err = -EFAULT; + goto fail; + } + + err = 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 CreateFlags: + * O_CREAT, O_TRUNC: inherent true (file was just created). Not sure + * about the access flags (SHFL_CF_ACCESS_*). + */ + sf_i->force_restat = 1; + sf_r->handle = sf_i->handle; + sf_i->handle = SHFL_HANDLE_NIL; + sf_i->file = file; + file->private_data = sf_r; + return 0; + } + + params.Handle = SHFL_HANDLE_NIL; + /* We check the value of params.Handle afterwards to find out if + * the call succeeded or failed, as the API does not seem to cleanly + * distinguish error and informational messages. + * + * Furthermore, we must set params.Handle to SHFL_HANDLE_NIL to + * make the shared folders host service use our fMode parameter */ + + if (file->f_flags & O_CREAT) { + params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW; + /* We ignore O_EXCL, as the Linux kernel seems to call create + beforehand itself, so O_EXCL should always fail. */ + if (file->f_flags & O_TRUNC) + params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS; + else + params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS; + } else { + params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW; + if (file->f_flags & O_TRUNC) + params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS; + } + + switch (file->f_flags & O_ACCMODE) { + case O_RDONLY: + params.CreateFlags |= SHFL_CF_ACCESS_READ; + break; + + case O_WRONLY: + params.CreateFlags |= SHFL_CF_ACCESS_WRITE; + break; + + case O_RDWR: + params.CreateFlags |= SHFL_CF_ACCESS_READWRITE; + break; + + default: + BUG(); + } + + if (file->f_flags & O_APPEND) + params.CreateFlags |= SHFL_CF_ACCESS_APPEND; + + params.Info.Attr.fMode = 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 (SHFL_HANDLE_NIL == params.Handle) { + switch (params.Result) { + case SHFL_PATH_NOT_FOUND: + case SHFL_FILE_NOT_FOUND: + rc_linux = -ENOENT; + break; + case SHFL_FILE_EXISTS: + rc_linux = -EEXIST; + break; + default: + break; + } + } + + sf_i->force_restat = 1; + sf_r->handle = params.Handle; + sf_i->file = file; + file->private_data = sf_r; + return rc_linux; +} + +/** + * Close a regular file. + * + * @param inode the inode + * @param file the file + * @returns 0 on success, Linux error code otherwise + */ +static int sf_reg_release(struct inode *inode, struct file *file) +{ + 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; + uint32_t 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 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; +} + +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, +}; + +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; + uint32_t 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; + uint32_t 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 len, unsigned 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 len, unsigned copied, struct page *page, void *fsdata) +{ + struct inode *inode = mapping->host; + struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); + struct sf_reg_info *sf_r = file->private_data; + void *buf; + unsigned from = pos & (PAGE_SIZE - 1); + uint32_t nwritten = len; + int err; + + 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; +} + +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..071f72cfbb86 --- /dev/null +++ b/fs/vboxsf/shfl_hostintf.h @@ -0,0 +1,1318 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * VirtualBox Shared Folders: host interface definition. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, 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 <linux/vbox_vmmdev.h> + +/* + * 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 ## _assert_size[1 - 2*!!(sizeof(struct type) != (size))] + +/** + * 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. + */ +typedef uint32_t SHFLROOT; + +#define SHFL_ROOT_NIL ((SHFLROOT)~0) + +/** A shared folders handle for an opened object. */ +typedef uint64_t SHFLHANDLE; + +#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. + */ +typedef struct SHFLSTRING { + /** Allocated size of the String member in bytes. */ + uint16_t u16Size; + + /** Length of string without trailing nul in bytes. */ + uint16_t u16Length; + + /** UTF-8 or UTF-16 string. Nul terminated. */ + union { + u8 utf8[1]; + u16 utf16[1]; + u16 ucs2[1]; /**< misnomer, use utf16. */ + } String; +} SHFLSTRING; +VMMDEV_ASSERT_SIZE(SHFLSTRING, 6); +VMMDEV_ASSERT_MEMBER_OFFSET(SHFLSTRING, String, 4); +/** The size of SHFLSTRING w/o the string part. */ +#define SHFLSTRING_HEADER_SIZE 4 +VMMDEV_ASSERT_MEMBER_OFFSET(SHFLSTRING, String, SHFLSTRING_HEADER_SIZE); + +/** Pointer to a shared folder string buffer. */ +typedef SHFLSTRING *PSHFLSTRING; +/** Pointer to a const shared folder string buffer. */ +typedef const SHFLSTRING *PCSHFLSTRING; + +/** Calculate size of the string. */ +static inline u32 ShflStringSizeOfBuffer(const struct SHFLSTRING *string) +{ + return string ? (uint32_t) (SHFLSTRING_HEADER_SIZE + + string->u16Size) : 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) are + * available / requested. + */ + SHFLFSOBJATTRADD_UNIX, + /** + * The additional extended attribute size (shfl_fsobjattr::u::EASize) 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.enmAdditional == 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 cHardlinks; + + /** + * 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 INodeIdDevice; + + /** + * The unique identifier (within the filesystem) of this filesystem object (st_ino). + * Together with INodeIdDevice, 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 INodeId; + + /** + * User flags (st_flags). + * This field is 0 if this information is not available. + */ + u32 fFlags; + + /** + * The current generation number (st_gen). + * This field is 0 if this information is not available. + */ + u32 GenerationId; + + /** + * The device number of a character or block device type object (st_rdev). + * This field is 0 if the file isn't of a character or block device type and + * when the OS doesn't subscribe to the major+minor device idenfication scheme. + */ + u32 Device; +} __packed; + +/** Extended attribute size. */ +struct SHFLFSOBJATTREASIZE { + 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 fMode; + + /** The additional attributes available. */ + enum shfl_fsobjattr_add enmAdditional; + + /** + * 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; + struct shfl_fsobjattr_unix EASize; + } __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 cbObject; + + /** Disk allocation size (st_blocks * DEV_BSIZE). */ + s64 cbAllocated; + + /** + * Time of last access (st_atime). + * @remarks Here (and other places) we depend on the IPRT timespec to + * remain unchanged. + */ + struct shfl_timespec AccessTime; + + /** Time of last data modification (st_mtime). */ + struct shfl_timespec ModificationTime; + + /** + * Time of last status change (st_ctime). + * If not available this is set to ModificationTime. + */ + struct shfl_timespec ChangeTime; + + /** + * Time of file birth (st_birthtime). + * If not available this is set to ChangeTime. + */ + struct shfl_timespec BirthTime; + + /** 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. + */ +typedef enum _SHFLCREATERESULT { + 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 +} SHFLCREATERESULT; + +/** Open/create flags. + * @{ + */ + +/** No flags. Initialization value. */ +#define SHFL_CF_NONE (0x00000000) + +/** Lookup only the object, do not return a handle. 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 (SHFL_CF_ACCESS_READ | SHFL_CF_ACCESS_WRITE) + +/** 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 (SHFL_CF_ACCESS_DENYREAD | SHFL_CF_ACCESS_DENYWRITE) + +/** 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 (SHFL_CF_ACCESS_ATTR_READ | SHFL_CF_ACCESS_ATTR_WRITE) + +/** 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 */ + SHFLCREATERESULT Result; + + /* SHFL_CF_* */ + uint32_t CreateFlags; + + /* + * 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) + +typedef struct _SHFLMAPPING { + /** Mapping status. */ + uint32_t u32Status; + /** Root handle. */ + SHFLROOT root; +} SHFLMAPPING; +/** Pointer to a SHFLMAPPING structure. */ +typedef SHFLMAPPING *PSHFLMAPPING; + +/** @} */ + +/** Shared Folder directory information + * @{ + */ + +typedef struct _SHFLDIRINFO { + /** 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. + */ + uint16_t cucShortName; + /** + * The short name for 8.3 compatibility. + * Empty string if not available. + */ + u16 uszShortName[14]; + /** @todo malc, a description, please. */ + SHFLSTRING name; +} SHFLDIRINFO, *PSHFLDIRINFO; + +/** + * Shared folder filesystem properties. + */ +typedef struct SHFLFSPROPERTIES { + /** + * The maximum size of a filesystem object name. + * This does not include the '\\0'. + */ + uint32_t cbMaxComponent; + + /** + * True if the filesystem is remote. + * False if the filesystem is local. + */ + bool fRemote; + + /** + * True if the filesystem is case sensitive. + * False if the filesystem is case insensitive. + */ + bool fCaseSensitive; + + /** + * True if the filesystem is mounted read only. + * False if the filesystem is mounted read write. + */ + bool fReadOnly; + + /** + * True if the filesystem can encode unicode object names. + * False if it can't. + */ + bool fSupportsUnicode; + + /** + * True if the filesystem is compresses. + * False if it isn't or we don't know. + */ + bool fCompressed; + + /** + * True if the filesystem compresses of individual files. + * False if it doesn't or we don't know. + */ + bool fFileCompression; + + /** @todo more? */ +} SHFLFSPROPERTIES; +VMMDEV_ASSERT_SIZE(SHFLFSPROPERTIES, 12); +/** Pointer to a shared folder filesystem properties structure. */ +typedef SHFLFSPROPERTIES *PSHFLFSPROPERTIES; +/** Pointer to a const shared folder filesystem properties structure. */ +typedef SHFLFSPROPERTIES const *PCSHFLFSPROPERTIES; + +typedef struct _SHFLVOLINFO { + s64 ullTotalAllocationBytes; + s64 ullAvailableAllocationBytes; + uint32_t ulBytesPerAllocationUnit; + uint32_t ulBytesPerSector; + uint32_t ulSerial; + SHFLFSPROPERTIES fsProperties; +} SHFLVOLINFO, *PSHFLVOLINFO; + +/** @} */ + +/** + * 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. */ +typedef struct _VBoxSFQueryMappings { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 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 numberOfMappings; + + /** + * pointer, in/out: + * Points to array of SHFLMAPPING structures. + */ + HGCMFunctionParameter mappings; + +} VBoxSFQueryMappings; + +/** Number of parameters */ +#define SHFL_CPARMS_QUERY_MAPPINGS (3) + +/** + * SHFL_FN_QUERY_MAP_NAME + */ + +/** Parameters structure. */ +typedef struct _VBoxSFQueryMapName { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 32bit, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in/out: + * Points to SHFLSTRING buffer. + */ + HGCMFunctionParameter name; + +} VBoxSFQueryMapName; + +/** Number of parameters */ +#define SHFL_CPARMS_QUERY_MAP_NAME (2) + +/** + * SHFL_FN_MAP_FOLDER_OLD + */ + +/** Parameters structure. */ +typedef struct _VBoxSFMapFolder_Old { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: + * Points to SHFLSTRING buffer. + */ + HGCMFunctionParameter path; + + /** + * pointer, out: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: UTF16 + * Path delimiter + */ + HGCMFunctionParameter delimiter; + +} VBoxSFMapFolder_Old; + +/** Number of parameters */ +#define SHFL_CPARMS_MAP_FOLDER_OLD (3) + +/** + * SHFL_FN_MAP_FOLDER + */ + +/** Parameters structure. */ +typedef struct _VBoxSFMapFolder { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: + * Points to SHFLSTRING 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 fCaseSensitive; + +} VBoxSFMapFolder; + +/** Number of parameters */ +#define SHFL_CPARMS_MAP_FOLDER (4) + +/** + * SHFL_FN_UNMAP_FOLDER + */ + +/** Parameters structure. */ +typedef struct _VBoxSFUnmapFolder { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + +} VBoxSFUnmapFolder; + +/** Number of parameters */ +#define SHFL_CPARMS_UNMAP_FOLDER (1) + +/** + * SHFL_FN_CREATE + */ + +/** Parameters structure. */ +typedef struct _VBoxSFCreate { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to SHFLSTRING buffer. + */ + HGCMFunctionParameter path; + + /** + * pointer, in/out: + * Points to SHFLCREATEPARMS buffer. + */ + HGCMFunctionParameter parms; + +} VBoxSFCreate; + +/** Number of parameters */ +#define SHFL_CPARMS_CREATE (3) + +/** + * SHFL_FN_CLOSE + */ + +/** Parameters structure. */ +typedef struct _VBoxSFClose { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to close. + */ + HGCMFunctionParameter handle; + +} VBoxSFClose; + +/** Number of parameters */ +#define SHFL_CPARMS_CLOSE (2) + +/** + * SHFL_FN_READ + */ + +/** Parameters structure. */ +typedef struct _VBoxSFRead { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 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; + +} VBoxSFRead; + +/** Number of parameters */ +#define SHFL_CPARMS_READ (5) + +/** + * SHFL_FN_WRITE + */ + +/** Parameters structure. */ +typedef struct _VBoxSFWrite { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 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; + +} VBoxSFWrite; + +/** 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. */ +typedef struct _VBoxSFLock { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 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; + +} VBoxSFLock; + +/** Number of parameters */ +#define SHFL_CPARMS_LOCK (5) + +/** + * SHFL_FN_FLUSH + */ + +/** Parameters structure. */ +typedef struct _VBoxSFFlush { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * value64, in: + * SHFLHANDLE of object to be locked. + */ + HGCMFunctionParameter handle; + +} VBoxSFFlush; + +/** 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. */ +typedef struct _VBoxSFList { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 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 SHFLSTRING buffer that specifies a search path. + */ + HGCMFunctionParameter path; + + /** + * pointer, out: + * Buffer to place listing information to. (SHFLDIRINFO) + */ + 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 resumePoint; + + /** + * pointer, out: + * Number of files returned + */ + HGCMFunctionParameter cFiles; + +} VBoxSFList; + +/** Number of parameters */ +#define SHFL_CPARMS_LIST (8) + +/** + * SHFL_FN_READLINK + */ + +/** Parameters structure. */ +typedef struct _VBoxSFReadLink { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to SHFLSTRING buffer. + */ + HGCMFunctionParameter path; + + /** + * pointer, out: + * Buffer to place data to. + */ + HGCMFunctionParameter buffer; + +} VBoxSFReadLink; + +/** 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. */ +typedef struct _VBoxSFInformation { + VBoxGuestHGCMCallInfo callInfo; + + /** + * 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 SHFLSTRING). Do not forget + * to set the shfl_fsobjinfo::Attr::enmAdditional for Get operation as well. + */ + HGCMFunctionParameter info; + +} VBoxSFInformation; + +/** 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. */ +typedef struct _VBoxSFRemove { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to SHFLSTRING buffer. + */ + HGCMFunctionParameter path; + + /** + * value32, in: + * remove flags (file/directory) + */ + HGCMFunctionParameter flags; + +} VBoxSFRemove; + +#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. */ +typedef struct _VBoxSFRename { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to SHFLSTRING src. + */ + HGCMFunctionParameter src; + + /** + * pointer, in: + * Points to SHFLSTRING dest. + */ + HGCMFunctionParameter dest; + + /** + * value32, in: + * rename flags (file/directory) + */ + HGCMFunctionParameter flags; + +} VBoxSFRename; + +#define SHFL_CPARMS_RENAME (4) + +/** + * SHFL_FN_SYMLINK + */ + +/** Parameters structure. */ +typedef struct _VBoxSFSymlink { + VBoxGuestHGCMCallInfo callInfo; + + /** + * pointer, in: SHFLROOT + * Root handle of the mapping which name is queried. + */ + HGCMFunctionParameter root; + + /** + * pointer, in: + * Points to SHFLSTRING of path for the new symlink. + */ + HGCMFunctionParameter newPath; + + /** + * pointer, in: + * Points to SHFLSTRING of destination for symlink. + */ + HGCMFunctionParameter oldPath; + + /** + * pointer, out: + * Information about created symlink. + */ + HGCMFunctionParameter info; + +} VBoxSFSymlink; + +#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 (RT_BIT_32(0)) +/** mapping is automounted by the guest */ +#define SHFL_ADD_MAPPING_F_AUTOMOUNT (RT_BIT_32(1)) +/** allow the guest to create symlinks */ +#define SHFL_ADD_MAPPING_F_CREATE_SYMLINKS (RT_BIT_32(2)) +/** mapping is actually missing on the host */ +#define SHFL_ADD_MAPPING_F_MISSING (RT_BIT_32(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..533dd539a480 --- /dev/null +++ b/fs/vboxsf/utils.c @@ -0,0 +1,671 @@ +/* + * VirtualBox Guest Shared Folders support: Utility functions. + * Mainly conversion from/to VirtualBox/Linux data structures. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <linux/namei.h> +#include <linux/nfs_fs.h> +#include <linux/nls.h> +#include <linux/vfs.h> +#include <linux/vbox_err.h> +#include <linux/vbox_utils.h> +#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->fMode & (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->fMode)) { + inode->i_mode = sf_g->dmode != ~0 ? (sf_g->dmode & 0777) : mode; + inode->i_mode &= ~sf_g->dmask; + inode->i_mode |= S_IFDIR; + inode->i_op = &sf_dir_iops; + inode->i_fop = &sf_dir_fops; + /* XXX: this probably should be set to the number of entries + in the directory plus two (. ..) */ + set_nlink(inode, 1); + } else if (SHFL_IS_SYMLINK(attr->fMode)) { + inode->i_mode = sf_g->fmode != ~0 ? (sf_g->fmode & 0777) : mode; + inode->i_mode &= ~sf_g->fmask; + inode->i_mode |= S_IFLNK; + inode->i_op = &sf_lnk_iops; + 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->cbObject; + inode->i_blkbits = 12; + /* i_blocks always in units of 512 bytes! */ + allocated = info->cbAllocated + 511; + do_div(allocated, 512); + inode->i_blocks = allocated; + + sf_timespec_from_vbox(&inode->i_atime, &info->AccessTime); + sf_timespec_from_vbox(&inode->i_ctime, &info->ChangeTime); + sf_timespec_from_vbox(&inode->i_mtime, &info->ModificationTime); +} + +int sf_stat(const char *caller, struct sf_glob_info *sf_g, + SHFLSTRING * path, struct shfl_fsobjinfo *result, int ok_to_fail) +{ + struct shfl_createparms params = {}; + int rc; + + params.Handle = SHFL_HANDLE_NIL; + params.CreateFlags = 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 directly as iop on 2.4, indirectly as dop + [sf_dentry_revalidate] on 2.4/2.6, indirectly as iop through + [sf_getattr] on 2.6. the job is to find out whether dentry/inode is + still valid. the test is failed if [dentry] does not have an inode + or [sf_stat] is unsuccessful, otherwise we return success and + update inode attributes */ +int sf_inode_revalidate(struct dentry *dentry) +{ + int err; + struct sf_glob_info *sf_g; + struct sf_inode_info *sf_i; + 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); + + BUG_ON(!sf_g); + BUG_ON(!sf_i); + + if (!sf_i->force_restat) { + if (jiffies - dentry->d_time < sf_g->ttl) + return 0; + } + + err = sf_stat(__func__, sf_g, sf_i->path, &info, 1); + if (err) + return err; + + dentry->d_time = jiffies; + sf_init_inode(sf_g, dentry->d_inode, &info); + return 0; +} + +/* this is called during name resolution/lookup to check if the + [dentry] in the cache is still valid. the job is handled by + [sf_inode_revalidate] */ +static int sf_dentry_revalidate(struct dentry *dentry, unsigned 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 cbBuffer; + 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.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS + | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_ATTR_WRITE; + + /* this is at least required for Posix hosts */ + if (iattr->ia_valid & ATTR_SIZE) + params.CreateFlags |= SHFL_CF_ACCESS_WRITE; + + rc = 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.fMode = mode_set(ISUID); + info.Attr.fMode |= mode_set(ISGID); + info.Attr.fMode |= mode_set(IRUSR); + info.Attr.fMode |= mode_set(IWUSR); + info.Attr.fMode |= mode_set(IXUSR); + info.Attr.fMode |= mode_set(IRGRP); + info.Attr.fMode |= mode_set(IWGRP); + info.Attr.fMode |= mode_set(IXGRP); + info.Attr.fMode |= mode_set(IROTH); + info.Attr.fMode |= mode_set(IWOTH); + info.Attr.fMode |= mode_set(IXOTH); + + if (iattr->ia_mode & S_IFDIR) + info.Attr.fMode |= SHFL_TYPE_DIRECTORY; + else + info.Attr.fMode |= SHFL_TYPE_FILE; + } + + if (iattr->ia_valid & ATTR_ATIME) + sf_timespec_to_vbox(&info.AccessTime, &iattr->ia_atime); + if (iattr->ia_valid & ATTR_MTIME) + sf_timespec_to_vbox(&info.ModificationTime, + &iattr->ia_mtime); + /* ignore ctime (inode change time) as it can't be set from userland anyway */ + + cbBuffer = sizeof(info); + rc = vboxsf_fsinfo(sf_g->root, params.Handle, + SHFL_INFO_SET | SHFL_INFO_FILE, &cbBuffer, + (PSHFLDIRINFO) & 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.cbObject = iattr->ia_size; + cbBuffer = sizeof(info); + rc = vboxsf_fsinfo(sf_g->root, params.Handle, + SHFL_INFO_SET | SHFL_INFO_SIZE, &cbBuffer, + (PSHFLDIRINFO) & 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, SHFLSTRING ** result) +{ + size_t path_len, shflstring_len; + SHFLSTRING *tmp; + uint16_t p_len; + uint8_t *p_name; + int fRoot = 0; + + p_len = sf_i->path->u16Length; + p_name = sf_i->path->String.utf8; + + if (p_len == 1 && *p_name == '/') { + path_len = d_len + 1; + fRoot = 1; + } else { + /* lengths of constituents plus terminating zero plus slash */ + path_len = p_len + d_len + 2; + if (path_len > 0xffff) + return -ENAMETOOLONG; + } + + shflstring_len = offsetof(SHFLSTRING, String.utf8) + path_len; + tmp = kmalloc(shflstring_len, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + tmp->u16Length = path_len - 1; + tmp->u16Size = path_len; + + if (fRoot) + memcpy(&tmp->String.utf8[0], d_name, d_len + 1); + else { + memcpy(&tmp->String.utf8[0], p_name, p_len); + tmp->String.utf8[p_len] = '/'; + memcpy(&tmp->String.utf8[p_len + 1], d_name, d_len); + tmp->String.utf8[p_len + 1 + d_len] = '\0'; + } + + *result = tmp; + return 0; +} + +/** + * [dentry] contains string encoded in coding system that corresponds + * to [sf_g]->nls, we must convert it to UTF8 here and pass down to + * [sf_make_path] which will allocate SHFLSTRING and fill it in + */ +int sf_path_from_dentry(const char *caller, struct sf_glob_info *sf_g, + struct sf_inode_info *sf_i, struct dentry *dentry, + SHFLSTRING ** result) +{ + int err; + const char *d_name; + size_t d_len; + const char *name; + size_t len = 0; + + 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->cEntries = 0; + b->cbUsed = 0; + b->cbFree = DIR_BUFFER_SIZE; + return b; +} + +static void sf_dir_buf_free(struct sf_dir_buf *b) +{ + BUG_ON(!b || !b->buf); + + 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; + list = &p->info_list; + list_for_each_safe(pos, tmp, list) { + struct sf_dir_buf *b; + b = list_entry(pos, struct sf_dir_buf, head); + b->cEntries = 0; + b->cbUsed = 0; + b->cbFree = DIR_BUFFER_SIZE; + } +} + +/** + * Create a new directory buffer descriptor. + */ +struct sf_dir_info *sf_dir_info_alloc(void) +{ + struct sf_dir_info *p; + + 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; + else { + if (b->cbUsed == 0) + return b; + } + } + + return NULL; +} + +int sf_dir_read_all(struct sf_glob_info *sf_g, struct sf_inode_info *sf_i, + struct sf_dir_info *sf_d, SHFLHANDLE handle) +{ + int err; + SHFLSTRING *mask; + struct sf_dir_buf *b; + + err = sf_make_path(__func__, sf_i, "*", 1, &mask); + if (err) + goto fail0; + + for (;;) { + int rc; + void *buf; + uint32_t cbSize; + uint32_t cEntries; + + b = sf_get_empty_dir_buf(sf_d); + if (!b) { + b = sf_dir_buf_alloc(); + if (!b) { + err = -ENOMEM; + goto fail1; + } + list_add(&b->head, &sf_d->info_list); + } + + buf = b->buf; + cbSize = b->cbFree; + + rc = vboxsf_dirinfo(sf_g->root, handle, mask, 0, 0, + &cbSize, buf, &cEntries); + 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->cEntries += cEntries; + b->cbFree -= cbSize; + b->cbUsed += cbSize; + + 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; + SHFLVOLINFO SHFLVolumeInfo; + u32 cbBuffer; + int rc; + + sf_g = GET_GLOB_INFO(sb); + cbBuffer = sizeof(SHFLVolumeInfo); + rc = vboxsf_fsinfo(sf_g->root, 0, + SHFL_INFO_GET | SHFL_INFO_VOLUME, &cbBuffer, + (PSHFLDIRINFO)&SHFLVolumeInfo); + if (rc < 0) + return -vbg_status_code_to_errno(rc); + + stat->f_type = NFS_SUPER_MAGIC; /* XXX vboxsf type? */ + stat->f_bsize = SHFLVolumeInfo.ulBytesPerAllocationUnit; + + do_div(SHFLVolumeInfo.ullTotalAllocationBytes, + SHFLVolumeInfo.ulBytesPerAllocationUnit); + stat->f_blocks = SHFLVolumeInfo.ullTotalAllocationBytes; + + do_div(SHFLVolumeInfo.ullAvailableAllocationBytes, + SHFLVolumeInfo.ulBytesPerAllocationUnit); + stat->f_bfree = SHFLVolumeInfo.ullAvailableAllocationBytes; + stat->f_bavail = SHFLVolumeInfo.ullAvailableAllocationBytes; + + stat->f_files = 1000; + stat->f_ffree = 1000; /* don't return 0 here since the guest may think + * that it is not possible to create any more files */ + stat->f_fsid.val[0] = 0; + stat->f_fsid.val[1] = 0; + stat->f_namelen = 255; + return 0; +} + +struct dentry_operations sf_dentry_ops = { + .d_revalidate = sf_dentry_revalidate +}; diff --git a/fs/vboxsf/vboxsf_wrappers.c b/fs/vboxsf/vboxsf_wrappers.c new file mode 100644 index 000000000000..620b6c9b2829 --- /dev/null +++ b/fs/vboxsf/vboxsf_wrappers.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * Wrapper functions for the shfl host calls. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, 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 <linux/mm.h> +#include <linux/slab.h> +#include <linux/vbox_err.h> +#include <linux/vbox_utils.h> +#include "vboxsf_wrappers.h" + +#define VBOX_INIT_CALL(a, b, c) \ +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 VBOXGUESTDEVEXT *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 VBOXGUESTDEVEXT *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 VBOXGUESTDEVEXT *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(SHFLMAPPING paMappings[], u32 *pcMappings) +{ + int rc; + VBoxSFQueryMappings data; + + VBOX_INIT_CALL(&data.callInfo, QUERY_MAPPINGS, pClient); + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = SHFL_MF_UCS2; + + data.numberOfMappings.type = VMMDevHGCMParmType_32bit; + data.numberOfMappings.u.value32 = *pcMappings; + + data.mappings.type = VMMDevHGCMParmType_LinAddr; + data.mappings.u.Pointer.size = sizeof(SHFLMAPPING) * *pcMappings; + data.mappings.u.Pointer.u.linearAddr = (uintptr_t)&paMappings[0]; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *pcMappings = data.numberOfMappings.u.value32; + + return rc; +} + +int vboxsf_query_mapname(SHFLROOT root, SHFLSTRING *pString, u32 size) +{ + VBoxSFQueryMapName data; + + VBOX_INIT_CALL(&data.callInfo, QUERY_MAP_NAME, pClient); + + 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)pString; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_map_folder(PSHFLSTRING szFolderName, SHFLROOT *root) +{ + int rc; + VBoxSFMapFolder data; + + VBOX_INIT_CALL(&data.callInfo, MAP_FOLDER, pClient); + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(szFolderName); + data.path.u.Pointer.u.linearAddr = (uintptr_t)szFolderName; + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = 0; + + data.delimiter.type = VMMDevHGCMParmType_32bit; + data.delimiter.u.value32 = '/'; + + data.fCaseSensitive.type = VMMDevHGCMParmType_32bit; + data.fCaseSensitive.u.value32 = 1; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) { + *root = data.root.u.value32; + } else if (rc == VERR_NOT_IMPLEMENTED) { + /* try the legacy interface too; temporary to assure backwards compatibility */ + VBoxSFMapFolder_Old OldData; + + VBOX_INIT_CALL(&OldData.callInfo, MAP_FOLDER_OLD, pClient); + + OldData.path.type = VMMDevHGCMParmType_LinAddr; + OldData.path.u.Pointer.size = + ShflStringSizeOfBuffer(szFolderName); + OldData.path.u.Pointer.u.linearAddr = (uintptr_t)szFolderName; + + OldData.root.type = VMMDevHGCMParmType_32bit; + OldData.root.u.value32 = 0; + + OldData.delimiter.type = VMMDevHGCMParmType_32bit; + OldData.delimiter.u.value32 = '/'; + + rc = vboxsf_hgcm_call(&OldData, sizeof(OldData)); + if (rc >= 0) + *root = OldData.root.u.value32; + } + return rc; +} + +int vboxsf_unmap_folder(SHFLROOT root) +{ + VBoxSFUnmapFolder data; + + VBOX_INIT_CALL(&data.callInfo, UNMAP_FOLDER, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_create(SHFLROOT root, PSHFLSTRING pParsedPath, + struct shfl_createparms *pCreateParms) +{ + /** @todo copy buffers to physical or mapped memory. */ + VBoxSFCreate data; + + VBOX_INIT_CALL(&data.callInfo, CREATE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.path.type = VMMDevHGCMParmType_LinAddr; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.parms.type = VMMDevHGCMParmType_LinAddr; + data.parms.u.Pointer.size = sizeof(struct shfl_createparms); + data.parms.u.Pointer.u.linearAddr = (uintptr_t)pCreateParms; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_close(SHFLROOT root, SHFLHANDLE Handle) +{ + VBoxSFClose data; + + VBOX_INIT_CALL(&data.callInfo, CLOSE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = Handle; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_remove(SHFLROOT root, PSHFLSTRING pParsedPath, u32 flags) +{ + VBoxSFRemove data; + + VBOX_INIT_CALL(&data.callInfo, REMOVE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_rename(SHFLROOT root, PSHFLSTRING pSrcPath, PSHFLSTRING pDestPath, + u32 flags) +{ + VBoxSFRename data; + + VBOX_INIT_CALL(&data.callInfo, RENAME, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.src.type = VMMDevHGCMParmType_LinAddr_In; + data.src.u.Pointer.size = ShflStringSizeOfBuffer(pSrcPath); + data.src.u.Pointer.u.linearAddr = (uintptr_t)pSrcPath; + + data.dest.type = VMMDevHGCMParmType_LinAddr_In; + data.dest.u.Pointer.size = ShflStringSizeOfBuffer(pDestPath); + data.dest.u.Pointer.u.linearAddr = (uintptr_t)pDestPath; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_read(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u32 *pcbBuffer, u8 *pBuffer) +{ + int rc; + VBoxSFRead data; + + VBOX_INIT_CALL(&data.callInfo, READ, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *pcbBuffer = data.cb.u.value32; + + return rc; +} + +int vboxsf_write(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u32 *pcbBuffer, u8 *pBuffer) +{ + int rc; + VBoxSFWrite data; + + VBOX_INIT_CALL(&data.callInfo, WRITE, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.buffer.type = VMMDevHGCMParmType_LinAddr_In; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *pcbBuffer = data.cb.u.value32; + + return rc; +} + +int vboxsf_write_physcont(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u32 *pcbBuffer, u64 PhysBuffer) +{ + u32 cPages, cbData, cbToWrite = *pcbBuffer; + HGCMPageListInfo *pPgLst; + VBoxSFWrite *pData; + u32 iPage; + int rc; + + cPages = + PAGE_ALIGN((PhysBuffer & ~PAGE_MASK) + cbToWrite) >> PAGE_SHIFT; + cbData = + sizeof(VBoxSFWrite) + offsetof(HGCMPageListInfo, aPages[cPages]); + pData = kmalloc(cbData, GFP_KERNEL); + if (!pData) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL(&pData->callInfo, WRITE, pClient); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToWrite; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToWrite; + pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite); + + pPgLst = (HGCMPageListInfo *) (pData + 1); + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pPgLst->offFirstPage = (u16)(PhysBuffer & ~PAGE_MASK); + pPgLst->cPages = cPages; + PhysBuffer &= ~(u64) ~ PAGE_MASK; + for (iPage = 0; iPage < cPages; iPage++, PhysBuffer += PAGE_SIZE) + pPgLst->aPages[iPage] = PhysBuffer; + + rc = vboxsf_hgcm_call(pData, cbData); + if (rc >= 0) + *pcbBuffer = pData->cb.u.value32; + + kfree(pData); + return rc; +} + +int vboxsf_flush(SHFLROOT root, SHFLHANDLE hFile) +{ + VBoxSFFlush data; + + VBOX_INIT_CALL(&data.callInfo, FLUSH, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_dirinfo(SHFLROOT root, SHFLHANDLE hFile, PSHFLSTRING ParsedPath, + u32 flags, u32 index, u32 *pcbBuffer, + PSHFLDIRINFO pBuffer, u32 *pcFiles) +{ + int rc; + VBoxSFList data; + + VBOX_INIT_CALL(&data.callInfo, LIST, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = + ParsedPath ? ShflStringSizeOfBuffer(ParsedPath) : 0; + data.path.u.Pointer.u.linearAddr = (uintptr_t)ParsedPath; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = *pcbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + data.resumePoint.type = VMMDevHGCMParmType_32bit; + data.resumePoint.u.value32 = index; + data.cFiles.type = VMMDevHGCMParmType_32bit; + data.cFiles.u.value32 = 0; /* out parameters only */ + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + + *pcbBuffer = data.cb.u.value32; + *pcFiles = data.cFiles.u.value32; + + return rc; +} + +int vboxsf_fsinfo(SHFLROOT root, SHFLHANDLE hFile, u32 flags, + u32 *pcbBuffer, PSHFLDIRINFO pBuffer) +{ + int rc; + VBoxSFInformation data; + + VBOX_INIT_CALL(&data.callInfo, INFORMATION, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = flags; + data.cb.type = VMMDevHGCMParmType_32bit; + data.cb.u.value32 = *pcbBuffer; + data.info.type = VMMDevHGCMParmType_LinAddr; + data.info.u.Pointer.size = *pcbBuffer; + data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + rc = vboxsf_hgcm_call(&data, sizeof(data)); + if (rc >= 0) + *pcbBuffer = data.cb.u.value32; + + return rc; +} + +int vboxsf_lock(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u64 cbSize, u32 fLock) +{ + VBoxSFLock data; + + VBOX_INIT_CALL(&data.callInfo, LOCK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.handle.type = VMMDevHGCMParmType_64bit; + data.handle.u.value64 = hFile; + data.offset.type = VMMDevHGCMParmType_64bit; + data.offset.u.value64 = offset; + data.length.type = VMMDevHGCMParmType_64bit; + data.length.u.value64 = cbSize; + + data.flags.type = VMMDevHGCMParmType_32bit; + data.flags.u.value32 = fLock; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_set_utf8(void) +{ + VBoxGuestHGCMCallInfo callInfo; + + VBOX_INIT_CALL(&callInfo, SET_UTF8, pClient); + + return vboxsf_hgcm_call(&callInfo, sizeof(callInfo)); +} + +int vboxsf_readlink(SHFLROOT root, PSHFLSTRING pParsedPath, u32 cbBuffer, + u8 *pBuffer) +{ + VBoxSFReadLink data; + + VBOX_INIT_CALL(&data.callInfo, READLINK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.path.type = VMMDevHGCMParmType_LinAddr_In; + data.path.u.Pointer.size = ShflStringSizeOfBuffer(pParsedPath); + data.path.u.Pointer.u.linearAddr = (uintptr_t)pParsedPath; + + data.buffer.type = VMMDevHGCMParmType_LinAddr_Out; + data.buffer.u.Pointer.size = cbBuffer; + data.buffer.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_symlink(SHFLROOT root, PSHFLSTRING pNewPath, PSHFLSTRING pOldPath, + struct shfl_fsobjinfo *pBuffer) +{ + VBoxSFSymlink data; + + VBOX_INIT_CALL(&data.callInfo, SYMLINK, pClient); + + data.root.type = VMMDevHGCMParmType_32bit; + data.root.u.value32 = root; + + data.newPath.type = VMMDevHGCMParmType_LinAddr_In; + data.newPath.u.Pointer.size = ShflStringSizeOfBuffer(pNewPath); + data.newPath.u.Pointer.u.linearAddr = (uintptr_t)pNewPath; + + data.oldPath.type = VMMDevHGCMParmType_LinAddr_In; + data.oldPath.u.Pointer.size = ShflStringSizeOfBuffer(pOldPath); + data.oldPath.u.Pointer.u.linearAddr = (uintptr_t)pOldPath; + + data.info.type = VMMDevHGCMParmType_LinAddr_Out; + data.info.u.Pointer.size = sizeof(struct shfl_fsobjinfo); + data.info.u.Pointer.u.linearAddr = (uintptr_t)pBuffer; + + return vboxsf_hgcm_call(&data, sizeof(data)); +} + +int vboxsf_set_symlinks(void) +{ + VBoxGuestHGCMCallInfo callInfo; + + VBOX_INIT_CALL(&callInfo, SET_SYMLINKS, pClient); + + return vboxsf_hgcm_call(&callInfo, sizeof(callInfo)); +} diff --git a/fs/vboxsf/vboxsf_wrappers.h b/fs/vboxsf/vboxsf_wrappers.h new file mode 100644 index 000000000000..987b924a2c28 --- /dev/null +++ b/fs/vboxsf/vboxsf_wrappers.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * Protype declarations for the wrapper functions for the shfl host calls. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, 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 <linux/vboxguest.h> /* 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(SHFLMAPPING paMappings[], u32 *pcMappings); +int vboxsf_query_mapname(SHFLROOT root, SHFLSTRING *pString, 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 IPRT status code, but see note below + * @param pClient Host-guest communication connection + * @param root Root of the shared folder in which to create the file + * @param pParsedPath The path of the file or folder relative to the shared + * folder + * @param pCreateParms Parameters for file/folder creation. See the + * structure description in shflsvc.h + * @retval pCreateParms 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, pCreateParms->Handle will be set to + * SHFL_HANDLE_NIL on return. In this case the value in + * pCreateParms->Result provides information as to why (e.g. + * SHFL_FILE_EXISTS). pCreateParms->Result is also set on success + * as additional information. + */ +int vboxsf_create(SHFLROOT root, PSHFLSTRING pParsedPath, + struct shfl_createparms *pCreateParms); + +int vboxsf_close(SHFLROOT root, SHFLHANDLE Handle); +int vboxsf_remove(SHFLROOT root, PSHFLSTRING pParsedPath, u32 flags); +int vboxsf_rename(SHFLROOT root, PSHFLSTRING pSrcPath, PSHFLSTRING pDestPath, + u32 flags); +int vboxsf_flush(SHFLROOT root, SHFLHANDLE hFile); + +int vboxsf_read(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u32 *pcbBuffer, u8 *pBuffer); +int vboxsf_write(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u32 *pcbBuffer, u8 *pBuffer); +int vboxsf_write_physcont(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u32 *pcbBuffer, u64 PhysBuffer); + +int vboxsf_lock(SHFLROOT root, SHFLHANDLE hFile, u64 offset, + u64 cbSize, u32 fLock); + +int vboxsf_dirinfo(SHFLROOT root, SHFLHANDLE hFile, PSHFLSTRING ParsedPath, + u32 flags, u32 index, u32 *pcbBuffer, + PSHFLDIRINFO pBuffer, u32 *pcFiles); +int vboxsf_fsinfo(SHFLROOT root, SHFLHANDLE hFile, u32 flags, + u32 *pcbBuffer, PSHFLDIRINFO pBuffer); + +int vboxsf_map_folder(PSHFLSTRING szFolderName, SHFLROOT *root); +int vboxsf_unmap_folder(SHFLROOT root); + +int vboxsf_readlink(SHFLROOT root, PSHFLSTRING pParsedPath, u32 cbBuffer, + u8 *pBuffer); +int vboxsf_symlink(SHFLROOT root, PSHFLSTRING pNewPath, PSHFLSTRING pOldPath, + struct shfl_fsobjinfo *pBuffer); + +int vboxsf_set_utf8(void); +int vboxsf_set_symlinks(void); + +/** @} */ + +#endif diff --git a/fs/vboxsf/vbsfmount.h b/fs/vboxsf/vbsfmount.h new file mode 100644 index 000000000000..1d5d12bc9166 --- /dev/null +++ b/fs/vboxsf/vbsfmount.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * VirtualBox Guest Shared Folders: mount(2) parameter structure. + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef VBFS_MOUNT_H +#define VBFS_MOUNT_H + +#define MAX_HOST_NAME 256 +#define MAX_NLS_NAME 32 + +/* Linux constraints the size of data mount argument to PAGE_SIZE - 1. */ +struct vbsf_mount_info_old { + char name[MAX_HOST_NAME]; + char nls_name[MAX_NLS_NAME]; + int uid; + int gid; + int ttl; +}; + +#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 { + char nullchar; /* name cannot be '\0' -- we use this field + to distinguish between the old structure + and the new structure */ + 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 */ diff --git a/fs/vboxsf/vfsmod.c b/fs/vboxsf/vfsmod.c new file mode 100644 index 000000000000..49a77e44debe --- /dev/null +++ b/fs/vboxsf/vfsmod.c @@ -0,0 +1,412 @@ +/* + * 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 file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/** + * @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 <linux/module.h> +#include <linux/nls.h> +#include <linux/vbox_utils.h> +#include "vbsfmount.h" +#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; + SHFLSTRING *str_name; + size_t name_len, str_len; + struct sf_glob_info *sf_g; +#ifdef CONFIG_NLS_DEFAULT + const char *nls_name = CONFIG_NLS_DEFAULT; +#else + const char *nls_name = ""; +#endif + + sf_g = kzalloc(sizeof(*sf_g), GFP_KERNEL); + if (!sf_g) { + err = -ENOMEM; + goto fail0; + } + + 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) { + /* An old version of mount.vboxsf made the syscall. Translate the + * old parameters to the new structure. */ + struct vbsf_mount_info_old *info_old = + (struct vbsf_mount_info_old *)info; + static struct vbsf_mount_info_new info_compat; + + info = &info_compat; + memset(info, 0, sizeof(*info)); + memcpy(&info->name, &info_old->name, MAX_HOST_NAME); + memcpy(&info->nls_name, &info_old->nls_name, MAX_NLS_NAME); + info->length = offsetof(struct vbsf_mount_info_new, dmode); + info->uid = info_old->uid; + info->gid = info_old->gid; + info->ttl = info_old->ttl; + } + + info->name[sizeof(info->name) - 1] = 0; + info->nls_name[sizeof(info->nls_name) - 1] = 0; + + name_len = strlen(info->name); + if (name_len > 0xfffe) { + err = -ENAMETOOLONG; + goto fail1; + } + + str_len = offsetof(SHFLSTRING, String.utf8) + name_len + 1; + str_name = kmalloc(str_len, GFP_KERNEL); + if (!str_name) { + err = -ENOMEM; + goto fail1; + } + + str_name->u16Length = name_len; + str_name->u16Size = 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 fail1; + } + } else { + sf_g->nls = NULL; + } + + rc = vboxsf_map_folder(str_name, &sf_g->root); + kfree(str_name); + + if (rc < 0) { + err = -EPROTO; + goto fail2; + } + + sf_g->ttl = info->ttl; + sf_g->uid = info->uid; + sf_g->gid = info->gid; + + if ((unsigned)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; + +fail2: + if (sf_g->nls) + unload_nls(sf_g->nls); + +fail1: + kfree(sf_g); + +fail0: + return err; +} + +/* unmap the share and free global info [sf_g] */ +static void sf_glob_free(struct sf_glob_info *sf_g) +{ + 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 -ENOSYS; + + err = sf_glob_alloc(info, &sf_g); + if (err) + goto fail0; + + sf_i = kmalloc(sizeof(*sf_i), GFP_KERNEL); + if (!sf_i) { + err = -ENOMEM; + goto fail1; + } + + sf_i->handle = SHFL_HANDLE_NIL; + sf_i->path = kmalloc(sizeof(SHFLSTRING) + 1, GFP_KERNEL); + if (!sf_i->path) { + err = -ENOMEM; + goto fail2; + } + + sf_i->path->u16Length = 1; + sf_i->path->u16Size = 2; + sf_i->path->String.utf8[0] = '/'; + sf_i->path->String.utf8[1] = 0; + sf_i->force_reread = 0; + + err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0); + if (err) + 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 -ENOSYS; + + 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 = 0; +module_param(follow_symlinks, int, 0); +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..ebfdaffb46d4 --- /dev/null +++ b/fs/vboxsf/vfsmod.h @@ -0,0 +1,111 @@ +/* + * VirtualBox Guest Shared Folders support: module header. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef VFSMOD_H +#define VFSMOD_H + +#include <linux/backing-dev.h> +#include <linux/version.h> +#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 */ + SHFLSTRING *path; + /* some information was changed, update data on next revalidate */ + int force_restat; + /* directory content changed, update the whole directory on next sf_getdent */ + int force_reread; + /* file structure, only valid between open() and release() */ + struct file *file; + /* handle valid if a file was created with sf_create_aux until it will + * be opened with sf_reg_open() */ + SHFLHANDLE handle; +}; + +struct sf_dir_info { + struct list_head info_list; +}; + +struct sf_dir_buf { + size_t cEntries; + size_t cbFree; + size_t cbUsed; + void *buf; + struct list_head head; +}; + +struct sf_reg_info { + SHFLHANDLE handle; +}; + +/* globals */ +extern struct inode_operations sf_dir_iops; +extern struct inode_operations sf_lnk_iops; +extern struct inode_operations sf_reg_iops; +extern struct file_operations sf_dir_fops; +extern struct file_operations sf_reg_fops; +extern struct dentry_operations sf_dentry_ops; +extern struct address_space_operations sf_reg_aops; + +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, + SHFLSTRING * 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, SHFLSTRING ** 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 -- 2.13.3