diff options
Diffstat (limited to 'fs/ext2/file.c')
-rw-r--r-- | fs/ext2/file.c | 271 |
1 files changed, 70 insertions, 201 deletions
diff --git a/fs/ext2/file.c b/fs/ext2/file.c index e0f497057..c90419ce3 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -30,15 +30,15 @@ #include <linux/locks.h> #include <linux/mm.h> #include <linux/pagemap.h> +#include <linux/smp_lock.h> #define NBUF 32 #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) +static int ext2_writepage (struct file * file, struct page * page); static long long ext2_file_lseek(struct file *, long long, int); -static ssize_t ext2_file_write (struct file *, const char *, size_t, loff_t *); -static int ext2_release_file (struct inode *, struct file *); #if BITS_PER_LONG < 64 static int ext2_open_file (struct inode *, struct file *); @@ -57,51 +57,6 @@ EXT2_MAX_SIZE(10), EXT2_MAX_SIZE(11), EXT2_MAX_SIZE(12), EXT2_MAX_SIZE(13) #endif -/* - * We have mostly NULL's here: the current defaults are ok for - * the ext2 filesystem. - */ -static struct file_operations ext2_file_operations = { - ext2_file_lseek, /* lseek */ - generic_file_read, /* read */ - ext2_file_write, /* write */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - ext2_ioctl, /* ioctl */ - generic_file_mmap, /* mmap */ -#if BITS_PER_LONG == 64 - NULL, /* no special open is needed */ -#else - ext2_open_file, -#endif - NULL, /* flush */ - ext2_release_file, /* release */ - ext2_sync_file, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL /* revalidate */ -}; - -struct inode_operations ext2_file_inode_operations = { - &ext2_file_operations,/* default file operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - generic_readpage, /* readpage */ - NULL, /* writepage */ - ext2_bmap, /* bmap */ - ext2_truncate, /* truncate */ - ext2_permission, /* permission */ - NULL /* smap */ -}; /* * Make sure the offset never goes beyond the 32-bit mark.. @@ -151,164 +106,30 @@ static inline void remove_suid(struct inode *inode) } } -static ssize_t ext2_file_write (struct file * filp, const char * buf, - size_t count, loff_t *ppos) +static int ext2_writepage (struct file * file, struct page * page) { - struct inode * inode = filp->f_dentry->d_inode; - off_t pos; - long block; - int offset; - int written, c; - struct buffer_head * bh, *bufferlist[NBUF]; - struct super_block * sb; - int err; - int i,buffercount,write_error; - - /* POSIX: mtime/ctime may not change for 0 count */ - if (!count) - return 0; - write_error = buffercount = 0; - if (!inode) { - printk("ext2_file_write: inode = NULL\n"); - return -EINVAL; - } - sb = inode->i_sb; - if (sb->s_flags & MS_RDONLY) - /* - * This fs has been automatically remounted ro because of errors - */ - return -ENOSPC; - - if (!S_ISREG(inode->i_mode)) { - ext2_warning (sb, "ext2_file_write", "mode = %07o", - (unsigned int) inode->i_mode); - return -EINVAL; - } - remove_suid(inode); - - if (filp->f_flags & O_APPEND) - pos = inode->i_size; - else { - pos = *ppos; - if (pos != *ppos) - return -EINVAL; -#if BITS_PER_LONG >= 64 - if (pos > ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]) - return -EINVAL; -#endif - } + return block_write_full_page(file, page, ext2_getblk_block); +} - /* Check for overflow.. */ -#if BITS_PER_LONG < 64 - if (pos > (__u32) (pos + count)) { - count = ~pos; /* == 0xFFFFFFFF - pos */ - if (!count) - return -EFBIG; - } -#else - { - off_t max = ext2_max_sizes[EXT2_BLOCK_SIZE_BITS(sb)]; +static long ext2_write_one_page (struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf) +{ + return block_write_partial_page(file, page, offset, bytes, buf, ext2_getblk_block); +} - if (pos + count > max) { - count = max - pos; - if (!count) - return -EFBIG; - } - if (((pos + count) >> 32) && - !(sb->u.ext2_sb.s_es->s_feature_ro_compat & - cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE))) { - /* If this is the first large file created, add a flag - to the superblock */ - sb->u.ext2_sb.s_es->s_feature_ro_compat |= - cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE); - mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1); - } +/* + * Write to a file (through the page cache). + */ +static ssize_t +ext2_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + ssize_t retval = generic_file_write(file, buf, count, ppos, ext2_write_one_page); + if (retval > 0) { + struct inode *inode = file->f_dentry->d_inode; + remove_suid(inode); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); } -#endif - - /* - * If a file has been opened in synchronous mode, we have to ensure - * that meta-data will also be written synchronously. Thus, we - * set the i_osync field. This field is tested by the allocation - * routines. - */ - if (filp->f_flags & O_SYNC) - inode->u.ext2_i.i_osync++; - block = pos >> EXT2_BLOCK_SIZE_BITS(sb); - offset = pos & (sb->s_blocksize - 1); - c = sb->s_blocksize - offset; - written = 0; - do { - bh = ext2_getblk (inode, block, 1, &err); - if (!bh) { - if (!written) - written = err; - break; - } - if (c > count) - c = count; - if (c != sb->s_blocksize && !buffer_uptodate(bh)) { - ll_rw_block (READ, 1, &bh); - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - brelse (bh); - if (!written) - written = -EIO; - break; - } - } - c -= copy_from_user (bh->b_data + offset, buf, c); - if (!c) { - brelse(bh); - if (!written) - written = -EFAULT; - break; - } - update_vm_cache(inode, pos, bh->b_data + offset, c); - pos += c; - written += c; - buf += c; - count -= c; - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); - - if (filp->f_flags & O_SYNC) - bufferlist[buffercount++] = bh; - else - brelse(bh); - if (buffercount == NBUF){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; i<buffercount; i++){ - wait_on_buffer(bufferlist[i]); - if (!buffer_uptodate(bufferlist[i])) - write_error=1; - brelse(bufferlist[i]); - } - buffercount=0; - } - if(write_error) - break; - block++; - offset = 0; - c = sb->s_blocksize; - } while (count); - if ( buffercount ){ - ll_rw_block(WRITE, buffercount, bufferlist); - for(i=0; i<buffercount; i++){ - wait_on_buffer(bufferlist[i]); - if (!buffer_uptodate(bufferlist[i])) - write_error=1; - brelse(bufferlist[i]); - } - } - if (pos > inode->i_size) - inode->i_size = pos; - if (filp->f_flags & O_SYNC) - inode->u.ext2_i.i_osync--; - inode->i_ctime = inode->i_mtime = CURRENT_TIME; - *ppos = pos; - mark_inode_dirty(inode); - return written; + return retval; } /* @@ -335,3 +156,51 @@ static int ext2_open_file (struct inode * inode, struct file * filp) return 0; } #endif + +/* + * We have mostly NULL's here: the current defaults are ok for + * the ext2 filesystem. + */ +static struct file_operations ext2_file_operations = { + ext2_file_lseek, /* lseek */ + generic_file_read, /* read */ + ext2_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + ext2_ioctl, /* ioctl */ + generic_file_mmap, /* mmap */ +#if BITS_PER_LONG == 64 + NULL, /* no special open is needed */ +#else + ext2_open_file, +#endif + NULL, /* flush */ + ext2_release_file, /* release */ + ext2_sync_file, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL /* revalidate */ +}; + +struct inode_operations ext2_file_inode_operations = { + &ext2_file_operations,/* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + ext2_bmap, /* bmap */ + block_read_full_page, /* readpage */ + ext2_writepage, /* writepage */ + block_flushpage, /* flushpage */ + ext2_truncate, /* truncate */ + ext2_permission, /* permission */ + NULL, /* smap */ + NULL, /* revalidate */ +}; |