/** * 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 * @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) { int err = 0, rc = VINF_SUCCESS; struct sf_glob_info *sf_g = GET_GLOB_INFO(old_parent->i_sb); TRACE(); if (sf_g != GET_GLOB_INFO(new_parent->i_sb)) { LogFunc(("rename with different roots\n")); err = -EINVAL; } else { struct sf_inode_info *sf_old_i = GET_INODE_INFO(old_parent); struct sf_inode_info *sf_new_i = GET_INODE_INFO(new_parent); /* As we save the relative path inside the inode structure, we need to change this if the rename is successful. */ struct sf_inode_info *sf_file_i = GET_INODE_INFO(old_dentry->d_inode); SHFLSTRING *old_path; SHFLSTRING *new_path; BUG_ON(!sf_old_i); BUG_ON(!sf_new_i); BUG_ON(!sf_file_i); old_path = sf_file_i->path; err = sf_path_from_dentry(__func__, sf_g, sf_new_i, new_dentry, &new_path); if (err) LogFunc(("failed to create new path\n")); else { int fDir = ((old_dentry->d_inode->i_mode & S_IFDIR) != 0); rc = vboxCallRename(&client_handle, &sf_g->map, old_path, new_path, fDir ? 0 : SHFL_RENAME_FILE | SHFL_RENAME_REPLACE_IF_EXISTS); if (RT_SUCCESS(rc)) { kfree(old_path); sf_new_i->force_restat = 1; sf_old_i->force_restat = 1; /* XXX: needed? */ /* Set the new relative path in the inode. */ sf_file_i->path = new_path; } else { LogFunc(("vboxCallRename failed rc=%Rrc\n", rc)); err = -RTErrConvertToErrno(rc); kfree(new_path); } } } return err; }
int sf_get_volume_info(struct super_block *sb, STRUCT_STATFS *stat) { struct sf_glob_info *sf_g; SHFLVOLINFO SHFLVolumeInfo; uint32_t cbBuffer; int rc; sf_g = GET_GLOB_INFO(sb); cbBuffer = sizeof(SHFLVolumeInfo); rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, 0, SHFL_INFO_GET | SHFL_INFO_VOLUME, &cbBuffer, (PSHFLDIRINFO)&SHFLVolumeInfo); if (RT_FAILURE(rc)) return -RTErrConvertToErrno(rc); stat->f_type = NFS_SUPER_MAGIC; /* XXX vboxsf type? */ stat->f_bsize = SHFLVolumeInfo.ulBytesPerAllocationUnit; stat->f_blocks = SHFLVolumeInfo.ullTotalAllocationBytes / SHFLVolumeInfo.ulBytesPerAllocationUnit; stat->f_bfree = SHFLVolumeInfo.ullAvailableAllocationBytes / SHFLVolumeInfo.ulBytesPerAllocationUnit; stat->f_bavail = SHFLVolumeInfo.ullAvailableAllocationBytes / SHFLVolumeInfo.ulBytesPerAllocationUnit; stat->f_files = 1000; stat->f_ffree = 1000; /* don't return 0 here since the guest may think * that it is not possible to create any more files */ stat->f_fsid.val[0] = 0; stat->f_fsid.val[1] = 0; stat->f_namelen = 255; return 0; }
static void *sf_follow_link(struct dentry *dentry, struct nameidata *nd) # endif { struct inode *inode = dentry->d_inode; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_inode_info *sf_i = GET_INODE_INFO(inode); int error = -ENOMEM; char *path = (char*)get_zeroed_page(GFP_KERNEL); int rc; if (path) { error = 0; rc = vboxReadLink(&client_handle, &sf_g->map, sf_i->path, PATH_MAX, path); if (RT_FAILURE(rc)) { LogFunc(("vboxReadLink failed, caller=%s, rc=%Rrc\n", __func__, rc)); free_page((unsigned long)path); error = -EPROTO; } } # if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) return error ? ERR_PTR(error) : (*cookie = path); # else nd_set_link(nd, error ? ERR_PTR(error) : path); return NULL; # endif }
/** * Close a regular file. * * @param inode the inode * @param file the file * @returns 0 on success, Linux error code otherwise */ static int sf_reg_release(struct inode *inode, struct file *file) { int rc; struct sf_reg_info *sf_r; struct sf_glob_info *sf_g; struct sf_inode_info *sf_i = GET_INODE_INFO(inode); TRACE(); sf_g = GET_GLOB_INFO(inode->i_sb); sf_r = file->private_data; BUG_ON(!sf_g); BUG_ON(!sf_r); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25) /* See the smbfs source (file.c). mmap in particular can cause data to be * written to the file after it is closed, which we can't cope with. We * copy and paste the body of filemap_write_and_wait() here as it was not * defined before 2.6.6 and not exported until quite a bit later. */ /* filemap_write_and_wait(inode->i_mapping); */ if ( inode->i_mapping->nrpages && filemap_fdatawrite(inode->i_mapping) != -EIO) filemap_fdatawait(inode->i_mapping); #endif rc = vboxCallClose(&client_handle, &sf_g->map, sf_r->handle); if (RT_FAILURE(rc)) LogFunc(("vboxCallClose failed rc=%Rrc\n", rc)); kfree(sf_r); sf_i->file = NULL; sf_i->handle = SHFL_HANDLE_NIL; file->private_data = NULL; return 0; }
static int sf_readpage(struct file *file, struct page *page) { struct inode *inode = GET_F_DENTRY(file)->d_inode; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_reg_info *sf_r = file->private_data; uint32_t nread = PAGE_SIZE; char *buf; loff_t off = ((loff_t)page->index) << PAGE_SHIFT; int ret; TRACE(); buf = kmap(page); ret = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off); if (ret) { kunmap(page); if (PageLocked(page)) unlock_page(page); return ret; } BUG_ON(nread > PAGE_SIZE); memset(&buf[nread], 0, PAGE_SIZE - nread); flush_dcache_page(page); kunmap(page); SetPageUptodate(page); unlock_page(page); return 0; }
int sf_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned copied, struct page *page, void *fsdata) { struct inode *inode = mapping->host; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_reg_info *sf_r = file->private_data; void *buf; unsigned from = pos & (PAGE_SIZE - 1); uint32_t nwritten = len; int err; TRACE(); buf = kmap(page); err = sf_reg_write_aux(__func__, sf_g, sf_r, buf+from, &nwritten, pos); kunmap(page); if (!PageUptodate(page) && err == PAGE_SIZE) SetPageUptodate(page); if (err >= 0) { pos += nwritten; if (pos > inode->i_size) inode->i_size = pos; } unlock_page(page); page_cache_release(page); return nwritten; }
/* vfs is done with [sb] (umount called) call [sf_glob_free] to unmap the folder and free [sf_g] */ static void sf_put_super(struct super_block *sb) { struct sf_glob_info *sf_g; sf_g = GET_GLOB_INFO(sb); BUG_ON(!sf_g); sf_done_backing_dev(sf_g); sf_glob_free(sf_g); }
/** * This should allocate memory for sf_inode_info, compute a unique inode * number, get an inode from vfs, initialize inode info, instantiate * dentry. * * @param parent inode entry of the directory * @param dentry directory cache entry * @param path path name * @param info file information * @param handle handle * @returns 0 on success, Linux error code otherwise */ static int sf_instantiate(struct inode *parent, struct dentry *dentry, SHFLSTRING *path, PSHFLFSOBJINFO info, SHFLHANDLE handle) { int err; ino_t ino; struct inode *inode; struct sf_inode_info *sf_new_i; struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb); TRACE(); BUG_ON(!sf_g); sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL); if (!sf_new_i) { LogRelFunc(("could not allocate inode info.\n")); err = -ENOMEM; goto fail0; } ino = iunique(parent->i_sb, 1); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25) inode = iget_locked(parent->i_sb, ino); #else inode = iget(parent->i_sb, ino); #endif if (!inode) { LogFunc(("iget failed\n")); err = -ENOMEM; goto fail1; } sf_init_inode(sf_g, inode, info); sf_new_i->path = path; SET_INODE_INFO(inode, sf_new_i); sf_new_i->force_restat = 1; sf_new_i->force_reread = 0; d_instantiate(dentry, inode); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25) unlock_new_inode(inode); #endif /* Store this handle if we leave the handle open. */ sf_new_i->handle = handle; return 0; fail1: kfree(sf_new_i); fail0: return err; }
static int sf_remount_fs(struct super_block *sb, int *flags, char *data) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 23) struct sf_glob_info *sf_g; struct vbsf_mount_info_new *info; struct sf_inode_info *sf_i; struct inode *iroot; SHFLFSOBJINFO fsinfo; int err; printk(KERN_DEBUG "ENTER: sf_remount_fs\n"); sf_g = GET_GLOB_INFO(sb); BUG_ON(!sf_g); BUG_ON(data[0] != 0); info = (struct vbsf_mount_info_new *)data; BUG_ON( 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) { printk(KERN_DEBUG "can't find root inode\n"); return -ENOSYS; } sf_i = GET_INODE_INFO(iroot); err = sf_stat(__func__, sf_g, sf_i->path, &fsinfo, 0); BUG_ON(err != 0); sf_init_inode(sf_g, iroot, &fsinfo); /*unlock_new_inode(iroot);*/ printk(KERN_DEBUG "LEAVE: sf_remount_fs\n"); return 0; #else return -ENOSYS; #endif }
static int sf_remount_fs(struct super_block *sb, int *flags, char *data) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 23) struct sf_glob_info *sf_g; struct sf_inode_info *sf_i; struct inode *iroot; SHFLFSOBJINFO fsinfo; int err; sf_g = GET_GLOB_INFO(sb); BUG_ON(!sf_g); if (data && data[0] != 0) { struct vbsf_mount_info_new *info = (struct vbsf_mount_info_new *)data; if ( info->signature[0] == VBSF_MOUNT_SIGNATURE_BYTE_0 && info->signature[1] == VBSF_MOUNT_SIGNATURE_BYTE_1 && info->signature[2] == VBSF_MOUNT_SIGNATURE_BYTE_2) { sf_g->uid = info->uid; sf_g->gid = info->gid; sf_g->ttl = info->ttl; 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); BUG_ON(err != 0); sf_init_inode(sf_g, iroot, &fsinfo); /*unlock_new_inode(iroot);*/ return 0; #else return -ENOSYS; #endif }
/** * Remove a regular file / directory. * * @param parent inode of the directory * @param dentry directory cache entry * @param fDirectory true if directory, false otherwise * @returns 0 on success, Linux error code otherwise */ static int sf_unlink_aux(struct inode *parent, struct dentry *dentry, int fDirectory) { int rc, err; struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb); struct sf_inode_info *sf_i = GET_INODE_INFO(parent); SHFLSTRING *path; uint32_t fFlags; TRACE(); BUG_ON(!sf_g); err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); if (err) goto fail0; fFlags = fDirectory ? SHFL_REMOVE_DIR : SHFL_REMOVE_FILE; if ( dentry && dentry->d_inode && ((dentry->d_inode->i_mode & S_IFLNK) == S_IFLNK)) fFlags |= SHFL_REMOVE_SYMLINK; rc = vboxCallRemove(&client_handle, &sf_g->map, path, fFlags); if (RT_FAILURE(rc)) { LogFunc(("(%d): vboxCallRemove(%s) failed rc=%Rrc\n", fDirectory, path->String.utf8, rc)); err = -RTErrConvertToErrno(rc); goto fail1; } /* directory access/change time changed */ sf_i->force_restat = 1; /* directory content changed */ sf_i->force_reread = 1; err = 0; fail1: kfree(path); fail0: return err; }
static int sf_writepage(struct page *page, struct writeback_control *wbc) { struct address_space *mapping = page->mapping; struct inode *inode = mapping->host; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_inode_info *sf_i = GET_INODE_INFO(inode); struct file *file = sf_i->file; struct sf_reg_info *sf_r = file->private_data; char *buf; uint32_t nwritten = PAGE_SIZE; int end_index = inode->i_size >> PAGE_SHIFT; loff_t off = ((loff_t) page->index) << PAGE_SHIFT; int err; TRACE(); if (page->index >= end_index) nwritten = inode->i_size & (PAGE_SIZE-1); buf = kmap(page); err = sf_reg_write_aux(__func__, sf_g, sf_r, buf, &nwritten, off); if (err < 0) { ClearPageUptodate(page); goto out; } if (off > inode->i_size) inode->i_size = off; if (PageError(page)) ClearPageError(page); err = 0; out: kunmap(page); unlock_page(page); return err; }
/* this is called directly as iop on 2.4, indirectly as dop [sf_dentry_revalidate] on 2.4/2.6, indirectly as iop through [sf_getattr] on 2.6. the job is to find out whether dentry/inode is still valid. the test is failed if [dentry] does not have an inode or [sf_stat] is unsuccessful, otherwise we return success and update inode attributes */ int sf_inode_revalidate(struct dentry *dentry) { int err; struct sf_glob_info *sf_g; struct sf_inode_info *sf_i; SHFLFSOBJINFO info; TRACE(); if (!dentry || !dentry->d_inode) { LogFunc(("no dentry(%p) or inode(%p)\n", dentry, dentry->d_inode)); return -EINVAL; } sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb); sf_i = GET_INODE_INFO(dentry->d_inode); #if 0 printk("%s called by %p:%p\n", sf_i->path->String.utf8, __builtin_return_address (0), __builtin_return_address (1)); #endif BUG_ON(!sf_g); BUG_ON(!sf_i); if (!sf_i->force_restat) { if (jiffies - dentry->d_time < sf_g->ttl) return 0; } err = sf_stat(__func__, sf_g, sf_i->path, &info, 1); if (err) return err; dentry->d_time = jiffies; sf_init_inode(sf_g, dentry->d_inode, &info); return 0; }
/* * Copyright (C) 2010-2011 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" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) # if LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0) # if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) static const char *sf_follow_link(struct dentry *dentry, void **cookie) # else static void *sf_follow_link(struct dentry *dentry, struct nameidata *nd) # endif { struct inode *inode = dentry->d_inode; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_inode_info *sf_i = GET_INODE_INFO(inode); int error = -ENOMEM; char *path = (char*)get_zeroed_page(GFP_KERNEL); int rc; if (path) { error = 0; rc = VbglR0SfReadLink(&client_handle, &sf_g->map, sf_i->path, PATH_MAX, path); if (RT_FAILURE(rc)) { LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n", __func__, rc)); free_page((unsigned long)path); error = -EPROTO; } } # if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) return error ? ERR_PTR(error) : (*cookie = path); # else nd_set_link(nd, error ? ERR_PTR(error) : path); return NULL; # endif } # if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) static void sf_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) { char *page = nd_get_link(nd); if (!IS_ERR(page)) free_page((unsigned long)page); } # endif # else /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) */ static const char *sf_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_inode_info *sf_i = GET_INODE_INFO(inode); char *path; int rc; if (!dentry) return ERR_PTR(-ECHILD); path = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!path) return ERR_PTR(-ENOMEM); rc = VbglR0SfReadLink(&client_handle, &sf_g->map, sf_i->path, PATH_MAX, path); if (RT_FAILURE(rc)) { LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n", __func__, rc)); kfree(path); return ERR_PTR(-EPROTO); } set_delayed_call(done, kfree_link, path); return path; }
/* filemap_write_and_wait(inode->i_mapping); */ if ( inode->i_mapping->nrpages && filemap_fdatawrite(inode->i_mapping) != -EIO) filemap_fdatawait(inode->i_mapping); #endif rc = vboxCallClose(&client_handle, &sf_g->map, sf_r->handle); if (RT_FAILURE(rc)) LogFunc(("vboxCallClose failed rc=%Rrc\n", rc)); kfree(sf_r); sf_i->file = NULL; sf_i->handle = SHFL_HANDLE_NIL; file->private_data = NULL; return 0; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) static int sf_reg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) static struct page *sf_reg_nopage(struct vm_area_struct *vma, unsigned long vaddr, int *type) # define SET_TYPE(t) *type = (t) #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) */ static struct page *sf_reg_nopage(struct vm_area_struct *vma, unsigned long vaddr, int unused) # define SET_TYPE(t) #endif { struct page *page; char *buf; loff_t off; uint32_t nread = PAGE_SIZE; int err; struct file *file = vma->vm_file; struct inode *inode = GET_F_DENTRY(file)->d_inode; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_reg_info *sf_r = file->private_data; TRACE(); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) if (vmf->pgoff > vma->vm_end) return VM_FAULT_SIGBUS; #else if (vaddr > vma->vm_end) { SET_TYPE(VM_FAULT_SIGBUS); return NOPAGE_SIGBUS; } #endif /* Don't use GFP_HIGHUSER as long as sf_reg_read_aux() calls vboxCallRead() * which works on virtual addresses. On Linux cannot reliably determine the * physical address for high memory, see rtR0MemObjNativeLockKernel(). */ page = alloc_page(GFP_USER); if (!page) { LogRelFunc(("failed to allocate page\n")); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) return VM_FAULT_OOM; #else SET_TYPE(VM_FAULT_OOM); return NOPAGE_OOM; #endif } buf = kmap(page); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) off = (vmf->pgoff << PAGE_SHIFT); #else off = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); #endif err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off); if (err) { kunmap(page); put_page(page); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) return VM_FAULT_SIGBUS; #else SET_TYPE(VM_FAULT_SIGBUS); return NOPAGE_SIGBUS; #endif } BUG_ON (nread > PAGE_SIZE); if (!nread) { #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) clear_user_page(page_address(page), vmf->pgoff, page); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) clear_user_page(page_address(page), vaddr, page); #else clear_user_page(page_address(page), vaddr); #endif } else memset(buf + nread, 0, PAGE_SIZE - nread); flush_dcache_page(page); kunmap(page); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25) vmf->page = page; return 0; #else SET_TYPE(VM_FAULT_MAJOR); return page; #endif }
/** * Extract element ([dir]->f_pos) from the directory [dir] into [d_name]. * * @returns 0 for success, 1 for end reached, Linux error code otherwise. */ static int sf_getdent(struct file *dir, char d_name[NAME_MAX], int *d_type) { loff_t cur; struct sf_glob_info *sf_g; struct sf_dir_info *sf_d; struct sf_inode_info *sf_i; struct inode *inode; struct list_head *pos, *list; TRACE(); inode = GET_F_DENTRY(dir)->d_inode; sf_i = GET_INODE_INFO(inode); sf_g = GET_GLOB_INFO(inode->i_sb); sf_d = dir->private_data; BUG_ON(!sf_g); BUG_ON(!sf_d); BUG_ON(!sf_i); if (sf_i->force_reread) { int rc; int err; SHFLCREATEPARMS params; RT_ZERO(params); params.Handle = SHFL_HANDLE_NIL; params.CreateFlags = 0 | SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ ; LogFunc(("sf_getdent: calling vboxCallCreate, folder %s, flags %#x\n", sf_i->path->String.utf8, params.CreateFlags)); rc = vboxCallCreate(&client_handle, &sf_g->map, sf_i->path, ¶ms); if (RT_FAILURE(rc)) { LogFunc(("vboxCallCreate(%s) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); return -EPERM; } if (params.Result != SHFL_FILE_EXISTS) { LogFunc(("directory %s does not exist\n", sf_i->path->String.utf8)); sf_dir_info_free(sf_d); return -ENOENT; } sf_dir_info_empty(sf_d); err = sf_dir_read_all(sf_g, sf_i, sf_d, params.Handle); rc = vboxCallClose(&client_handle, &sf_g->map, params.Handle); if (RT_FAILURE(rc)) LogFunc(("vboxCallClose(%s) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); if (err) return err; sf_i->force_reread = 0; } cur = 0; list = &sf_d->info_list; list_for_each(pos, list) { struct sf_dir_buf *b; SHFLDIRINFO *info; loff_t i; b = list_entry(pos, struct sf_dir_buf, head); if (dir->f_pos >= cur + b->cEntries) { cur += b->cEntries; continue; } for (i = 0, info = b->buf; i < dir->f_pos - cur; ++i) { size_t size; size = offsetof(SHFLDIRINFO, name.String) + info->name.u16Size; info = (SHFLDIRINFO *) ((uintptr_t) info + size); } *d_type = sf_get_d_type(info->Info.Attr.fMode); return sf_nlscpy(sf_g, d_name, NAME_MAX, info->name.String.utf8, info->name.u16Length); } return 1; }
/** * Open a regular file. * * @param inode the inode * @param file the file * @returns 0 on success, Linux error code otherwise */ static int sf_reg_open(struct inode *inode, struct file *file) { int rc, rc_linux = 0; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_inode_info *sf_i = GET_INODE_INFO(inode); struct sf_reg_info *sf_r; SHFLCREATEPARMS params; TRACE(); BUG_ON(!sf_g); BUG_ON(!sf_i); LogFunc(("open %s\n", sf_i->path->String.utf8)); sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL); if (!sf_r) { LogRelFunc(("could not allocate reg info\n")); return -ENOMEM; } /* Already open? */ if (sf_i->handle != SHFL_HANDLE_NIL) { /* * This inode was created with sf_create_aux(). Check the CreateFlags: * O_CREAT, O_TRUNC: inherent true (file was just created). Not sure * about the access flags (SHFL_CF_ACCESS_*). */ sf_i->force_restat = 1; sf_r->handle = sf_i->handle; sf_i->handle = SHFL_HANDLE_NIL; sf_i->file = file; file->private_data = sf_r; return 0; } RT_ZERO(params); params.Handle = SHFL_HANDLE_NIL; /* We check the value of params.Handle afterwards to find out if * the call succeeded or failed, as the API does not seem to cleanly * distinguish error and informational messages. * * Furthermore, we must set params.Handle to SHFL_HANDLE_NIL to * make the shared folders host service use our fMode parameter */ if (file->f_flags & O_CREAT) { LogFunc(("O_CREAT set\n")); params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW; /* We ignore O_EXCL, as the Linux kernel seems to call create beforehand itself, so O_EXCL should always fail. */ if (file->f_flags & O_TRUNC) { LogFunc(("O_TRUNC set\n")); params.CreateFlags |= ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS | SHFL_CF_ACCESS_WRITE); } else params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS; } else { params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW; if (file->f_flags & O_TRUNC) { LogFunc(("O_TRUNC set\n")); params.CreateFlags |= ( SHFL_CF_ACT_OVERWRITE_IF_EXISTS | SHFL_CF_ACCESS_WRITE); } } if (!(params.CreateFlags & SHFL_CF_ACCESS_READWRITE)) { switch (file->f_flags & O_ACCMODE) { case O_RDONLY: params.CreateFlags |= SHFL_CF_ACCESS_READ; break; case O_WRONLY: params.CreateFlags |= SHFL_CF_ACCESS_WRITE; break; case O_RDWR: params.CreateFlags |= SHFL_CF_ACCESS_READWRITE; break; default: BUG (); } } if (file->f_flags & O_APPEND) { LogFunc(("O_APPEND set\n")); params.CreateFlags |= SHFL_CF_ACCESS_APPEND; } params.Info.Attr.fMode = inode->i_mode; LogFunc(("sf_reg_open: calling vboxCallCreate, file %s, flags=%#x, %#x\n", sf_i->path->String.utf8 , file->f_flags, params.CreateFlags)); rc = vboxCallCreate(&client_handle, &sf_g->map, sf_i->path, ¶ms); if (RT_FAILURE(rc)) { LogFunc(("vboxCallCreate failed flags=%d,%#x rc=%Rrc\n", file->f_flags, params.CreateFlags, rc)); kfree(sf_r); return -RTErrConvertToErrno(rc); } if (SHFL_HANDLE_NIL == params.Handle) { switch (params.Result) { case SHFL_PATH_NOT_FOUND: case SHFL_FILE_NOT_FOUND: rc_linux = -ENOENT; break; case SHFL_FILE_EXISTS: rc_linux = -EEXIST; break; default: break; } } sf_i->force_restat = 1; sf_r->handle = params.Handle; sf_i->file = file; file->private_data = sf_r; return rc_linux; }
/** * Write to a regular file. * * @param file the file * @param buf the buffer * @param size length of the buffer * @param off offset within the file * @returns the number of written bytes on success, Linux error code otherwise */ static ssize_t sf_reg_write(struct file *file, const char *buf, size_t size, loff_t *off) { int err; void *tmp; RTCCPHYS tmp_phys; size_t tmp_size; size_t left = size; ssize_t total_bytes_written = 0; struct inode *inode = GET_F_DENTRY(file)->d_inode; struct sf_inode_info *sf_i = GET_INODE_INFO(inode); struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_reg_info *sf_r = file->private_data; loff_t pos; TRACE(); BUG_ON(!sf_i); BUG_ON(!sf_g); BUG_ON(!sf_r); if (!S_ISREG(inode->i_mode)) { LogFunc(("write to non regular file %d\n", inode->i_mode)); return -EINVAL; } pos = *off; if (file->f_flags & O_APPEND) { pos = inode->i_size; *off = pos; } /** 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; } #if 1 if (VbglR0CanUsePhysPageList()) { err = VbglR0SfWritePhysCont(&client_handle, &sf_g->map, sf_r->handle, pos, &nwritten, tmp_phys); err = RT_FAILURE(err) ? -EPROTO : 0; } else #endif err = sf_reg_write_aux(__func__, sf_g, sf_r, tmp, &nwritten, pos); if (err) goto fail; pos += nwritten; left -= nwritten; buf += nwritten; total_bytes_written += nwritten; if (nwritten != to_write) break; } *off += total_bytes_written; if (*off > inode->i_size) inode->i_size = *off; sf_i->force_restat = 1; free_bounce_buffer(tmp); return total_bytes_written; fail: free_bounce_buffer(tmp); return err; }
/** * Read from a regular file. * * @param file the file * @param buf the buffer * @param size length of the buffer * @param off offset within the file * @returns the number of read bytes on success, Linux error code otherwise */ static ssize_t sf_reg_read(struct file *file, char *buf, size_t size, loff_t *off) { int err; void *tmp; RTCCPHYS tmp_phys; size_t tmp_size; size_t left = size; ssize_t total_bytes_read = 0; struct inode *inode = GET_F_DENTRY(file)->d_inode; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_reg_info *sf_r = file->private_data; loff_t pos = *off; TRACE(); if (!S_ISREG(inode->i_mode)) { LogFunc(("read from non regular file %d\n", inode->i_mode)); return -EINVAL; } /** 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; }
/** * Open a directory. Read the complete content into a buffer. * * @param inode inode * @param file file * @returns 0 on success, Linux error code otherwise */ static int sf_dir_open(struct inode *inode, struct file *file) { int rc; int err; struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb); struct sf_dir_info *sf_d; struct sf_inode_info *sf_i = GET_INODE_INFO(inode); SHFLCREATEPARMS params; TRACE(); BUG_ON(!sf_g); BUG_ON(!sf_i); if (file->private_data) { LogFunc(("sf_dir_open() called on already opened directory '%s'\n", sf_i->path->String.utf8)); return 0; } sf_d = sf_dir_info_alloc(); if (!sf_d) { LogRelFunc(("could not allocate directory info for '%s'\n", sf_i->path->String.utf8)); return -ENOMEM; } RT_ZERO(params); params.Handle = SHFL_HANDLE_NIL; params.CreateFlags = 0 | SHFL_CF_DIRECTORY | SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_READ ; LogFunc(("sf_dir_open(): calling vboxCallCreate, folder %s, flags %#x\n", sf_i->path->String.utf8, params.CreateFlags)); rc = vboxCallCreate(&client_handle, &sf_g->map, sf_i->path, ¶ms); if (RT_SUCCESS(rc)) { if (params.Result == SHFL_FILE_EXISTS) { err = sf_dir_read_all(sf_g, sf_i, sf_d, params.Handle); if (!err) file->private_data = sf_d; } else err = -ENOENT; rc = vboxCallClose(&client_handle, &sf_g->map, params.Handle); if (RT_FAILURE(rc)) LogFunc(("sf_dir_open(): vboxCallClose(%s) after err=%d failed rc=%Rrc\n", sf_i->path->String.utf8, err, rc)); } else err = -EPERM; if (err) sf_dir_info_free(sf_d); return err; }
/** * This is called when vfs failed to locate dentry in the cache. The * job of this function is to allocate inode and link it to dentry. * [dentry] contains the name to be looked in the [parent] directory. * Failure to locate the name is not a "hard" error, in this case NULL * inode is added to [dentry] and vfs should proceed trying to create * the entry via other means. NULL(or "positive" pointer) ought to be * returned in case of success and "negative" pointer on error */ static struct dentry *sf_lookup(struct inode *parent, struct dentry *dentry #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) , unsigned int flags #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) , struct nameidata *nd #endif ) { int err; struct sf_inode_info *sf_i, *sf_new_i; struct sf_glob_info *sf_g; SHFLSTRING *path; struct inode *inode; ino_t ino; SHFLFSOBJINFO fsinfo; TRACE(); sf_g = GET_GLOB_INFO(parent->i_sb); sf_i = GET_INODE_INFO(parent); BUG_ON(!sf_g); BUG_ON(!sf_i); err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); if (err) goto fail0; err = sf_stat(__func__, sf_g, path, &fsinfo, 1); if (err) { if (err == -ENOENT) { /* -ENOENT: add NULL inode to dentry so it later can be created via call to create/mkdir/open */ kfree(path); inode = NULL; } else goto fail1; } else { sf_new_i = kmalloc(sizeof(*sf_new_i), GFP_KERNEL); if (!sf_new_i) { LogRelFunc(("could not allocate memory for new inode info\n")); err = -ENOMEM; goto fail1; } sf_new_i->handle = SHFL_HANDLE_NIL; sf_new_i->force_reread = 0; ino = iunique(parent->i_sb, 1); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25) inode = iget_locked(parent->i_sb, ino); #else inode = iget(parent->i_sb, ino); #endif if (!inode) { LogFunc(("iget failed\n")); err = -ENOMEM; /* XXX: ??? */ goto fail2; } SET_INODE_INFO(inode, sf_new_i); sf_init_inode(sf_g, inode, &fsinfo); sf_new_i->path = path; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25) unlock_new_inode(inode); #endif } sf_i->force_restat = 0; dentry->d_time = jiffies; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) d_set_d_op(dentry, &sf_dentry_ops); #else dentry->d_op = &sf_dentry_ops; #endif d_add(dentry, inode); return NULL; fail2: kfree(sf_new_i); fail1: kfree(path); fail0: return ERR_PTR(err); }
int sf_setattr(struct dentry *dentry, struct iattr *iattr) { struct sf_glob_info *sf_g; struct sf_inode_info *sf_i; SHFLCREATEPARMS params; SHFLFSOBJINFO info; uint32_t cbBuffer; int rc, err; TRACE(); sf_g = GET_GLOB_INFO(dentry->d_inode->i_sb); sf_i = GET_INODE_INFO(dentry->d_inode); err = 0; RT_ZERO(params); params.Handle = SHFL_HANDLE_NIL; params.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS | SHFL_CF_ACT_FAIL_IF_NEW | SHFL_CF_ACCESS_ATTR_WRITE; /* this is at least required for Posix hosts */ if (iattr->ia_valid & ATTR_SIZE) params.CreateFlags |= SHFL_CF_ACCESS_WRITE; rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, ¶ms); if (RT_FAILURE(rc)) { LogFunc(("VbglR0SfCreate(%s) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); err = -RTErrConvertToErrno(rc); goto fail2; } if (params.Result != SHFL_FILE_EXISTS) { LogFunc(("file %s does not exist\n", sf_i->path->String.utf8)); err = -ENOENT; goto fail1; } /* Setting the file size and setting the other attributes has to be * handled separately, see implementation of vbsfSetFSInfo() in * vbsf.cpp */ if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) { #define mode_set(r) ((iattr->ia_mode & (S_##r)) ? RTFS_UNIX_##r : 0) RT_ZERO(info); if (iattr->ia_valid & ATTR_MODE) { info.Attr.fMode = mode_set(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 |= RTFS_TYPE_DIRECTORY; else info.Attr.fMode |= RTFS_TYPE_FILE; } if (iattr->ia_valid & ATTR_ATIME) sf_timespec_from_ftime(&info.AccessTime, &iattr->ia_atime); if (iattr->ia_valid & ATTR_MTIME) sf_timespec_from_ftime(&info.ModificationTime, &iattr->ia_mtime); /* ignore ctime (inode change time) as it can't be set from userland anyway */ cbBuffer = sizeof(info); rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, params.Handle, SHFL_INFO_SET | SHFL_INFO_FILE, &cbBuffer, (PSHFLDIRINFO)&info); if (RT_FAILURE(rc)) { LogFunc(("VbglR0SfFsInfo(%s, FILE) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); err = -RTErrConvertToErrno(rc); goto fail1; } } if (iattr->ia_valid & ATTR_SIZE) { RT_ZERO(info); info.cbObject = iattr->ia_size; cbBuffer = sizeof(info); rc = VbglR0SfFsInfo(&client_handle, &sf_g->map, params.Handle, SHFL_INFO_SET | SHFL_INFO_SIZE, &cbBuffer, (PSHFLDIRINFO)&info); if (RT_FAILURE(rc)) { LogFunc(("VbglR0SfFsInfo(%s, SIZE) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); err = -RTErrConvertToErrno(rc); goto fail1; } } rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle); if (RT_FAILURE(rc)) LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); return sf_inode_revalidate(dentry); fail1: rc = VbglR0SfClose(&client_handle, &sf_g->map, params.Handle); if (RT_FAILURE(rc)) LogFunc(("VbglR0SfClose(%s) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); fail2: return err; }
/** * Create a new regular file / directory. * * @param parent inode of the directory * @param dentry directory cache entry * @param mode file mode * @param fDirectory true if directory, false otherwise * @returns 0 on success, Linux error code otherwise */ static int sf_create_aux(struct inode *parent, struct dentry *dentry, umode_t mode, int fDirectory) { int rc, err; SHFLCREATEPARMS params; SHFLSTRING *path; struct sf_inode_info *sf_i = GET_INODE_INFO(parent); struct sf_glob_info *sf_g = GET_GLOB_INFO(parent->i_sb); TRACE(); BUG_ON(!sf_i); BUG_ON(!sf_g); err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); if (err) goto fail0; RT_ZERO(params); params.Handle = SHFL_HANDLE_NIL; params.CreateFlags = 0 | SHFL_CF_ACT_CREATE_IF_NEW | SHFL_CF_ACT_FAIL_IF_EXISTS | SHFL_CF_ACCESS_READWRITE | (fDirectory ? SHFL_CF_DIRECTORY : 0) ; params.Info.Attr.fMode = 0 | (fDirectory ? RTFS_TYPE_DIRECTORY : RTFS_TYPE_FILE) | (mode & S_IRWXUGO) ; params.Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING; LogFunc(("sf_create_aux: calling vboxCallCreate, folder %s, flags %#x\n", path->String.utf8, params.CreateFlags)); rc = vboxCallCreate(&client_handle, &sf_g->map, path, ¶ms); if (RT_FAILURE(rc)) { if (rc == VERR_WRITE_PROTECT) { err = -EROFS; goto fail1; } err = -EPROTO; LogFunc(("(%d): vboxCallCreate(%s) failed rc=%Rrc\n", fDirectory, sf_i->path->String.utf8, rc)); goto fail1; } if (params.Result != SHFL_FILE_CREATED) { err = -EPERM; LogFunc(("(%d): could not create file %s result=%d\n", fDirectory, sf_i->path->String.utf8, params.Result)); goto fail1; } err = sf_instantiate(parent, dentry, path, ¶ms.Info, fDirectory ? SHFL_HANDLE_NIL : params.Handle); if (err) { LogFunc(("(%d): could not instantiate dentry for %s err=%d\n", fDirectory, sf_i->path->String.utf8, err)); goto fail2; } /* * Don't close this handle right now. We assume that the same file is * opened with sf_reg_open() and later closed with sf_reg_close(). Save * the handle in between. Does not apply to directories. True? */ if (fDirectory) { rc = vboxCallClose(&client_handle, &sf_g->map, params.Handle); if (RT_FAILURE(rc)) LogFunc(("(%d): vboxCallClose failed rc=%Rrc\n", fDirectory, rc)); } sf_i->force_restat = 1; return 0; fail2: rc = vboxCallClose(&client_handle, &sf_g->map, params.Handle); if (RT_FAILURE(rc)) LogFunc(("(%d): vboxCallClose failed rc=%Rrc\n", fDirectory, rc)); fail1: kfree(path); fail0: 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; SHFLFSOBJINFO info; int symname_len = strlen(symname) + 1; TRACE(); sf_g = GET_GLOB_INFO(parent->i_sb); sf_i = GET_INODE_INFO(parent); BUG_ON(!sf_g); BUG_ON(!sf_i); err = sf_path_from_dentry(__func__, sf_g, sf_i, dentry, &path); if (err) goto fail0; ssymname = kmalloc(offsetof(SHFLSTRING, String.utf8) + symname_len, GFP_KERNEL); if (!ssymname) { LogRelFunc(("kmalloc failed, caller=sf_symlink\n")); err = -ENOMEM; goto fail1; } ssymname->u16Length = symname_len - 1; ssymname->u16Size = symname_len; memcpy(ssymname->String.utf8, symname, symname_len); rc = vboxCallSymlink(&client_handle, &sf_g->map, path, ssymname, &info); kfree(ssymname); if (RT_FAILURE(rc)) { if (rc == VERR_WRITE_PROTECT) { err = -EROFS; goto fail1; } LogFunc(("vboxCallSymlink(%s) failed rc=%Rrc\n", sf_i->path->String.utf8, rc)); err = -EPROTO; goto fail1; } err = sf_instantiate(parent, dentry, path, &info, SHFL_HANDLE_NIL); if (err) { LogFunc(("could not instantiate dentry for %s err=%d\n", sf_i->path->String.utf8, err)); goto fail1; } sf_i->force_restat = 1; return 0; fail1: kfree(path); fail0: return err; }