summaryrefslogtreecommitdiffstats
path: root/fs/ext2
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-22 23:05:57 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-22 23:05:57 +0000
commit51d3b7814cdccef9188240fe0cbd8d97ff2c7470 (patch)
tree5cbb01d0323d4f63ade66bdf48ba4a91aaa6df16 /fs/ext2
parent52273a23c9a84336b93a35e4847fc88fac7eb0e4 (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.c16
-rw-r--r--fs/ext2/dir.c6
-rw-r--r--fs/ext2/file.c271
-rw-r--r--fs/ext2/fsync.c195
-rw-r--r--fs/ext2/inode.c282
-rw-r--r--fs/ext2/symlink.c6
-rw-r--r--fs/ext2/truncate.c50
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);
}