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; }
/* 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 }
/** * 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; }
/** * 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; }
/** * 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; }