summaryrefslogtreecommitdiffstats
path: root/fs/ext2
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-06-25 01:20:01 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-06-25 01:20:01 +0000
commit3797ba0b62debb71af4606910acacc9896a9ae3b (patch)
tree414eea76253c7871bfdf3bd9d1817771eb40917c /fs/ext2
parent2b6c0c580795a4404f72d2a794214dd9e080709d (diff)
Merge with Linux 2.4.0-test2.
Diffstat (limited to 'fs/ext2')
-rw-r--r--fs/ext2/balloc.c7
-rw-r--r--fs/ext2/dir.c2
-rw-r--r--fs/ext2/file.c3
-rw-r--r--fs/ext2/fsync.c133
-rw-r--r--fs/ext2/ialloc.c8
-rw-r--r--fs/ext2/inode.c44
-rw-r--r--fs/ext2/namei.c15
-rw-r--r--fs/ext2/super.c1
-rw-r--r--fs/ext2/truncate.c4
9 files changed, 157 insertions, 60 deletions
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 97fb703e1..a3f8ae4ce 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -473,8 +473,11 @@ repeat:
if (i >= sb->u.ext2_sb.s_groups_count)
i = 0;
gdp = ext2_get_group_desc (sb, i, &bh2);
- if (!gdp)
- goto io_error;
+ if (!gdp) {
+ *err = -EIO;
+ unlock_super (sb);
+ return 0;
+ }
if (le16_to_cpu(gdp->bg_free_blocks_count) > 0)
break;
}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
index cd62f058d..3a18b375c 100644
--- a/fs/ext2/dir.c
+++ b/fs/ext2/dir.c
@@ -26,7 +26,7 @@ struct file_operations ext2_dir_operations = {
read: generic_read_dir,
readdir: ext2_readdir,
ioctl: ext2_ioctl,
- fsync: ext2_fsync_file,
+ fsync: ext2_sync_file,
};
int ext2_check_dir_entry (const char * function, struct inode * dir,
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
index 130013e50..d2c137e2c 100644
--- a/fs/ext2/file.c
+++ b/fs/ext2/file.c
@@ -91,7 +91,6 @@ static int ext2_open_file (struct inode * inode, struct file * filp)
return 0;
}
-
/*
* We have mostly NULL's here: the current defaults are ok for
* the ext2 filesystem.
@@ -104,7 +103,7 @@ struct file_operations ext2_file_operations = {
mmap: generic_file_mmap,
open: ext2_open_file,
release: ext2_release_file,
- fsync: ext2_fsync_file,
+ fsync: ext2_sync_file,
};
struct inode_operations ext2_file_inode_operations = {
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
index 5b58f6cad..52ffd6138 100644
--- a/fs/ext2/fsync.c
+++ b/fs/ext2/fsync.c
@@ -27,28 +27,131 @@
#include <linux/smp_lock.h>
+#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb))
+#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb))
+
+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, le32_to_cpu(*block), blocksize);
+ if (!bh)
+ return 0;
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
+ /* There can be a parallell read(2) that started read-I/O
+ on the buffer so we can't assume that there's been
+ an I/O error without first waiting I/O completation. */
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh))
+ {
+ brelse (bh);
+ return -1;
+ }
+ }
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+ if (wait)
+ /* when we return from fsync all the blocks
+ must be _just_ stored on disk */
+ wait_on_buffer(bh);
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ atomic_dec(&bh->b_count);
+ return 0;
+}
+
+static int sync_iblock(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_indirect(inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = bread(inode->i_dev, tmp, blocksize);
+ if (!*bh)
+ return -1;
+ return 0;
+}
+
+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);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_indirect(inode, ((u32 *) dind_bh->b_data) + i, wait);
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+
+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);
+ if (rc || !tind_bh)
+ return rc;
+
+ for (i = 0; i < addr_per_block; i++) {
+ rc = sync_dindirect(inode, ((u32 *) tind_bh->b_data) + i, wait);
+ if (rc)
+ err = rc;
+ }
+ brelse(tind_bh);
+ return err;
+}
+
/*
* File may be NULL when we are called. Perhaps we shouldn't
* even pass file to fsync ?
*/
-int ext2_fsync_file(struct file * file, struct dentry *dentry, int datasync)
+int ext2_sync_file(struct file * file, struct dentry *dentry)
{
+ int wait, err = 0;
struct inode *inode = dentry->d_inode;
- return ext2_fsync_inode(inode, datasync);
-}
-int ext2_fsync_inode(struct inode *inode, int datasync)
-{
- int err;
-
- err = fsync_inode_buffers(inode);
- if (!(inode->i_state & I_DIRTY))
- return err;
- if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
- return err;
-
- err |= ext2_sync_inode(inode);
+ lock_kernel();
+ if (S_ISLNK(inode->i_mode) && !(inode->i_blocks))
+ /*
+ * Don't sync fast links!
+ */
+ goto skip;
+
+ err = generic_buffer_fdatasync(inode, 0, ~0UL);
+
+ for (wait=0; wait<=1; wait++)
+ {
+ 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);
+ }
+skip:
+ err |= ext2_sync_inode (inode);
+ unlock_kernel();
return err ? -EIO : 0;
}
-
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
index cbc806cda..3c95ccd70 100644
--- a/fs/ext2/ialloc.c
+++ b/fs/ext2/ialloc.c
@@ -287,6 +287,7 @@ struct inode * ext2_new_inode (const struct inode * dir, int mode, int * err)
repeat:
gdp = NULL; i=0;
+ *err = -ENOSPC;
if (S_ISDIR(mode)) {
avefreei = le32_to_cpu(es->s_free_inodes_count) /
sb->u.ext2_sb.s_groups_count;
@@ -368,7 +369,6 @@ repeat:
if (!gdp) {
unlock_super (sb);
iput(inode);
- *err = -ENOSPC;
return NULL;
}
bitmap_nr = load_inode_bitmap (sb, i);
@@ -398,8 +398,9 @@ repeat:
ext2_error (sb, "ext2_new_inode",
"Free inodes count corrupted in group %d",
i);
- /* If we continue recover from this case */
- gdp->bg_free_inodes_count = 0;
+ unlock_super (sb);
+ iput (inode);
+ return NULL;
}
goto repeat;
}
@@ -410,7 +411,6 @@ repeat:
"block_group = %d,inode=%d", i, j);
unlock_super (sb);
iput (inode);
- *err = EIO; /* Should never happen */
return NULL;
}
gdp->bg_free_inodes_count =
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index d3abb7cb2..7e5263fb1 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -117,7 +117,7 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
inode->u.ext2_i.i_prealloc_count--;
ext2_debug ("preallocation hit (%lu/%lu).\n",
++alloc_hits, ++alloc_attempts);
- *err = 0;
+
} else {
ext2_discard_prealloc (inode);
ext2_debug ("preallocation miss (%lu/%lu).\n",
@@ -200,7 +200,6 @@ out:
return ret;
}
-/* returns NULL and sets *err on error */
static struct buffer_head * inode_getblk (struct inode * inode, int nr,
int new_block, int * err, int metadata, long *phys, int *new)
{
@@ -224,6 +223,7 @@ repeat:
return NULL;
}
}
+ *err = -EFBIG;
/* Check file limits.. */
{
@@ -311,7 +311,7 @@ repeat:
* can fail due to: - not present
* - out of space
*
- * NULL return in the data case, or an error, is mandatory.
+ * NULL return in the data case is mandatory.
*/
static struct buffer_head * block_getblk (struct inode * inode,
struct buffer_head * bh, int nr,
@@ -341,7 +341,6 @@ repeat:
if (tmp == le32_to_cpu(*p))
goto out;
brelse (result);
- result = NULL;
goto repeat;
} else {
*phys = tmp;
@@ -403,9 +402,11 @@ repeat:
*new = 1;
}
*p = le32_to_cpu(tmp);
- mark_buffer_dirty_inode(bh, 1, inode);
- if (IS_SYNC(inode) || inode->u.ext2_i.i_osync)
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) {
ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
inode->i_ctime = CURRENT_TIME;
inode->i_blocks += blocksize/512;
mark_inode_dirty(inode);
@@ -486,9 +487,9 @@ static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *
#define GET_INODE_PTR(x) \
inode_getblk(inode, x, iblock, &err, 1, NULL, NULL)
#define GET_INDIRECT_DATABLOCK(x) \
- block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new)
+ block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new);
#define GET_INDIRECT_PTR(x) \
- block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL)
+ block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL);
if (ptr < direct_blocks) {
bh = GET_INODE_DATABLOCK(ptr);
@@ -546,11 +547,13 @@ abort_too_big:
struct buffer_head * ext2_getblk(struct inode * inode, long block, int create, int * err)
{
struct buffer_head dummy;
+ int error;
dummy.b_state = 0;
dummy.b_blocknr = -1000;
- *err = ext2_get_block(inode, block, &dummy, create);
- if (!*err && buffer_mapped(&dummy)) {
+ error = ext2_get_block(inode, block, &dummy, create);
+ *err = error;
+ if (!error && buffer_mapped(&dummy)) {
struct buffer_head *bh;
bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize);
if (buffer_new(&dummy)) {
@@ -878,23 +881,8 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
if (S_ISDIR(inode->i_mode))
raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
- else {
+ else
raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
- if (inode->i_size >> 31) {
- struct super_block *sb = inode->i_sb;
- struct ext2_super_block *es = sb->u.ext2_sb.s_es;
- if (!(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
- * SMP Note: we're currently protected by the
- * big kernel lock here, so this will need
- * to be changed if that's no longer true.
- */
- es->s_feature_ro_compat |= cpu_to_le32(EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
- ext2_write_super(sb);
- }
- }
- }
raw_inode->i_generation = cpu_to_le32(inode->i_generation);
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
@@ -916,10 +904,10 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
return err;
}
-void ext2_write_inode (struct inode * inode, int wait)
+void ext2_write_inode (struct inode * inode)
{
lock_kernel();
- ext2_update_inode (inode, wait);
+ ext2_update_inode (inode, 0);
unlock_kernel();
}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
index 116b4852f..46e273935 100644
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -366,9 +366,12 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
struct inode * inode;
int err;
+ /*
+ * N.B. Several error exits in ext2_new_inode don't set err.
+ */
inode = ext2_new_inode (dir, mode, &err);
if (!inode)
- return err;
+ return -EIO;
inode->i_op = &ext2_file_inode_operations;
inode->i_fop = &ext2_file_operations;
@@ -394,7 +397,7 @@ static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int
inode = ext2_new_inode (dir, mode, &err);
if (!inode)
- return err;
+ return -EIO;
inode->i_uid = current->fsuid;
init_special_inode(inode, mode, rdev);
@@ -425,7 +428,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
inode = ext2_new_inode (dir, S_IFDIR, &err);
if (!inode)
- return err;
+ return -EIO;
inode->i_op = &ext2_dir_inode_operations;
inode->i_fop = &ext2_dir_operations;
@@ -451,7 +454,7 @@ static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
strcpy (de->name, "..");
ext2_set_de_type(dir->i_sb, de, S_IFDIR);
inode->i_nlink = 2;
- mark_buffer_dirty_inode(dir_block, 1, dir);
+ mark_buffer_dirty(dir_block, 1);
brelse (dir_block);
inode->i_mode = S_IFDIR | mode;
if (dir->i_mode & S_ISGID)
@@ -631,7 +634,7 @@ static int ext2_symlink (struct inode * dir, struct dentry *dentry, const char *
return -ENAMETOOLONG;
if (!(inode = ext2_new_inode (dir, S_IFLNK, &err)))
- return err;
+ return -EIO;
inode->i_mode = S_IFLNK | S_IRWXUGO;
@@ -788,7 +791,7 @@ static int ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
mark_inode_dirty(old_dir);
if (dir_bh) {
PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
- mark_buffer_dirty_inode(dir_bh, 1, old_inode);
+ mark_buffer_dirty(dir_bh, 1);
old_dir->i_nlink--;
mark_inode_dirty(old_dir);
if (new_inode) {
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index d3af3b992..aa6a599fc 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -593,6 +593,7 @@ struct super_block * ext2_read_super (struct super_block * sb, void * data,
/*
* set up enough so that it can read an inode
*/
+ sb->s_dev = dev;
sb->s_op = &ext2_sops;
sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
if (!sb->s_root) {
diff --git a/fs/ext2/truncate.c b/fs/ext2/truncate.c
index 1c05cc09f..ba8397196 100644
--- a/fs/ext2/truncate.c
+++ b/fs/ext2/truncate.c
@@ -211,7 +211,7 @@ static int trunc_indirect (struct inode * inode, int offset, u32 * p, struct buf
inode->i_ino, tmp);
*p = 0;
if (dind_bh)
- mark_buffer_dirty_inode(dind_bh, 1, inode);
+ mark_buffer_dirty(dind_bh, 1);
else
mark_inode_dirty(inode);
return 0;
@@ -279,7 +279,7 @@ static int trunc_dindirect (struct inode * inode, int offset, u32 * p,
inode->i_ino, tmp);
*p = 0;
if (tind_bh)
- mark_buffer_dirty_inode(tind_bh, 1, inode);
+ mark_buffer_dirty(tind_bh, 1);
else
mark_inode_dirty(inode);
return 0;