diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-22 23:05:57 +0000 |
commit | 51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch) | |
tree | 5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /fs/ext2 | |
parent | 52273a23c9a84336b93a35e4847fc88fac7eb0e4 (diff) |
Merge with Linux 2.3.7.
WARNING: 2.3.7 is known to eat filesystems for breakfast and little
children for lunch, so if you try this on your machine make backups
first ...
Diffstat (limited to 'fs/ext2')
-rw-r--r-- | fs/ext2/balloc.c | 16 | ||||
-rw-r--r-- | fs/ext2/dir.c | 6 | ||||
-rw-r--r-- | fs/ext2/file.c | 271 | ||||
-rw-r--r-- | fs/ext2/fsync.c | 195 | ||||
-rw-r--r-- | fs/ext2/inode.c | 282 | ||||
-rw-r--r-- | fs/ext2/symlink.c | 6 | ||||
-rw-r--r-- | fs/ext2/truncate.c | 50 |
7 files changed, 340 insertions, 486 deletions
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 2c7ba02d7..053022309 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -358,7 +358,7 @@ error_return: * bitmap, and then for any free bit if that fails. */ int ext2_new_block (const struct inode * inode, unsigned long goal, - u32 * prealloc_count, u32 * prealloc_block, int * err) + u32 * prealloc_count, u32 * prealloc_block, int * err) { struct buffer_head * bh; struct buffer_head * bh2; @@ -594,20 +594,12 @@ got_block: if (j >= le32_to_cpu(es->s_blocks_count)) { ext2_error (sb, "ext2_new_block", - "block >= blocks count - " - "block_group = %d, block=%d", i, j); + "block(%d) >= blocks count(%d) - " + "block_group = %d, es == %p ",j, + le32_to_cpu(es->s_blocks_count), i, es); unlock_super (sb); return 0; } - if (!(bh = getblk (sb->s_dev, j, sb->s_blocksize))) { - ext2_error (sb, "ext2_new_block", "cannot get block %d", j); - unlock_super (sb); - return 0; - } - memset(bh->b_data, 0, sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse (bh); ext2_debug ("allocating block %d. " "Goal hits %d of %d.\n", j, goal_hits, goal_attempts); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index a6753d276..59f068b5e 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -67,12 +67,14 @@ struct inode_operations ext2_dir_inode_operations = { ext2_rename, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ ext2_permission, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; int ext2_check_dir_entry (const char * function, struct inode * dir, 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 */ +}; diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index 1a13c876b..8ae361e73 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -17,6 +17,9 @@ * Removed unnecessary code duplication for little endian machines * and excessive __inline__s. * Andi Kleen, 1997 + * + * Major simplications and cleanup - we only need to do the metadata, because + * we can depend on generic_block_fdatasync() to sync the data blocks. */ #include <asm/uaccess.h> @@ -32,221 +35,84 @@ #include <linux/locks.h> -#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) -#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) +#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) +#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) -static int sync_block (struct inode * inode, u32 * block, int wait) +static int sync_indirect(struct inode * inode, u32 * block, int wait) { struct buffer_head * bh; if (!*block) return 0; - bh = get_hash_table (inode->i_dev, *block, blocksize); + bh = get_hash_table(inode->i_dev, le32_to_cpu(*block), blocksize); if (!bh) return 0; if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); + brelse(bh); return -1; } if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); + brelse(bh); return 0; } - ll_rw_block (WRITE, 1, &bh); + ll_rw_block(WRITE, 1, &bh); bh->b_count--; return 0; } -#ifndef __LITTLE_ENDIAN -static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) -{ - struct buffer_head * bh; - - if (!le32_to_cpu(*block)) - return 0; - bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); - if (!bh) - return 0; - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); - return 0; - } - ll_rw_block (WRITE, 1, &bh); - bh->b_count--; - return 0; -} -#else -#define sync_block_swab32 sync_block -#endif - - -static int sync_iblock (struct inode * inode, u32 * iblock, +static int sync_iblock(struct inode * inode, u32 * iblock, struct buffer_head ** bh, int wait) { int rc, tmp; *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} - -#ifndef __LITTLE_ENDIAN -static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; tmp = le32_to_cpu(*iblock); if (!tmp) return 0; - rc = sync_block_swab32 (inode, iblock, wait); + rc = sync_indirect(inode, iblock, wait); if (rc) return rc; - *bh = bread (inode->i_dev, tmp, blocksize); + *bh = bread(inode->i_dev, tmp, blocksize); if (!*bh) return -1; return 0; } -#else -#define sync_iblock_swab32 sync_iblock -#endif - -static int sync_direct (struct inode * inode, int wait) -{ - int i; - int rc, err = 0; - - for (i = 0; i < EXT2_NDIR_BLOCKS; i++) { - rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait); - if (rc) - err = rc; - } - return err; -} - -static int sync_indirect (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} -#else -#define sync_indirect_swab32 sync_indirect -#endif -static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) +static int sync_dindirect(struct inode * inode, u32 * diblock, int wait) { int i; struct buffer_head * dind_bh; int rc, err = 0; - rc = sync_iblock (inode, diblock, &dind_bh, wait); + rc = sync_iblock(inode, diblock, &dind_bh, wait); if (rc || !dind_bh) return rc; for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); + rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait); if (rc) err = rc; } - brelse (dind_bh); + brelse(dind_bh); return err; } -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} -#else -#define sync_dindirect_swab32 sync_dindirect -#endif - -static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) +static int sync_tindirect(struct inode * inode, u32 * tiblock, int wait) { int i; struct buffer_head * tind_bh; int rc, err = 0; - rc = sync_iblock (inode, tiblock, &tind_bh, wait); + rc = sync_iblock(inode, tiblock, &tind_bh, wait); if (rc || !tind_bh) return rc; for (i = 0; i < addr_per_block; i++) { - rc = sync_dindirect_swab32 (inode, - ((u32 *) tind_bh->b_data) + i, - wait); + rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait); if (rc) err = rc; } - brelse (tind_bh); + brelse(tind_bh); return err; } @@ -266,18 +132,19 @@ int ext2_sync_file(struct file * file, struct dentry *dentry) */ goto skip; + err = generic_buffer_fdatasync(inode, 0, ~0UL); + for (wait=0; wait<=1; wait++) { - err |= sync_direct (inode, wait); - err |= sync_indirect (inode, - inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + err |= sync_indirect(inode, + inode->u.ext2_i.i_data+EXT2_IND_BLOCK, + wait); + err |= sync_dindirect(inode, + inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, + wait); + err |= sync_tindirect(inode, + inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, wait); - err |= sync_dindirect (inode, - inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, - wait); - err |= sync_tindirect (inode, - inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, - wait); } skip: err |= ext2_sync_inode (inode); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 693964a80..02fb5b7b7 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -31,6 +31,7 @@ #include <linux/string.h> #include <linux/locks.h> #include <linux/mm.h> +#include <linux/smp_lock.h> static int ext2_update_inode(struct inode * inode, int do_sync); @@ -59,7 +60,7 @@ void ext2_delete_inode (struct inode * inode) ext2_free_inode (inode); } -#define inode_bmap(inode, nr) ((inode)->u.ext2_i.i_data[(nr)]) +#define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)])) static inline int block_bmap (struct buffer_head * bh, int nr) { @@ -92,13 +93,12 @@ void ext2_discard_prealloc (struct inode * inode) #endif } -static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err) +static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err) { #ifdef EXT2FS_DEBUG static unsigned long alloc_hits = 0, alloc_attempts = 0; #endif unsigned long result; - struct buffer_head * bh; wait_on_super (inode->i_sb); @@ -112,19 +112,6 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err ext2_debug ("preallocation hit (%lu/%lu).\n", ++alloc_hits, ++alloc_attempts); - /* It doesn't matter if we block in getblk() since - we have already atomically allocated the block, and - are only clearing it now. */ - if (!(bh = getblk (inode->i_sb->s_dev, result, - inode->i_sb->s_blocksize))) { - ext2_error (inode->i_sb, "ext2_alloc_block", - "cannot get block %lu", result); - return 0; - } - memset(bh->b_data, 0, inode->i_sb->s_blocksize); - mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); - brelse (bh); } else { ext2_discard_prealloc (inode); ext2_debug ("preallocation miss (%lu/%lu).\n", @@ -139,13 +126,76 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int * err #else result = ext2_new_block (inode, goal, 0, 0, err); #endif - return result; } int ext2_bmap (struct inode * inode, int block) { + int i, ret; + int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); + int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + + ret = 0; + lock_kernel(); + if (block < 0) { + ext2_warning (inode->i_sb, "ext2_bmap", "block < 0"); + goto out; + } + if (block >= EXT2_NDIR_BLOCKS + addr_per_block + + (1 << (addr_per_block_bits * 2)) + + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { + ext2_warning (inode->i_sb, "ext2_bmap", "block > big"); + goto out; + } + if (block < EXT2_NDIR_BLOCKS) { + ret = inode_bmap (inode, block); + goto out; + } + block -= EXT2_NDIR_BLOCKS; + if (block < addr_per_block) { + i = inode_bmap (inode, EXT2_IND_BLOCK); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), block); + goto out; + } + block -= addr_per_block; + if (block < (1 << (addr_per_block_bits * 2))) { + i = inode_bmap (inode, EXT2_DIND_BLOCK); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block >> addr_per_block_bits); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, + inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); + } + block -= (1 << (addr_per_block_bits * 2)); + i = inode_bmap (inode, EXT2_TIND_BLOCK); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block >> (addr_per_block_bits * 2)); + if (!i) + goto out; + i = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + (block >> addr_per_block_bits) & (addr_per_block - 1)); + if (!i) + goto out; + ret = block_bmap (bread (inode->i_dev, i, inode->i_sb->s_blocksize), + block & (addr_per_block - 1)); +out: + unlock_kernel(); + return ret; +} + +int ext2_bmap_create (struct inode * inode, int block) +{ int i; int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); @@ -201,7 +251,8 @@ int ext2_bmap (struct inode * inode, int block) } static struct buffer_head * inode_getblk (struct inode * inode, int nr, - int create, int new_block, int * err) + int create, int new_block, int * err, int metadata, + int *phys_block, int *created) { u32 * p; int tmp, goal = 0; @@ -210,13 +261,18 @@ static struct buffer_head * inode_getblk (struct inode * inode, int nr, p = inode->u.ext2_i.i_data + nr; repeat: - tmp = *p; + tmp = le32_to_cpu(*p); if (tmp) { - struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp == *p) - return result; - brelse (result); - goto repeat; + if (metadata) { + struct buffer_head * result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (tmp == le32_to_cpu(*p)) + return result; + brelse (result); + goto repeat; + } else { + *phys_block = tmp; + return NULL; + } } *err = -EFBIG; if (!create) @@ -244,7 +300,7 @@ dont_create: if (!goal) { for (tmp = nr - 1; tmp >= 0; tmp--) { if (inode->u.ext2_i.i_data[tmp]) { - goal = inode->u.ext2_i.i_data[tmp]; + goal = le32_to_cpu(inode->u.ext2_i.i_data[tmp]); break; } } @@ -259,13 +315,28 @@ dont_create: tmp = ext2_alloc_block (inode, goal, err); if (!tmp) return NULL; - result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (*p) { - ext2_free_blocks (inode, tmp, 1); - brelse (result); - goto repeat; + if (metadata) { + result = getblk (inode->i_dev, tmp, inode->i_sb->s_blocksize); + if (*p) { + ext2_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + if (*p) { + ext2_free_blocks (inode, tmp, 1); + goto repeat; + } + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; } - *p = tmp; + *p = cpu_to_le32(tmp); + inode->u.ext2_i.i_next_alloc_block = new_block; inode->u.ext2_i.i_next_alloc_goal = tmp; inode->i_ctime = CURRENT_TIME; @@ -277,10 +348,17 @@ dont_create: return result; } +/* + * metadata / data + * possibly create / access + * can fail due to: - not present + * - out of space + * + * NULL return in the data case is mandatory. + */ static struct buffer_head * block_getblk (struct inode * inode, - struct buffer_head * bh, int nr, - int create, int blocksize, - int new_block, int * err) + struct buffer_head * bh, int nr, int create, int blocksize, + int new_block, int * err, int metadata, int *phys_block, int *created) { int tmp, goal = 0; u32 * p; @@ -302,13 +380,19 @@ static struct buffer_head * block_getblk (struct inode * inode, repeat: tmp = le32_to_cpu(*p); if (tmp) { - result = getblk (bh->b_dev, tmp, blocksize); - if (tmp == le32_to_cpu(*p)) { + if (metadata) { + result = getblk (bh->b_dev, tmp, blocksize); + if (tmp == le32_to_cpu(*p)) { + brelse (bh); + return result; + } + brelse (result); + goto repeat; + } else { + *phys_block = tmp; brelse (bh); - return result; + return NULL; } - brelse (result); - goto repeat; } *err = -EFBIG; if (!create) { @@ -343,7 +427,22 @@ repeat: brelse (bh); return NULL; } - result = getblk (bh->b_dev, tmp, blocksize); + if (metadata) { + result = getblk (bh->b_dev, tmp, blocksize); + if (*p) { + ext2_free_blocks (inode, tmp, 1); + brelse (result); + goto repeat; + } + memset(result->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(result, 1); + mark_buffer_dirty(result, 1); + } else { + *phys_block = tmp; + result = NULL; + *err = 0; + *created = 1; + } if (le32_to_cpu(*p)) { ext2_free_blocks (inode, tmp, 1); brelse (result); @@ -364,24 +463,27 @@ repeat: return result; } -struct buffer_head * ext2_getblk (struct inode * inode, long block, - int create, int * err) +int ext2_getblk_block (struct inode * inode, long block, + int create, int * err, int * created) { - struct buffer_head * bh; + struct buffer_head * bh, *tmp; unsigned long b; unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb); int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb); + int phys_block, ret; + lock_kernel(); + ret = 0; *err = -EIO; if (block < 0) { ext2_warning (inode->i_sb, "ext2_getblk", "block < 0"); - return NULL; + goto abort; } if (block > EXT2_NDIR_BLOCKS + addr_per_block + (1 << (addr_per_block_bits * 2)) + ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) { ext2_warning (inode->i_sb, "ext2_getblk", "block > big"); - return NULL; + goto abort; } /* * If this is a sequential block allocation, set the next_alloc_block @@ -398,32 +500,72 @@ struct buffer_head * ext2_getblk (struct inode * inode, long block, inode->u.ext2_i.i_next_alloc_goal++; } - *err = -ENOSPC; + *err = 0; // -ENOSPC; b = block; - if (block < EXT2_NDIR_BLOCKS) - return inode_getblk (inode, block, create, b, err); + *created = 0; + if (block < EXT2_NDIR_BLOCKS) { + /* + * data page. + */ + tmp = inode_getblk (inode, block, create, b, + err, 0, &phys_block, created); + goto out; + } block -= EXT2_NDIR_BLOCKS; if (block < addr_per_block) { - bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err); - return block_getblk (inode, bh, block, create, - inode->i_sb->s_blocksize, b, err); + bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, err, 1, NULL, NULL); + tmp = block_getblk (inode, bh, block, create, + inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + goto out; } block -= addr_per_block; if (block < (1 << (addr_per_block_bits * 2))) { - bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err); + bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, err, 1, NULL, NULL); bh = block_getblk (inode, bh, block >> addr_per_block_bits, - create, inode->i_sb->s_blocksize, b, err); - return block_getblk (inode, bh, block & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err); + create, inode->i_sb->s_blocksize, b, err, 1, NULL, NULL); + tmp = block_getblk (inode, bh, block & (addr_per_block - 1), + create, inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + goto out; } block -= (1 << (addr_per_block_bits * 2)); - bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err); + bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, err, 1, NULL,NULL); bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2), - create, inode->i_sb->s_blocksize, b, err); - bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1), - create, inode->i_sb->s_blocksize, b, err); - return block_getblk (inode, bh, block & (addr_per_block - 1), create, - inode->i_sb->s_blocksize, b, err); + create, inode->i_sb->s_blocksize, b, err, 1, NULL,NULL); + bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & + (addr_per_block - 1), create, inode->i_sb->s_blocksize, + b, err, 1, NULL,NULL); + tmp = block_getblk (inode, bh, block & (addr_per_block - 1), create, + inode->i_sb->s_blocksize, b, err, 0, &phys_block, created); + +out: + if (!phys_block) + goto abort; + if (*err) + goto abort; + ret = phys_block; +abort: + unlock_kernel(); + return ret; +} + +struct buffer_head * ext2_getblk (struct inode * inode, long block, + int create, int * err) +{ + struct buffer_head *tmp = NULL; + int phys_block; + int created; + + phys_block = ext2_getblk_block (inode, block, create, err, &created); + + if (phys_block) { + tmp = getblk (inode->i_dev, phys_block, inode->i_sb->s_blocksize); + if (created) { + memset(tmp->b_data, 0, inode->i_sb->s_blocksize); + mark_buffer_uptodate(tmp, 1); + mark_buffer_dirty(tmp, 1); + } + } + return tmp; } struct buffer_head * ext2_bread (struct inode * inode, int block, @@ -569,11 +711,14 @@ void ext2_read_inode (struct inode * inode) if (inode->u.ext2_i.i_prealloc_count) ext2_error (inode->i_sb, "ext2_read_inode", "New inode has non-zero prealloc count!"); - if (S_ISLNK(inode->i_mode) && !inode->i_blocks) - for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; - else for (block = 0; block < EXT2_N_BLOCKS; block++) - inode->u.ext2_i.i_data[block] = le32_to_cpu(raw_inode->i_block[block]); + + /* + * NOTE! The in-memory inode i_blocks array is in little-endian order + * even on big-endian machines: we do NOT byteswap the block numbers! + */ + for (block = 0; block < EXT2_N_BLOCKS; block++) + inode->u.ext2_i.i_data[block] = raw_inode->i_block[block]; + if (inode->i_ino == EXT2_ACL_IDX_INO || inode->i_ino == EXT2_ACL_DATA_INO) /* Nothing to do */ ; @@ -689,11 +834,8 @@ static int ext2_update_inode(struct inode * inode, int do_sync) raw_inode->i_generation = cpu_to_le32(inode->i_generation); if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev)); - else if (S_ISLNK(inode->i_mode) && !inode->i_blocks) - for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; else for (block = 0; block < EXT2_N_BLOCKS; block++) - raw_inode->i_block[block] = cpu_to_le32(inode->u.ext2_i.i_data[block]); + raw_inode->i_block[block] = inode->u.ext2_i.i_data[block]; mark_buffer_dirty(bh, 1); if (do_sync) { ll_rw_block (WRITE, 1, &bh); diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index 826cb4176..b0ebcb91b 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -43,12 +43,14 @@ struct inode_operations ext2_symlink_inode_operations = { NULL, /* rename */ ext2_readlink, /* readlink */ ext2_follow_link, /* follow_link */ + NULL, /* bmap */ NULL, /* readpage */ NULL, /* writepage */ - NULL, /* bmap */ + NULL, /* flushpage */ NULL, /* truncate */ NULL, /* permission */ - NULL /* smap */ + NULL, /* smap */ + NULL /* revalidate */ }; static struct dentry * ext2_follow_link(struct dentry * dentry, diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c index 84eacf87d..75ffaa534 100644 --- a/fs/ext2/truncate.c +++ b/fs/ext2/truncate.c @@ -131,10 +131,7 @@ static int check_block_empty(struct inode *inode, struct buffer_head *bh, if (bh->b_count == 1) { int tmp; - if (ind_bh) - tmp = le32_to_cpu(*p); - else - tmp = *p; + tmp = le32_to_cpu(*p); *p = 0; inode->i_blocks -= (inode->i_sb->s_blocksize / 512); mark_inode_dirty(inode); @@ -160,6 +157,9 @@ out: return retry; } +#define DATA_BUFFER_USED(bh) \ + ((bh->b_count > 1) || buffer_locked(bh)) + static int trunc_direct (struct inode * inode) { struct buffer_head * bh; @@ -170,7 +170,7 @@ static int trunc_direct (struct inode * inode) for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) { u32 * p = inode->u.ext2_i.i_data + i; - int tmp = *p; + int tmp = le32_to_cpu(*p); if (!tmp) continue; @@ -178,7 +178,7 @@ static int trunc_direct (struct inode * inode) bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { bh->b_count++; - if(bh->b_count != 1 || buffer_locked(bh)) { + if (DATA_BUFFER_USED(bh)) { brelse(bh); retry = 1; continue; @@ -215,11 +215,11 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, unsigned long block_to_free = 0, free_count = 0; int indirect_block, addr_per_block, blocks; - tmp = dind_bh ? le32_to_cpu(*p) : *p; + tmp = le32_to_cpu(*p); if (!tmp) return 0; ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != (dind_bh ? le32_to_cpu(*p) : *p)) { + if (tmp != le32_to_cpu(*p)) { brelse (ind_bh); return 1; } @@ -255,8 +255,8 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, bh = find_buffer(inode->i_dev, tmp, inode->i_sb->s_blocksize); if (bh) { bh->b_count++; - if (bh->b_count != 1 || buffer_locked(bh)) { - brelse (bh); + if (DATA_BUFFER_USED(bh)) { + brelse(bh); retry = 1; continue; } @@ -297,11 +297,11 @@ static int trunc_dindirect (struct inode * inode, int offset, u32 * p, int i, tmp, retry = 0; int dindirect_block, addr_per_block; - tmp = tind_bh ? le32_to_cpu(*p) : *p; + tmp = le32_to_cpu(*p); if (!tmp) return 0; dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != (tind_bh ? le32_to_cpu(*p) : *p)) { + if (tmp != le32_to_cpu(*p)) { brelse (dind_bh); return 1; } @@ -344,10 +344,11 @@ static int trunc_tindirect (struct inode * inode) int i, tmp, retry = 0; int tindirect_block, addr_per_block, offset; - if (!(tmp = *p)) + tmp = le32_to_cpu(*p); + if (!tmp) return 0; tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize); - if (tmp != *p) { + if (tmp != le32_to_cpu(*p)) { brelse (tind_bh); return 1; } @@ -384,8 +385,6 @@ static int trunc_tindirect (struct inode * inode) void ext2_truncate (struct inode * inode) { - int err, offset; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; @@ -411,25 +410,6 @@ void ext2_truncate (struct inode * inode) current->policy |= SCHED_YIELD; schedule(); } - /* - * If the file is not being truncated to a block boundary, the - * contents of the partial block following the end of the file - * must be zeroed in case it ever becomes accessible again due - * to subsequent file growth. - */ - offset = inode->i_size & (inode->i_sb->s_blocksize - 1); - if (offset) { - struct buffer_head * bh; - bh = ext2_bread (inode, - inode->i_size >> EXT2_BLOCK_SIZE_BITS(inode->i_sb), - 0, &err); - if (bh) { - memset (bh->b_data + offset, 0, - inode->i_sb->s_blocksize - offset); - mark_buffer_dirty (bh, 0); - brelse (bh); - } - } inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); } |