diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-25 01:20:01 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-25 01:20:01 +0000 |
commit | 3797ba0b62debb71af4606910acacc9896a9ae3b (patch) | |
tree | 414eea76253c7871bfdf3bd9d1817771eb40917c /fs/ext2 | |
parent | 2b6c0c580795a4404f72d2a794214dd9e080709d (diff) |
Merge with Linux 2.4.0-test2.
Diffstat (limited to 'fs/ext2')
-rw-r--r-- | fs/ext2/balloc.c | 7 | ||||
-rw-r--r-- | fs/ext2/dir.c | 2 | ||||
-rw-r--r-- | fs/ext2/file.c | 3 | ||||
-rw-r--r-- | fs/ext2/fsync.c | 133 | ||||
-rw-r--r-- | fs/ext2/ialloc.c | 8 | ||||
-rw-r--r-- | fs/ext2/inode.c | 44 | ||||
-rw-r--r-- | fs/ext2/namei.c | 15 | ||||
-rw-r--r-- | fs/ext2/super.c | 1 | ||||
-rw-r--r-- | fs/ext2/truncate.c | 4 |
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; |