📄 myfsfile.c
字号:
#include "common.h"
#include "mytypes.h"
/* external variables */
int myfs_maxsz;
extern struct myfs_inode *myfs_inop(struct inode *ino);
/* file operations */
static int myfs_readpage(struct file *file, struct page * page);
static ssize_t myfs_file_read(struct file *file, char *buf, size_t count,
loff_t *ppos);
static ssize_t myfs_file_write(struct file *file, const char *buf,
size_t count, loff_t *ppos);
static void myfs_file_truncate(struct inode *);
static int myfs_file_mmap(struct file *, struct vm_area_struct *);
static loff_t myfs_file_llseek(struct file *, loff_t, int);
int myfs_file_create(struct inode *dir, struct dentry *entry, int mode);
int myfs_open(struct inode *inode, struct file *file);
int myfs_release(struct inode *inode, struct file *f);
struct address_space_operations myfs_file_aops = {
readpage: myfs_readpage /* readpage */
};
struct inode_operations myfs_file_inode_operations = {
create: myfs_file_create, /* create */
// rename: myfs_rename, /* rename */
truncate: myfs_file_truncate, /* truncate */
// revalidate: myfs_revalidate_inode, /* revalidate */
// setattr: myfs_notify_change /* setattr */
};
/* file_operations notes:
*
* we're going to use mm/filemap.c:generic_file_mmap() here. It will
* rely on our readpage function to work, so we'll have to get that
* going.
*
*/
struct file_operations myfs_file_operations = {
llseek: myfs_file_llseek,/* lseek */
read: myfs_file_read, /* read */
write: myfs_file_write, /* write */
mmap: myfs_file_mmap, /* mmap - we'll try the default */
open: myfs_open, /* open called on first open instance of file */
release: myfs_release, /* release called when last open instance closed */
// fsync: myfs_fsync /* fsync */
};
/* myfs_file_truncate()
*
* This is called from fs/open.c:do_truncate() and is called AFTER
* the notify_change. The notify_change MUST use inode_setattr() or
* some similar method to get the updated size into the inode, or we
* won't have it to use.
*
* Perhaps notify_change() should be setting the inode values but isn't?
*/
static void myfs_file_truncate(struct inode *inode)
{
int error;
// struct myfs_meta meta;
printk( "myfs_truncate called for %s, size = %Ld\n",
myfs_inop(inode)->name, (long long) inode->i_size);
/* memset(&meta, 0, sizeof(meta));
meta.valid = V_SIZE;
meta.size = inode->i_size;
if ((error = ll_myfs_setmeta(myfs_inop(inode), &meta, current->fsuid,
current->fsgid)) != 0) {
PERROR("myfs_truncate failed\n");
return;
}
// if we were so inclined, we could update all our other values here,
// since the setmeta gives us updated values back.
*/
return;
}
/* myfs_file_mmap()
*
* NOTES:
* Tried setting the VM_WRITE flag in the vma to get through our tests
* in myfs_map_userbuf, but that didn't work.
*/
static int myfs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
int error = 0;
printk("myfs_file_mmap called.\n");
/*
error = generic_file_mmap(file, vma);
*/
return error;
}
/* myfs_readpage()
*
* Inside the page structure are "offset", the offset into the file, and
* the page address, which can be found with "page_address(page)".
*
* See fs/nfs/read.c for readpage example.
*/
static int myfs_readpage(struct file *file, struct page *page)
{
int error = 0;
// struct inode *inode;
// char *buf;
// myfs_off_t offset;
// size_t count = PAGE_SIZE;
printk( "myfs_readpage is called!\n" );
/* // from generic_readpage()
atomic_inc(&page->count);
set_bit(PG_locked, &page->flags);
#if 0
// NOTE: THIS WAS COMMENTED OUT IN 2.4 CODE; I JUST WENT AHEAD AND
// COMMENTED IT OUT HERE TOO...
set_bit(PG_free_after, &page->flags); // not necessary ???
#endif
// from brw_page()
clear_bit(PG_uptodate, &page->flags);
clear_bit(PG_error, &page->flags);
#ifdef HAVE_KMAP
// this should help readpage work correctly for big mem machines
buf = (char *)kmap(page);
#else
buf = (char *)page_address(page);
#endif
#ifdef HAVE_LINUX_STRUCT_PAGE_OFFSET
// THIS WORKED FOR 2.2.15 and before...
offset = page->offset;
#else
// THIS HOPEFULLY WILL WORK NOW (IT'S WHAT PHIL HAD IN 2.4)
offset = ((loff_t)page->index) << PAGE_CACHE_SHIFT;
#if 0
// THIS IS WHAT I HAD BEFORE
offset = pgoff2loff(page->index);
#endif
#endif
inode = file->f_dentry->d_inode;
memset(buf, 0, count);
printk(D_FILE, "myfs_readpage called for %s (%ld), offset %ld, size %ld\n",
myfs_inop(inode)->name, (unsigned long) myfs_inop(inode)->handle,
(long) offset, (long) count);
error = ll_myfs_file_read(myfs_inop(inode), buf, count, &offset, 1);
if (error <= 0) goto myfs_readpage_error;
// from brw_page()
set_bit(PG_uptodate, &page->flags);
myfs_readpage_error:
#ifdef HAVE_KMAP
kunmap(page);
#endif
#ifdef HAVE_UNLOCKPAGE
// this supposedly helps prevent smp races in 2.4
UnlockPage(page);
#else
// from brw_page()
clear_bit(PG_locked, &page->flags);
wake_up(&page->wait);
#endif
#if 0
free_page(page_address(page)); // NFS does this
#endif
__free_page(page); // after_unlock_page() does this
*/
return error;
}
/* myfs_file_read(file, buf, count, ppos)
*
* Notes:
* - generally the pointer to the position passed in is a pointer to
* file->f_pos, so that doesn't need to be updated as well.
*
* TODO: Combine with myfs_file_write() to save space.
*/
static ssize_t myfs_file_read(struct file *file, char *cbuf, size_t count,
loff_t *ppos)
{
int error = 0, retsz = 0;
// struct inode *inode;
// myfs_off_t myfs_pos = *ppos;
// size_t xfersize;
// char *buf = (char *) cbuf;
printk( "myfs_file_read is called!\n" );
/* // should we error check here? do we know for sure that the dentry is
// there?
inode = file->f_dentry->d_inode;
printk(D_FILE, "myfs_file_read called for %s (%ld), offset %ld, size %ld\n",
myfs_inop(inode)->name, (unsigned long) myfs_inop(inode)->handle,
(long) myfs_pos, (long) count);
if (access_ok(VERIFY_WRITE, buf, count) == 0){
return -EFAULT;
}
if (count == 0){
return 0;
}
// split our operation into blocks of myfs_maxsz or smaller
do {
xfersize = (count < myfs_maxsz) ? count : myfs_maxsz;
error = ll_myfs_file_read(myfs_inop(inode), buf, xfersize, &myfs_pos, 0);
if (error <= 0){
return error;
}
retsz += error;
// position is updated by ll_myfs_file_read()
count -= xfersize;
buf += xfersize;
} while (count > 0);
*ppos = myfs_pos;
*/
return retsz;
}
/* myfs_file_write(file, buf, count, ppos)
*
* Notes:
* - generally the pointer to the position passed in is a pointer to
* file->f_pos, so that doesn't need to be updated as well.
*/
static ssize_t myfs_file_write(struct file *file, const char *cbuf,
size_t count, loff_t *ppos)
{
int error = 0, retsz = 0;
// struct inode *inode;
// myfs_off_t myfs_pos = *ppos;
// size_t xfersize;
// char *buf = (char *) cbuf;
printk( "myfs_file_write is called!\n" );
/*
// should we error check here? do we know for sure that the dentry is
// there?
inode = file->f_dentry->d_inode;
printk(D_FILE, "myfs_file_write called for %s (%ld), offset %ld, size %ld\n",
myfs_inop(inode)->name, (unsigned long) myfs_inop(inode)->handle,
(long) myfs_pos, (long) count);
if (access_ok(VERIFY_READ, (char *)buf, count) == 0){
return -EFAULT;
}
if (count == 0){
return 0;
}
// split our operation into blocks of myfs_maxsz or smaller
do {
xfersize = (count < myfs_maxsz) ? count : myfs_maxsz;
error = ll_myfs_file_write(myfs_inop(inode), buf, xfersize, &myfs_pos, 0);
if (error <= 0){
return error;
}
retsz += error;
// position is updated by ll_myfs_file_write()
count -= xfersize;
buf += xfersize;
} while (count > 0);
*ppos = myfs_pos;
if (myfs_pos > inode->i_size)
inode->i_size = myfs_pos;
*/
return retsz;
}
/* myfs_release()
*
* Called when the last open file reference for a given file is
* finished. We use this as an opportunity to tell the daemon to close
* the file when it wants to.
*/
int myfs_release(struct inode *inode, struct file *f)
{
int error = 0;
// struct myfs_inode *pinode;
printk( "myfs_release is called !\n" );
/*
pinode = myfs_inop(inode);
printk(D_FILE, "myfs_release called for %s (%ld)\n", pinode->name,
(unsigned long) pinode->handle);
error = ll_myfs_hint(pinode, HINT_CLOSE);
*/
return error;
}
/* myfs_open()
*
* Called from fs/open.c:filep_open()
*
* NOTES:
* Truncation and creation are handled by fs/namei.c:open_namei()
* We need to take care of O_APPEND here.
*
* The inode for the file is not necessarily up to date. This is
* important in the O_APPEND case because we need to have the most
* recent size, so we're going to force a revalidate_inode() in the case
* of an append.
*/
int myfs_open(struct inode *inode, struct file *file)
{
int error = 0;
printk( "myfs_open is called!\n" );
/*
#ifdef NEED_TO_LOCK_KERNEL
lock_kernel();
#endif
if ((file->f_flags & O_APPEND)) {
// force a revalidate
printk(D_FILE, "myfs_open: getting most up to date size\n");
if ((error = myfs_revalidate_inode(file->f_dentry)) < 0) {
#ifdef NEED_TO_LOCK_KERNEL
unlock_kernel();
#endif
return error;
}
// set the file position to point to one byte past the last byte
// in the file
file->f_pos = inode->i_size;
}
#ifdef NEED_TO_LOCK_KERNEL
unlock_kernel();
#endif
*/
return error;
}
/* myfs_file_create()
*/
int myfs_file_create(struct inode *dir, struct dentry *entry, int mode)
{
int error = 0, len_dir, len_file;
// struct inode *inode;
// struct myfs_meta meta;
// struct myfs_phys phys;
// struct myfs_inode *pinode;
// char *ptr;
printk("myfs_file_create called for %ld\n", dir->i_ino);
/* len_dir = strlen(myfs_inop(dir)->name);
len_file = entry->d_name.len;
if ((pinode = (struct myfs_inode *) kmalloc(sizeof(struct myfs_inode)
+ len_dir + len_file + 2, GFP_KERNEL)) == NULL){
d_drop(entry);
return -ENOMEM;
}
// build myfs_inode name field first
pinode->name = (int8_t *)pinode + sizeof(struct myfs_inode);
ptr = pinode->name;
strcpy(ptr, myfs_inop(dir)->name);
ptr += len_dir;
*ptr = '/';
ptr++;
strcpy(ptr, entry->d_name.name);
// do the create
meta.valid = V_MODE | V_UID | V_GID | V_TIMES;
meta.uid = current->fsuid;
meta.gid = current->fsgid;
meta.mode = mode;
meta.mtime = meta.atime = meta.ctime = CURRENT_TIME;
phys.blksize = DEFAULT_BLKSIZE;
phys.dist = DEFAULT_DIST;
phys.nodect = DEFAULT_NODECT;
printk(D_FILE, "myfs_file_create calling ll_myfs_create\n");
if ((error = ll_myfs_create(myfs_sbp(dir->i_sb), pinode->name,
len_dir + len_file + 1, &meta, &phys)) < 0)
{
kfree(pinode);
d_drop(entry);
return error;
}
// do a lookup so we can fill in the inode
printk(D_FILE, "myfs_file_create calling ll_myfs_lookup\n");
if ((error = ll_myfs_lookup(myfs_sbp(dir->i_sb), pinode->name,
len_dir + len_file + 1, &meta)) < 0)
{
kfree(pinode);
d_drop(entry);
return error;
}
// fill in inode structure and remainder of myfs_inode
if ((inode = iget(dir->i_sb, meta.handle)) == NULL) {
kfree(pinode);
d_drop(entry);
return -ENOMEM;
}
pinode->handle = inode->i_ino;
pinode->super = myfs_sbp(inode->i_sb);
myfs_meta_to_inode(&meta, inode);
inode->u.generic_ip = pinode;
d_instantiate(entry, inode); // do I know what this does? nope!
printk(D_FILE, "myfs_file_create: saved name is %s\n",
myfs_inop(inode)->name);
if (S_ISDIR(inode->i_mode)) {
inode->i_op = &myfs_dir_inode_operations;
#ifdef HAVE_LINUX_STRUCT_INODE_I_FOP
inode->i_fop = &myfs_dir_operations;
#endif
}
else if (S_ISREG(inode->i_mode)) {
inode->i_op = &myfs_file_inode_operations;
#ifdef HAVE_LINUX_STRUCT_INODE_I_FOP
inode->i_fop = &myfs_file_operations;
#endif
#ifdef HAVE_LINUX_STRUCT_ADDRESS_SPACE_OPERATIONS
inode->i_data.a_ops = &myfs_file_aops;
#endif
}
else {
inode->i_op = NULL;
#ifdef HAVE_LINUX_STRUCT_INODE_I_FOP
inode->i_fop = NULL;
#endif
}
*/
return 0;
}
/* myfs_file_llseek()
*
* NOTES: we mainly implement this so that we have the opportunity to
* update our metadata information (ie file size) when lseek is called
* with SEEK_END. generic_file_llseek() relies on the inode's size
* field being up to date and therefore is not always suitable in that
* case
*/
static loff_t myfs_file_llseek(struct file *file, loff_t offset, int
origin)
{
long long retval;
int error;
printk( "myfs_file_llseek is called!\n" );
/*
switch (origin) {
case 2:
// force a revalidate
printk(D_FILE, "myfs_file_llseek: getting most up to date size\n");
if ((error = myfs_revalidate_inode(file->f_dentry)) < 0) {
return error;
}
offset += file->f_dentry->d_inode->i_size;
break;
case 1:
offset += file->f_pos;
}
retval = -EINVAL;
#ifdef HAVE_LINUX_STRUCT_SUPER_BLOCK_S_MAXBYTES
if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
#else
if (offset>=0){
#endif
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
}
retval = offset;
}
*/
return retval;
}
/*
* Local variables:
* c-indent-level: 3
* c-basic-offset: 3
* tab-width: 3
* End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -