1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
/*
* linux/fs/fat/mmap.c
*
* Written by Jacques Gelinas (jacques@solucorp.qc.ca)
* Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
*
* mmap handling for fat-based filesystems
*/
#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
#include <linux/version.h>
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/msdos_fs.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/pgtable.h>
/*
* Fill in the supplied page for mmap
*/
static unsigned long fat_file_mmap_nopage(
struct vm_area_struct * area,
unsigned long address,
int error_code)
{
struct inode * inode = area->vm_file->f_dentry->d_inode;
unsigned long page;
unsigned int clear;
int pos;
long gap; /* distance from eof to pos */
page = __get_free_page(GFP_KERNEL);
if (!page)
return page;
address &= PAGE_MASK;
pos = address - area->vm_start + area->vm_offset;
clear = 0;
gap = inode->i_size - pos;
if (gap <= 0){
/* mmaping beyond end of file */
clear = PAGE_SIZE;
}else{
int cur_read;
int need_read;
struct file filp;
if (gap < PAGE_SIZE){
clear = PAGE_SIZE - gap;
}
filp.f_reada = 0;
filp.f_pos = pos;
filp.f_dentry=area->vm_file->f_dentry;
need_read = PAGE_SIZE - clear;
{
mm_segment_t cur_fs = get_fs();
set_fs (KERNEL_DS);
cur_read = fat_file_read (&filp, (char*)page,
need_read, &filp.f_pos);
set_fs (cur_fs);
}
if (cur_read != need_read){
printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
,cur_read,need_read);
}
}
if (clear > 0){
memset ((char*)page+PAGE_SIZE-clear,0,clear);
}
return page;
}
struct vm_operations_struct fat_file_mmap = {
NULL, /* open */
NULL, /* close */
NULL, /* unmap */
NULL, /* protect */
NULL, /* sync */
NULL, /* advise */
fat_file_mmap_nopage, /* nopage */
NULL, /* wppage */
NULL, /* swapout */
NULL, /* swapin */
};
/*
* This is used for a general mmap of an msdos file
* Returns 0 if ok, or a negative error code if not.
*/
int fat_mmap(struct file * file, struct vm_area_struct * vma)
{
struct inode *inode = file->f_dentry->d_inode;
if (MSDOS_SB(inode->i_sb)->cvf_format &&
MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(file,vma);
if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
return -EINVAL;
if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
return -EINVAL;
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!IS_RDONLY(inode)) {
inode->i_atime = CURRENT_TIME;
mark_inode_dirty(inode);
}
vma->vm_ops = &fat_file_mmap;
return 0;
}
int fat_readpage(struct file *file, struct page * page)
{
struct dentry * dentry = file->f_dentry;
struct inode * inode = dentry->d_inode;
if (MSDOS_SB(inode->i_sb)->cvf_format &&
MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage) {
int ret = MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
flush_dcache_page(page_address(page));
return ret;
}
printk("fat_readpage called with no handler (shouldn't happen)\n");
return -1;
}
|