diff options
Diffstat (limited to 'drivers/staging/vboxsf/utils.c')
-rw-r--r-- | drivers/staging/vboxsf/utils.c | 551 |
1 files changed, 0 insertions, 551 deletions
diff --git a/drivers/staging/vboxsf/utils.c b/drivers/staging/vboxsf/utils.c deleted file mode 100644 index 34a49e6f74fc..000000000000 --- a/drivers/staging/vboxsf/utils.c +++ /dev/null @@ -1,551 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * VirtualBox Guest Shared Folders support: Utility functions. - * Mainly conversion from/to VirtualBox/Linux data structures. - * - * Copyright (C) 2006-2018 Oracle Corporation - */ - -#include <linux/namei.h> -#include <linux/nls.h> -#include <linux/sizes.h> -#include <linux/vfs.h> -#include "vfsmod.h" - -struct inode *vboxsf_new_inode(struct super_block *sb) -{ - struct vboxsf_sbi *sbi = VBOXSF_SBI(sb); - struct inode *inode; - unsigned long flags; - int cursor, ret; - u32 gen; - - inode = new_inode(sb); - if (!inode) - return ERR_PTR(-ENOMEM); - - idr_preload(GFP_KERNEL); - spin_lock_irqsave(&sbi->ino_idr_lock, flags); - cursor = idr_get_cursor(&sbi->ino_idr); - ret = idr_alloc_cyclic(&sbi->ino_idr, inode, 1, 0, GFP_ATOMIC); - if (ret >= 0 && ret < cursor) - sbi->next_generation++; - gen = sbi->next_generation; - spin_unlock_irqrestore(&sbi->ino_idr_lock, flags); - idr_preload_end(); - - if (ret < 0) { - iput(inode); - return ERR_PTR(ret); - } - - inode->i_ino = ret; - inode->i_generation = gen; - return inode; -} - -/* set [inode] attributes based on [info], uid/gid based on [sbi] */ -void vboxsf_init_inode(struct vboxsf_sbi *sbi, struct inode *inode, - const struct shfl_fsobjinfo *info) -{ - const struct shfl_fsobjattr *attr; - s64 allocated; - int mode; - - attr = &info->attr; - -#define mode_set(r) ((attr->mode & (SHFL_UNIX_##r)) ? (S_##r) : 0) - - mode = mode_set(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 - - /* We use the host-side values for these */ - inode->i_flags |= S_NOATIME | S_NOCMTIME; - inode->i_mapping->a_ops = &vboxsf_reg_aops; - - if (SHFL_IS_DIRECTORY(attr->mode)) { - inode->i_mode = sbi->o.dmode_set ? sbi->o.dmode : mode; - inode->i_mode &= ~sbi->o.dmask; - inode->i_mode |= S_IFDIR; - inode->i_op = &vboxsf_dir_iops; - inode->i_fop = &vboxsf_dir_fops; - /* - * XXX: this probably should be set to the number of entries - * in the directory plus two (. ..) - */ - set_nlink(inode, 1); - } else if (SHFL_IS_SYMLINK(attr->mode)) { - inode->i_mode = sbi->o.fmode_set ? sbi->o.fmode : mode; - inode->i_mode &= ~sbi->o.fmask; - inode->i_mode |= S_IFLNK; - inode->i_op = &vboxsf_lnk_iops; - set_nlink(inode, 1); - } else { - inode->i_mode = sbi->o.fmode_set ? sbi->o.fmode : mode; - inode->i_mode &= ~sbi->o.fmask; - inode->i_mode |= S_IFREG; - inode->i_op = &vboxsf_reg_iops; - inode->i_fop = &vboxsf_reg_fops; - set_nlink(inode, 1); - } - - inode->i_uid = sbi->o.uid; - inode->i_gid = sbi->o.gid; - - inode->i_size = info->size; - inode->i_blkbits = 12; - /* i_blocks always in units of 512 bytes! */ - allocated = info->allocated + 511; - do_div(allocated, 512); - inode->i_blocks = allocated; - - inode->i_atime = ns_to_timespec64( - info->access_time.ns_relative_to_unix_epoch); - inode->i_ctime = ns_to_timespec64( - info->change_time.ns_relative_to_unix_epoch); - inode->i_mtime = ns_to_timespec64( - info->modification_time.ns_relative_to_unix_epoch); -} - -int vboxsf_create_at_dentry(struct dentry *dentry, - struct shfl_createparms *params) -{ - struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb); - struct shfl_string *path; - int err; - - path = vboxsf_path_from_dentry(sbi, dentry); - if (IS_ERR(path)) - return PTR_ERR(path); - - err = vboxsf_create(sbi->root, path, params); - __putname(path); - - return err; -} - -int vboxsf_stat(struct vboxsf_sbi *sbi, struct shfl_string *path, - struct shfl_fsobjinfo *info) -{ - struct shfl_createparms params = {}; - int err; - - params.handle = SHFL_HANDLE_NIL; - params.create_flags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW; - - err = vboxsf_create(sbi->root, path, ¶ms); - if (err) - return err; - - if (params.result != SHFL_FILE_EXISTS) - return -ENOENT; - - if (info) - *info = params.info; - - return 0; -} - -int vboxsf_stat_dentry(struct dentry *dentry, struct shfl_fsobjinfo *info) -{ - struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb); - struct shfl_string *path; - int err; - - path = vboxsf_path_from_dentry(sbi, dentry); - if (IS_ERR(path)) - return PTR_ERR(path); - - err = vboxsf_stat(sbi, path, info); - __putname(path); - return err; -} - -int vboxsf_inode_revalidate(struct dentry *dentry) -{ - struct vboxsf_sbi *sbi; - struct vboxsf_inode *sf_i; - struct shfl_fsobjinfo info; - struct timespec64 prev_mtime; - struct inode *inode; - int err; - - if (!dentry || !d_really_is_positive(dentry)) - return -EINVAL; - - inode = d_inode(dentry); - prev_mtime = inode->i_mtime; - sf_i = VBOXSF_I(inode); - sbi = VBOXSF_SBI(dentry->d_sb); - if (!sf_i->force_restat) { - if (time_before(jiffies, dentry->d_time + sbi->o.ttl)) - return 0; - } - - err = vboxsf_stat_dentry(dentry, &info); - if (err) - return err; - - dentry->d_time = jiffies; - sf_i->force_restat = 0; - vboxsf_init_inode(sbi, inode, &info); - - /* - * If the file was changed on the host side we need to invalidate the - * page-cache for it. Note this also gets triggered by our own writes, - * this is unavoidable. - */ - if (timespec64_compare(&inode->i_mtime, &prev_mtime) > 0) - invalidate_inode_pages2(inode->i_mapping); - - return 0; -} - -int vboxsf_getattr(const struct path *path, struct kstat *kstat, - u32 request_mask, unsigned int flags) -{ - int err; - struct dentry *dentry = path->dentry; - struct inode *inode = d_inode(dentry); - struct vboxsf_inode *sf_i = VBOXSF_I(inode); - - switch (flags & AT_STATX_SYNC_TYPE) { - case AT_STATX_DONT_SYNC: - err = 0; - break; - case AT_STATX_FORCE_SYNC: - sf_i->force_restat = 1; - /* fall-through */ - default: - err = vboxsf_inode_revalidate(dentry); - } - if (err) - return err; - - generic_fillattr(d_inode(dentry), kstat); - return 0; -} - -int vboxsf_setattr(struct dentry *dentry, struct iattr *iattr) -{ - struct vboxsf_inode *sf_i = VBOXSF_I(d_inode(dentry)); - struct vboxsf_sbi *sbi = VBOXSF_SBI(dentry->d_sb); - struct shfl_createparms params = {}; - struct shfl_fsobjinfo info = {}; - u32 buf_len; - int err; - - params.handle = SHFL_HANDLE_NIL; - params.create_flags = SHFL_CF_ACT_OPEN_IF_EXISTS | - SHFL_CF_ACT_FAIL_IF_NEW | - SHFL_CF_ACCESS_ATTR_WRITE; - - /* this is at least required for Posix hosts */ - if (iattr->ia_valid & ATTR_SIZE) - params.create_flags |= SHFL_CF_ACCESS_WRITE; - - err = vboxsf_create_at_dentry(dentry, ¶ms); - if (err || params.result != SHFL_FILE_EXISTS) - return err ? err : -ENOENT; - -#define mode_set(r) ((iattr->ia_mode & (S_##r)) ? SHFL_UNIX_##r : 0) - - /* - * Setting the file size and setting the other attributes has to - * be handled separately. - */ - if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) { - if (iattr->ia_valid & ATTR_MODE) { - info.attr.mode = mode_set(IRUSR); - info.attr.mode |= mode_set(IWUSR); - info.attr.mode |= mode_set(IXUSR); - info.attr.mode |= mode_set(IRGRP); - info.attr.mode |= mode_set(IWGRP); - info.attr.mode |= mode_set(IXGRP); - info.attr.mode |= mode_set(IROTH); - info.attr.mode |= mode_set(IWOTH); - info.attr.mode |= mode_set(IXOTH); - - if (iattr->ia_mode & S_IFDIR) - info.attr.mode |= SHFL_TYPE_DIRECTORY; - else - info.attr.mode |= SHFL_TYPE_FILE; - } - - if (iattr->ia_valid & ATTR_ATIME) - info.access_time.ns_relative_to_unix_epoch = - timespec64_to_ns(&iattr->ia_atime); - - if (iattr->ia_valid & ATTR_MTIME) - info.modification_time.ns_relative_to_unix_epoch = - timespec64_to_ns(&iattr->ia_mtime); - - /* - * Ignore ctime (inode change time) as it can't be set - * from userland anyway. - */ - - buf_len = sizeof(info); - err = vboxsf_fsinfo(sbi->root, params.handle, - SHFL_INFO_SET | SHFL_INFO_FILE, &buf_len, - &info); - if (err) { - vboxsf_close(sbi->root, params.handle); - return err; - } - - /* the host may have given us different attr then requested */ - sf_i->force_restat = 1; - } - -#undef mode_set - - if (iattr->ia_valid & ATTR_SIZE) { - memset(&info, 0, sizeof(info)); - info.size = iattr->ia_size; - buf_len = sizeof(info); - err = vboxsf_fsinfo(sbi->root, params.handle, - SHFL_INFO_SET | SHFL_INFO_SIZE, &buf_len, - &info); - if (err) { - vboxsf_close(sbi->root, params.handle); - return err; - } - - /* the host may have given us different attr then requested */ - sf_i->force_restat = 1; - } - - vboxsf_close(sbi->root, params.handle); - - /* Update the inode with what the host has actually given us. */ - if (sf_i->force_restat) - vboxsf_inode_revalidate(dentry); - - return 0; -} - -/* - * [dentry] contains string encoded in coding system that corresponds - * to [sbi]->nls, we must convert it to UTF8 here. - * Returns a shfl_string allocated through __getname (must be freed using - * __putname), or an ERR_PTR on error. - */ -struct shfl_string *vboxsf_path_from_dentry(struct vboxsf_sbi *sbi, - struct dentry *dentry) -{ - struct shfl_string *shfl_path; - int path_len, out_len, nb; - char *buf, *path; - wchar_t uni; - u8 *out; - - buf = __getname(); - if (!buf) - return ERR_PTR(-ENOMEM); - - path = dentry_path_raw(dentry, buf, PATH_MAX); - if (IS_ERR(path)) { - __putname(buf); - return (struct shfl_string *)path; - } - path_len = strlen(path); - - if (sbi->nls) { - shfl_path = __getname(); - if (!shfl_path) { - __putname(buf); - return ERR_PTR(-ENOMEM); - } - - out = shfl_path->string.utf8; - out_len = PATH_MAX - SHFLSTRING_HEADER_SIZE - 1; - - while (path_len) { - nb = sbi->nls->char2uni(path, path_len, &uni); - if (nb < 0) { - __putname(shfl_path); - __putname(buf); - return ERR_PTR(-EINVAL); - } - path += nb; - path_len -= nb; - - nb = utf32_to_utf8(uni, out, out_len); - if (nb < 0) { - __putname(shfl_path); - __putname(buf); - return ERR_PTR(-ENAMETOOLONG); - } - out += nb; - out_len -= nb; - } - *out = 0; - shfl_path->length = out - shfl_path->string.utf8; - shfl_path->size = shfl_path->length + 1; - __putname(buf); - } else { - if ((SHFLSTRING_HEADER_SIZE + path_len + 1) > PATH_MAX) { - __putname(buf); - return ERR_PTR(-ENAMETOOLONG); - } - /* - * dentry_path stores the name at the end of buf, but the - * shfl_string string we return must be properly aligned. - */ - shfl_path = (struct shfl_string *)buf; - memmove(shfl_path->string.utf8, path, path_len); - shfl_path->string.utf8[path_len] = 0; - shfl_path->length = path_len; - shfl_path->size = path_len + 1; - } - - return shfl_path; -} - -int vboxsf_nlscpy(struct vboxsf_sbi *sbi, char *name, size_t name_bound_len, - const unsigned char *utf8_name, size_t utf8_len) -{ - 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; - /* Reserve space for terminating 0 */ - out_bound_len = name_bound_len - 1; - - 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 = sbi->nls->uni2char(uni, out, out_bound_len); - if (nb < 0) - return nb; - - out += nb; - out_bound_len -= nb; - out_len += nb; - } - - *out = 0; - - return 0; -} - -static struct vboxsf_dir_buf *vboxsf_dir_buf_alloc(struct list_head *list) -{ - struct vboxsf_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; - } - - b->entries = 0; - b->used = 0; - b->free = DIR_BUFFER_SIZE; - list_add(&b->head, list); - - return b; -} - -static void vboxsf_dir_buf_free(struct vboxsf_dir_buf *b) -{ - list_del(&b->head); - kfree(b->buf); - kfree(b); -} - -struct vboxsf_dir_info *vboxsf_dir_info_alloc(void) -{ - struct vboxsf_dir_info *p; - - p = kmalloc(sizeof(*p), GFP_KERNEL); - if (!p) - return NULL; - - INIT_LIST_HEAD(&p->info_list); - return p; -} - -void vboxsf_dir_info_free(struct vboxsf_dir_info *p) -{ - struct list_head *list, *pos, *tmp; - - list = &p->info_list; - list_for_each_safe(pos, tmp, list) { - struct vboxsf_dir_buf *b; - - b = list_entry(pos, struct vboxsf_dir_buf, head); - vboxsf_dir_buf_free(b); - } - kfree(p); -} - -int vboxsf_dir_read_all(struct vboxsf_sbi *sbi, struct vboxsf_dir_info *sf_d, - u64 handle) -{ - struct vboxsf_dir_buf *b; - u32 entries, size; - int err = 0; - void *buf; - - /* vboxsf_dirinfo returns 1 on end of dir */ - while (err == 0) { - b = vboxsf_dir_buf_alloc(&sf_d->info_list); - if (!b) { - err = -ENOMEM; - break; - } - - buf = b->buf; - size = b->free; - - err = vboxsf_dirinfo(sbi->root, handle, NULL, 0, 0, - &size, buf, &entries); - if (err < 0) - break; - - b->entries += entries; - b->free -= size; - b->used += size; - } - - if (b && b->used == 0) - vboxsf_dir_buf_free(b); - - /* -EILSEQ means the host could not translate a filename, ignore */ - if (err > 0 || err == -EILSEQ) - err = 0; - - return err; -} |