static void ext3_clear_journal_err(struct super_block * sb,
struct ext3_super_block * es);
static int ext3_sync_fs(struct super_block *sb, int wait);
+static const char *ext3_decode_error(struct super_block * sb, int errno,
+ char nbuf[16]);
+static int ext3_remount (struct super_block * sb, int * flags, char * data);
+static int ext3_statfs (struct super_block * sb, struct kstatfs * buf);
+static void ext3_unlockfs(struct super_block *sb);
+static void ext3_write_super (struct super_block * sb);
+static void ext3_write_super_lockfs(struct super_block *sb);
/*
* Wrappers for journal_start/end.
* that sync() will call the filesystem's write_super callback if
* appropriate.
*/
-handle_t *ext3_journal_start(struct inode *inode, int nblocks)
+handle_t *ext3_journal_start_sb(struct super_block *sb, int nblocks)
{
journal_t *journal;
- if (inode->i_sb->s_flags & MS_RDONLY)
+ if (sb->s_flags & MS_RDONLY)
return ERR_PTR(-EROFS);
/* Special case here: if the journal has aborted behind our
* backs (eg. EIO in the commit thread), then we still need to
* take the FS itself readonly cleanly. */
- journal = EXT3_JOURNAL(inode);
+ journal = EXT3_SB(sb)->s_journal;
if (is_journal_aborted(journal)) {
- ext3_abort(inode->i_sb, __FUNCTION__,
+ ext3_abort(sb, __FUNCTION__,
"Detected aborted journal");
return ERR_PTR(-EROFS);
}
char nbuf[16];
const char *errstr = ext3_decode_error(NULL, err, nbuf);
- printk(KERN_ERR "%s: aborting transaction: %s in %s",
- caller, errstr, err_fn);
-
if (bh)
BUFFER_TRACE(bh, "abort");
- journal_abort_handle(handle);
+
if (!handle->h_err)
handle->h_err = err;
-}
-static char error_buf[1024];
+ if (is_handle_aborted(handle))
+ return;
+
+ printk(KERN_ERR "%s: aborting transaction: %s in %s\n",
+ caller, errstr, err_fn);
+
+ journal_abort_handle(handle);
+}
/* Deal with the reporting of failure conditions on a filesystem such as
* inconsistencies detected or read IO failures.
struct ext3_super_block *es = EXT3_SB(sb)->s_es;
EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS;
- es->s_state |= cpu_to_le32(EXT3_ERROR_FS);
+ es->s_state |= cpu_to_le16(EXT3_ERROR_FS);
if (sb->s_flags & MS_RDONLY)
return;
- if (test_opt (sb, ERRORS_PANIC))
- panic ("EXT3-fs (device %s): panic forced after error\n",
- sb->s_id);
if (test_opt (sb, ERRORS_RO)) {
printk (KERN_CRIT "Remounting filesystem read-only\n");
sb->s_flags |= MS_RDONLY;
if (journal)
journal_abort(journal, -EIO);
}
+ if (test_opt(sb, ERRORS_PANIC))
+ panic("EXT3-fs (device %s): panic forced after error\n",
+ sb->s_id);
ext3_commit_super(sb, es, 1);
}
{
va_list args;
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
-
- printk (KERN_CRIT "EXT3-fs error (device %s): %s: %s\n",
- sb->s_id, function, error_buf);
+ va_start(args, fmt);
+ printk(KERN_CRIT "EXT3-fs error (device %s): %s: ",sb->s_id, function);
+ vprintk(fmt, args);
+ printk("\n");
+ va_end(args);
ext3_handle_error(sb);
}
-const char *ext3_decode_error(struct super_block * sb, int errno, char nbuf[16])
+static const char *ext3_decode_error(struct super_block * sb, int errno,
+ char nbuf[16])
{
char *errstr = NULL;
int errno)
{
char nbuf[16];
- const char *errstr = ext3_decode_error(sb, errno, nbuf);
+ const char *errstr;
+
+ /* Special case: if the error is EROFS, and we're not already
+ * inside a transaction, then there's really no point in logging
+ * an error. */
+ if (errno == -EROFS && journal_current_handle() == NULL &&
+ (sb->s_flags & MS_RDONLY))
+ return;
+ errstr = ext3_decode_error(sb, errno, nbuf);
printk (KERN_CRIT "EXT3-fs error (device %s) in %s: %s\n",
sb->s_id, function, errstr);
printk (KERN_CRIT "ext3_abort called.\n");
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
-
- if (test_opt (sb, ERRORS_PANIC))
- panic ("EXT3-fs panic (device %s): %s: %s\n",
- sb->s_id, function, error_buf);
+ va_start(args, fmt);
+ printk(KERN_CRIT "EXT3-fs error (device %s): %s: ",sb->s_id, function);
+ vprintk(fmt, args);
+ printk("\n");
+ va_end(args);
- printk (KERN_CRIT "EXT3-fs abort (device %s): %s: %s\n",
- sb->s_id, function, error_buf);
+ if (test_opt(sb, ERRORS_PANIC))
+ panic("EXT3-fs panic from previous error\n");
if (sb->s_flags & MS_RDONLY)
return;
- printk (KERN_CRIT "Remounting filesystem read-only\n");
+ printk(KERN_CRIT "Remounting filesystem read-only\n");
EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS;
sb->s_flags |= MS_RDONLY;
EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT;
journal_abort(EXT3_SB(sb)->s_journal, -EIO);
}
-/* Deal with the reporting of failure conditions while running, such as
- * inconsistencies in operation or invalid system states.
- *
- * Use ext3_error() for cases of invalid filesystem states, as that will
- * record an error on disk and force a filesystem check on the next boot.
- */
-NORET_TYPE void ext3_panic (struct super_block * sb, const char * function,
- const char * fmt, ...)
-{
- va_list args;
-
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
-
- /* this is to prevent panic from syncing this filesystem */
- /* AKPM: is this sufficient? */
- sb->s_flags |= MS_RDONLY;
- panic ("EXT3-fs panic (device %s): %s: %s\n",
- sb->s_id, function, error_buf);
-}
-
void ext3_warning (struct super_block * sb, const char * function,
const char * fmt, ...)
{
va_list args;
- va_start (args, fmt);
- vsprintf (error_buf, fmt, args);
- va_end (args);
- printk (KERN_WARNING "EXT3-fs warning (device %s): %s: %s\n",
- sb->s_id, function, error_buf);
+ va_start(args, fmt);
+ printk(KERN_WARNING "EXT3-fs warning (device %s): %s: ",
+ sb->s_id, function);
+ vprintk(fmt, args);
+ printk("\n");
+ va_end(args);
}
void ext3_update_dynamic_rev(struct super_block *sb)
bdev = sbi->journal_bdev;
if (bdev) {
ret = ext3_blkdev_put(bdev);
- sbi->journal_bdev = 0;
+ sbi->journal_bdev = NULL;
}
return ret;
}
"inode %s:%ld at %p: mode %o, nlink %d, next %d\n",
inode->i_sb->s_id, inode->i_ino, inode,
inode->i_mode, inode->i_nlink,
- le32_to_cpu(NEXT_ORPHAN(inode)));
+ NEXT_ORPHAN(inode));
}
}
-void ext3_put_super (struct super_block * sb)
+static void ext3_put_super (struct super_block * sb)
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
struct ext3_super_block *es = sbi->s_es;
journal_destroy(sbi->s_journal);
if (!(sb->s_flags & MS_RDONLY)) {
EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
- es->s_state = le16_to_cpu(sbi->s_mount_state);
+ es->s_state = cpu_to_le16(sbi->s_mount_state);
BUFFER_TRACE(sbi->s_sbh, "marking dirty");
mark_buffer_dirty(sbi->s_sbh);
ext3_commit_super(sb, es, 1);
for (i = 0; i < sbi->s_gdb_count; i++)
brelse(sbi->s_group_desc[i]);
kfree(sbi->s_group_desc);
- kfree(sbi->s_debts);
+ percpu_counter_destroy(&sbi->s_freeblocks_counter);
+ percpu_counter_destroy(&sbi->s_freeinodes_counter);
+ percpu_counter_destroy(&sbi->s_dirs_counter);
brelse(sbi->s_sbh);
#ifdef CONFIG_QUOTA
- for (i = 0; i < MAXQUOTAS; i++) {
- if (sbi->s_qf_names[i])
- kfree(sbi->s_qf_names[i]);
- }
+ for (i = 0; i < MAXQUOTAS; i++)
+ kfree(sbi->s_qf_names[i]);
#endif
/* Debugging code just in case the in-memory inode orphan list
ei->i_acl = EXT3_ACL_NOT_CACHED;
ei->i_default_acl = EXT3_ACL_NOT_CACHED;
#endif
+ ei->i_block_alloc_info = NULL;
ei->vfs_inode.i_version = 1;
return &ei->vfs_inode;
}
{
ext3_inode_cachep = kmem_cache_create("ext3_inode_cache",
sizeof(struct ext3_inode_info),
- 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+ 0, SLAB_RECLAIM_ACCOUNT,
init_once, NULL);
if (ext3_inode_cachep == NULL)
return -ENOMEM;
printk(KERN_INFO "ext3_inode_cache: not all structures were freed\n");
}
-#ifdef CONFIG_EXT3_FS_POSIX_ACL
-
static void ext3_clear_inode(struct inode *inode)
{
+ struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info;
+#ifdef CONFIG_EXT3_FS_POSIX_ACL
if (EXT3_I(inode)->i_acl &&
EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) {
posix_acl_release(EXT3_I(inode)->i_acl);
posix_acl_release(EXT3_I(inode)->i_default_acl);
EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED;
}
-}
-
-#else
-# define ext3_clear_inode NULL
#endif
+ ext3_discard_reservation(inode);
+ EXT3_I(inode)->i_block_alloc_info = NULL;
+ kfree(rsv);
+}
#ifdef CONFIG_QUOTA
static int ext3_dquot_initialize(struct inode *inode, int type);
static int ext3_dquot_drop(struct inode *inode);
static int ext3_write_dquot(struct dquot *dquot);
+static int ext3_acquire_dquot(struct dquot *dquot);
+static int ext3_release_dquot(struct dquot *dquot);
static int ext3_mark_dquot_dirty(struct dquot *dquot);
static int ext3_write_info(struct super_block *sb, int type);
static int ext3_quota_on(struct super_block *sb, int type, int format_id, char *path);
static int ext3_quota_on_mount(struct super_block *sb, int type);
-static int ext3_quota_off_mount(struct super_block *sb, int type);
+static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
+ size_t len, loff_t off);
+static ssize_t ext3_quota_write(struct super_block *sb, int type,
+ const char *data, size_t len, loff_t off);
static struct dquot_operations ext3_quota_operations = {
.initialize = ext3_dquot_initialize,
.free_inode = dquot_free_inode,
.transfer = dquot_transfer,
.write_dquot = ext3_write_dquot,
+ .acquire_dquot = ext3_acquire_dquot,
+ .release_dquot = ext3_release_dquot,
.mark_dirty = ext3_mark_dquot_dirty,
.write_info = ext3_write_info
};
.read_inode = ext3_read_inode,
.write_inode = ext3_write_inode,
.dirty_inode = ext3_dirty_inode,
- .put_inode = ext3_put_inode,
.delete_inode = ext3_delete_inode,
.put_super = ext3_put_super,
.write_super = ext3_write_super,
.statfs = ext3_statfs,
.remount_fs = ext3_remount,
.clear_inode = ext3_clear_inode,
+#ifdef CONFIG_QUOTA
+ .quota_read = ext3_quota_read,
+ .quota_write = ext3_quota_write,
+#endif
};
struct dentry *ext3_get_parent(struct dentry *child);
Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov,
- Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, Opt_noload,
+ Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
+ Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh,
Opt_commit, Opt_journal_update, Opt_journal_inum,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0,
- Opt_ignore, Opt_err,
+ Opt_tagxid, Opt_barrier, Opt_ignore, Opt_err, Opt_resize,
};
static match_table_t tokens = {
{Opt_nouser_xattr, "nouser_xattr"},
{Opt_acl, "acl"},
{Opt_noacl, "noacl"},
+ {Opt_reservation, "reservation"},
+ {Opt_noreservation, "noreservation"},
{Opt_noload, "noload"},
+ {Opt_nobh, "nobh"},
{Opt_commit, "commit=%u"},
{Opt_journal_update, "journal=update"},
{Opt_journal_inum, "journal=%u"},
{Opt_grpjquota, "grpjquota=%s"},
{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
+ {Opt_tagxid, "tagxid"},
{Opt_ignore, "grpquota"},
{Opt_ignore, "noquota"},
{Opt_ignore, "quota"},
{Opt_ignore, "usrquota"},
- {Opt_err, NULL}
+ {Opt_barrier, "barrier=%u"},
+ {Opt_err, NULL},
+ {Opt_resize, "resize"},
};
static unsigned long get_sb_block(void **data)
}
static int parse_options (char * options, struct super_block *sb,
- unsigned long * inum, int is_remount)
+ unsigned long * inum, unsigned long *n_blocks_count, int is_remount)
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
char * p;
int option;
#ifdef CONFIG_QUOTA
int qtype;
+ char *qname;
#endif
if (!options)
case Opt_nouid32:
set_opt (sbi->s_mount_opt, NO_UID32);
break;
+#ifndef CONFIG_INOXID_NONE
+ case Opt_tagxid:
+ if (is_remount) {
+ printk(KERN_ERR "EXT3-fs: cannot specify "
+ "tagxid on remount\n");
+ return 0;
+ }
+ set_opt (sbi->s_mount_opt, TAG_XID);
+ break;
+#endif
case Opt_check:
#ifdef CONFIG_EXT3_CHECK
set_opt (sbi->s_mount_opt, CHECK);
printk("EXT3 (no)acl options not supported\n");
break;
#endif
+ case Opt_reservation:
+ set_opt(sbi->s_mount_opt, RESERVATION);
+ break;
+ case Opt_noreservation:
+ clear_opt(sbi->s_mount_opt, RESERVATION);
+ break;
case Opt_journal_update:
/* @@@ FIXME */
/* Eventually we will want to be able to create
"quota options when quota turned on.\n");
return 0;
}
- if (sbi->s_qf_names[qtype]) {
+ qname = match_strdup(&args[0]);
+ if (!qname) {
printk(KERN_ERR
- "EXT3-fs: %s quota file already "
- "specified.\n", QTYPE2NAME(qtype));
+ "EXT3-fs: not enough memory for "
+ "storing quotafile name.\n");
return 0;
}
- sbi->s_qf_names[qtype] = match_strdup(&args[0]);
- if (!sbi->s_qf_names[qtype]) {
+ if (sbi->s_qf_names[qtype] &&
+ strcmp(sbi->s_qf_names[qtype], qname)) {
printk(KERN_ERR
- "EXT3-fs: not enough memory for "
- "storing quotafile name.\n");
+ "EXT3-fs: %s quota file already "
+ "specified.\n", QTYPE2NAME(qtype));
+ kfree(qname);
return 0;
}
+ sbi->s_qf_names[qtype] = qname;
if (strchr(sbi->s_qf_names[qtype], '/')) {
printk(KERN_ERR
"EXT3-fs: quotafile must be on "
"quota turned on.\n");
return 0;
}
- if (sbi->s_qf_names[qtype]) {
- kfree(sbi->s_qf_names[qtype]);
- sbi->s_qf_names[qtype] = NULL;
- }
+ kfree(sbi->s_qf_names[qtype]);
+ sbi->s_qf_names[qtype] = NULL;
break;
case Opt_jqfmt_vfsold:
sbi->s_jquota_fmt = QFMT_VFS_OLD;
case Opt_abort:
set_opt(sbi->s_mount_opt, ABORT);
break;
+ case Opt_barrier:
+ if (match_int(&args[0], &option))
+ return 0;
+ if (option)
+ set_opt(sbi->s_mount_opt, BARRIER);
+ else
+ clear_opt(sbi->s_mount_opt, BARRIER);
+ break;
case Opt_ignore:
break;
+ case Opt_resize:
+ if (!n_blocks_count) {
+ printk("EXT3-fs: resize option only available "
+ "for remount\n");
+ return 0;
+ }
+ match_int(&args[0], &option);
+ *n_blocks_count = option;
+ break;
+ case Opt_nobh:
+ set_opt(sbi->s_mount_opt, NOBH);
+ break;
default:
printk (KERN_ERR
"EXT3-fs: Unrecognized mount option \"%s\" "
es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT3_VALID_FS);
#endif
if (!(__s16) le16_to_cpu(es->s_max_mnt_count))
- es->s_max_mnt_count =
- (__s16) cpu_to_le16(EXT3_DFL_MAX_MNT_COUNT);
+ es->s_max_mnt_count = cpu_to_le16(EXT3_DFL_MAX_MNT_COUNT);
es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1);
es->s_mtime = cpu_to_le32(get_seconds());
ext3_update_dynamic_rev(sb);
return res;
}
+/* Called at mount-time, super-block is locked */
static int ext3_check_descriptors (struct super_block * sb)
{
struct ext3_sb_info *sbi = EXT3_SB(sb);
/* Turn quotas off */
for (i = 0; i < MAXQUOTAS; i++) {
if (sb_dqopt(sb)->files[i])
- ext3_quota_off_mount(sb, i);
+ vfs_quota_off(sb, i);
}
#endif
sb->s_flags = s_flags; /* Restore MS_RDONLY status */
static loff_t ext3_max_size(int bits)
{
loff_t res = EXT3_NDIR_BLOCKS;
+ /* This constant is calculated to be the largest file size for a
+ * dense, 4k-blocksize file such that the total number of
+ * sectors in the file, including data and all indirect blocks,
+ * does not exceed 2^32. */
+ const loff_t upper_limit = 0x1ff7fffd000LL;
+
res += 1LL << (bits-2);
res += 1LL << (2*(bits-2));
res += 1LL << (3*(bits-2));
res <<= bits;
- if (res > (512LL << 32) - (1 << bits))
- res = (512LL << 32) - (1 << bits);
+ if (res > upper_limit)
+ res = upper_limit;
return res;
}
static int ext3_fill_super (struct super_block *sb, void *data, int silent)
{
struct buffer_head * bh;
- struct ext3_super_block *es = 0;
+ struct ext3_super_block *es = NULL;
struct ext3_sb_info *sbi;
unsigned long block;
unsigned long sb_block = get_sb_block(&data);
int db_count;
int i;
int needs_recovery;
+ __le32 features;
sbi = kmalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
sbi->s_resuid = EXT3_DEF_RESUID;
sbi->s_resgid = EXT3_DEF_RESGID;
+ unlock_kernel();
+
blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE);
if (!blocksize) {
printk(KERN_ERR "EXT3-fs: unable to set blocksize\n");
es = (struct ext3_super_block *) (((char *)bh->b_data) + offset);
sbi->s_es = es;
sb->s_magic = le16_to_cpu(es->s_magic);
- if (sb->s_magic != EXT3_SUPER_MAGIC) {
- if (!silent)
- printk(KERN_ERR
- "VFS: Can't find ext3 filesystem on dev %s.\n",
- sb->s_id);
- goto failed_mount;
- }
+ if (sb->s_magic != EXT3_SUPER_MAGIC)
+ goto cantfind_ext3;
/* Set defaults before we parse the mount options */
def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
- if (!parse_options ((char *) data, sb, &journal_inum, 0))
+ set_opt(sbi->s_mount_opt, RESERVATION);
+
+ if (!parse_options ((char *) data, sb, &journal_inum, NULL, 0))
goto failed_mount;
- sb->s_flags |= MS_ONE_SECOND;
+ if (EXT3_SB(sb)->s_mount_opt & EXT3_MOUNT_TAG_XID)
+ sb->s_flags |= MS_TAGXID;
+
sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
((sbi->s_mount_opt & EXT3_MOUNT_POSIX_ACL) ? MS_POSIXACL : 0);
* previously didn't change the revision level when setting the flags,
* so there is a chance incompat flags are set on a rev 0 filesystem.
*/
- if ((i = EXT3_HAS_INCOMPAT_FEATURE(sb, ~EXT3_FEATURE_INCOMPAT_SUPP))) {
+ features = EXT3_HAS_INCOMPAT_FEATURE(sb, ~EXT3_FEATURE_INCOMPAT_SUPP);
+ if (features) {
printk(KERN_ERR "EXT3-fs: %s: couldn't mount because of "
"unsupported optional features (%x).\n",
- sb->s_id, i);
+ sb->s_id, le32_to_cpu(features));
goto failed_mount;
}
- if (!(sb->s_flags & MS_RDONLY) &&
- (i = EXT3_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP))){
+ features = EXT3_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP);
+ if (!(sb->s_flags & MS_RDONLY) && features) {
printk(KERN_ERR "EXT3-fs: %s: couldn't mount RDWR because of "
"unsupported optional features (%x).\n",
- sb->s_id, i);
+ sb->s_id, le32_to_cpu(features));
goto failed_mount;
}
blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size);
}
es = (struct ext3_super_block *)(((char *)bh->b_data) + offset);
sbi->s_es = es;
- if (es->s_magic != le16_to_cpu(EXT3_SUPER_MAGIC)) {
+ if (es->s_magic != cpu_to_le16(EXT3_SUPER_MAGIC)) {
printk (KERN_ERR
"EXT3-fs: Magic mismatch, very weird !\n");
goto failed_mount;
sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
+ if (EXT3_INODE_SIZE(sb) == 0)
+ goto cantfind_ext3;
sbi->s_inodes_per_block = blocksize / EXT3_INODE_SIZE(sb);
- sbi->s_itb_per_group = sbi->s_inodes_per_group /sbi->s_inodes_per_block;
+ if (sbi->s_inodes_per_block == 0)
+ goto cantfind_ext3;
+ sbi->s_itb_per_group = sbi->s_inodes_per_group /
+ sbi->s_inodes_per_block;
sbi->s_desc_per_block = blocksize / sizeof(struct ext3_group_desc);
sbi->s_sbh = bh;
sbi->s_mount_state = le16_to_cpu(es->s_state);
goto failed_mount;
}
+ if (EXT3_BLOCKS_PER_GROUP(sb) == 0)
+ goto cantfind_ext3;
sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) -
le32_to_cpu(es->s_first_data_block) +
EXT3_BLOCKS_PER_GROUP(sb) - 1) /
printk (KERN_ERR "EXT3-fs: not enough memory\n");
goto failed_mount;
}
- sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(u8),
- GFP_KERNEL);
- if (!sbi->s_debts) {
- printk("EXT3-fs: not enough memory to allocate s_bgi\n");
- goto failed_mount2;
- }
- memset(sbi->s_debts, 0, sbi->s_groups_count * sizeof(u8));
percpu_counter_init(&sbi->s_freeblocks_counter);
percpu_counter_init(&sbi->s_freeinodes_counter);
sbi->s_gdb_count = db_count;
get_random_bytes(&sbi->s_next_generation, sizeof(u32));
spin_lock_init(&sbi->s_next_gen_lock);
+ /* per fileystem reservation list head & lock */
+ spin_lock_init(&sbi->s_rsv_window_lock);
+ sbi->s_rsv_window_root = RB_ROOT;
+ /* Add a single, static dummy reservation to the start of the
+ * reservation window list --- it gives us a placeholder for
+ * append-at-start-of-list which makes the allocation logic
+ * _much_ simpler. */
+ sbi->s_rsv_window_head.rsv_start = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;
+ sbi->s_rsv_window_head.rsv_end = EXT3_RESERVE_WINDOW_NOT_ALLOCATED;
+ sbi->s_rsv_window_head.rsv_alloc_hit = 0;
+ sbi->s_rsv_window_head.rsv_goal_size = 0;
+ ext3_rsv_window_add(sb, &sbi->s_rsv_window_head);
+
/*
* set up enough so that it can read an inode
*/
sb->s_op = &ext3_sops;
sb->s_export_op = &ext3_export_ops;
+ sb->s_xattr = ext3_xattr_handlers;
#ifdef CONFIG_QUOTA
sb->s_qcop = &ext3_qctl_operations;
sb->dq_op = &ext3_quota_operations;
#endif
INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
- sb->s_root = 0;
+ sb->s_root = NULL;
needs_recovery = (es->s_last_orphan != 0 ||
EXT3_HAS_INCOMPAT_FEATURE(sb,
break;
}
+ if (test_opt(sb, NOBH)) {
+ if (sb->s_blocksize_bits != PAGE_CACHE_SHIFT) {
+ printk(KERN_WARNING "EXT3-fs: Ignoring nobh option "
+ "since filesystem blocksize doesn't match "
+ "pagesize\n");
+ clear_opt(sbi->s_mount_opt, NOBH);
+ }
+ if (!(test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA)) {
+ printk(KERN_WARNING "EXT3-fs: Ignoring nobh option - "
+ "its supported only with writeback mode\n");
+ clear_opt(sbi->s_mount_opt, NOBH);
+ }
+ }
/*
* The journal_load will have done any necessary log recovery,
* so we can safely mount the rest of the filesystem now.
percpu_counter_mod(&sbi->s_dirs_counter,
ext3_count_dirs(sb));
+ lock_kernel();
return 0;
+cantfind_ext3:
+ if (!silent)
+ printk(KERN_ERR "VFS: Can't find ext3 filesystem on dev %s.\n",
+ sb->s_id);
+ goto failed_mount;
+
failed_mount3:
journal_destroy(sbi->s_journal);
failed_mount2:
- kfree(sbi->s_debts);
for (i = 0; i < db_count; i++)
brelse(sbi->s_group_desc[i]);
kfree(sbi->s_group_desc);
failed_mount:
#ifdef CONFIG_QUOTA
- for (i = 0; i < MAXQUOTAS; i++) {
- if (sbi->s_qf_names[i])
- kfree(sbi->s_qf_names[i]);
- }
+ for (i = 0; i < MAXQUOTAS; i++)
+ kfree(sbi->s_qf_names[i]);
#endif
ext3_blkdev_remove(sbi);
brelse(bh);
out_fail:
sb->s_fs_info = NULL;
kfree(sbi);
+ lock_kernel();
return -EINVAL;
}
* initial mount, once the journal has been initialised but before we've
* done any recovery; and again on any subsequent remount.
*/
-static void ext3_init_journal_params(struct ext3_sb_info *sbi,
- journal_t *journal)
+static void ext3_init_journal_params(struct super_block *sb, journal_t *journal)
{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+
if (sbi->s_commit_interval)
journal->j_commit_interval = sbi->s_commit_interval;
/* We could also set up an ext3-specific default for the commit
* interval here, but for now we'll just fall back to the jbd
* default. */
-}
+ spin_lock(&journal->j_state_lock);
+ if (test_opt(sb, BARRIER))
+ journal->j_flags |= JFS_BARRIER;
+ else
+ journal->j_flags &= ~JFS_BARRIER;
+ spin_unlock(&journal->j_state_lock);
+}
static journal_t *ext3_get_journal(struct super_block *sb, int journal_inum)
{
if (!journal) {
printk(KERN_ERR "EXT3-fs: Could not load journal inode\n");
iput(journal_inode);
+ return NULL;
}
journal->j_private = sb;
- ext3_init_journal_params(EXT3_SB(sb), journal);
+ ext3_init_journal_params(sb, journal);
return journal;
}
printk(KERN_ERR "EXT3-fs: I/O error on journal device\n");
goto out_journal;
}
- if (ntohl(journal->j_superblock->s_nr_users) != 1) {
+ if (be32_to_cpu(journal->j_superblock->s_nr_users) != 1) {
printk(KERN_ERR "EXT3-fs: External journal has more than one "
"user (unsupported) - %d\n",
- ntohl(journal->j_superblock->s_nr_users));
+ be32_to_cpu(journal->j_superblock->s_nr_users));
goto out_journal;
}
EXT3_SB(sb)->journal_bdev = bdev;
- ext3_init_journal_params(EXT3_SB(sb), journal);
+ ext3_init_journal_params(sb, journal);
return journal;
out_journal:
journal_destroy(journal);
static void ext3_mark_recovery_complete(struct super_block * sb,
struct ext3_super_block * es)
{
- journal_flush(EXT3_SB(sb)->s_journal);
+ journal_t *journal = EXT3_SB(sb)->s_journal;
+
+ journal_lock_updates(journal);
+ journal_flush(journal);
if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER) &&
sb->s_flags & MS_RDONLY) {
EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
sb->s_dirt = 0;
ext3_commit_super(sb, es, 1);
}
+ journal_unlock_updates(journal);
}
/*
* This implicitly triggers the writebehind on sync().
*/
-void ext3_write_super (struct super_block * sb)
+static void ext3_write_super (struct super_block * sb)
{
if (down_trylock(&sb->s_lock) == 0)
BUG();
* LVM calls this function before a (read-only) snapshot is created. This
* gives us a chance to flush the journal completely and mark the fs clean.
*/
-void ext3_write_super_lockfs(struct super_block *sb)
+static void ext3_write_super_lockfs(struct super_block *sb)
{
sb->s_dirt = 0;
* Called by LVM after the snapshot is done. We need to reset the RECOVER
* flag here, even though the filesystem is not technically dirty yet.
*/
-void ext3_unlockfs(struct super_block *sb)
+static void ext3_unlockfs(struct super_block *sb)
{
if (!(sb->s_flags & MS_RDONLY)) {
lock_super(sb);
}
}
-int ext3_remount (struct super_block * sb, int * flags, char * data)
+static int ext3_remount (struct super_block * sb, int * flags, char * data)
{
struct ext3_super_block * es;
struct ext3_sb_info *sbi = EXT3_SB(sb);
unsigned long tmp;
+ unsigned long n_blocks_count = 0;
/*
* Allow the "check" option to be passed as a remount option.
*/
- if (!parse_options(data, sb, &tmp, 1))
+ if (!parse_options(data, sb, &tmp, &n_blocks_count, 1))
return -EINVAL;
if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
es = sbi->s_es;
- ext3_init_journal_params(sbi, sbi->s_journal);
+ ext3_init_journal_params(sb, sbi->s_journal);
- if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+ if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY) ||
+ n_blocks_count > le32_to_cpu(es->s_blocks_count)) {
if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
return -EROFS;
ext3_mark_recovery_complete(sb, es);
} else {
- int ret;
+ __le32 ret;
if ((ret = EXT3_HAS_RO_COMPAT_FEATURE(sb,
~EXT3_FEATURE_RO_COMPAT_SUPP))) {
printk(KERN_WARNING "EXT3-fs: %s: couldn't "
"remount RDWR because of unsupported "
"optional features (%x).\n",
- sb->s_id, ret);
+ sb->s_id, le32_to_cpu(ret));
return -EROFS;
}
/*
*/
ext3_clear_journal_err(sb, es);
sbi->s_mount_state = le16_to_cpu(es->s_state);
+ if ((ret = ext3_group_extend(sb, es, n_blocks_count)))
+ return ret;
if (!ext3_setup_super (sb, es, 0))
sb->s_flags &= ~MS_RDONLY;
}
return 0;
}
-int ext3_statfs (struct super_block * sb, struct kstatfs * buf)
+static int ext3_statfs (struct super_block * sb, struct kstatfs * buf)
{
struct ext3_super_block *es = EXT3_SB(sb)->s_es;
unsigned long overhead;
if (test_opt (sb, MINIX_DF))
overhead = 0;
else {
+ unsigned long ngroups;
+ ngroups = EXT3_SB(sb)->s_groups_count;
+ smp_rmb();
+
/*
* Compute the overhead (FS structures)
*/
* block group descriptors. If the sparse superblocks
* feature is turned on, then not all groups have this.
*/
- for (i = 0; i < EXT3_SB(sb)->s_groups_count; i++)
+ for (i = 0; i < ngroups; i++) {
overhead += ext3_bg_has_super(sb, i) +
ext3_bg_num_gdb(sb, i);
+ cond_resched();
+ }
/*
* Every block group has an inode bitmap, a block
* bitmap, and an inode table.
*/
- overhead += (EXT3_SB(sb)->s_groups_count *
- (2 + EXT3_SB(sb)->s_itb_per_group));
+ overhead += (ngroups * (2 + EXT3_SB(sb)->s_itb_per_group));
}
buf->f_type = EXT3_SUPER_MAGIC;
static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]->f_dentry->d_inode;
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
}
static int ext3_dquot_initialize(struct inode *inode, int type)
{
int ret, err;
handle_t *handle;
+ struct inode *inode;
- handle = ext3_journal_start(dquot_to_inode(dquot),
+ inode = dquot_to_inode(dquot);
+ handle = ext3_journal_start(inode,
EXT3_QUOTA_TRANS_BLOCKS);
if (IS_ERR(handle))
return PTR_ERR(handle);
return ret;
}
+static int ext3_acquire_dquot(struct dquot *dquot)
+{
+ int ret, err;
+ handle_t *handle;
+
+ handle = ext3_journal_start(dquot_to_inode(dquot),
+ EXT3_QUOTA_INIT_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ ret = dquot_acquire(dquot);
+ err = ext3_journal_stop(handle);
+ if (!ret)
+ ret = err;
+ return ret;
+}
+
+static int ext3_release_dquot(struct dquot *dquot)
+{
+ int ret, err;
+ handle_t *handle;
+
+ handle = ext3_journal_start(dquot_to_inode(dquot),
+ EXT3_QUOTA_INIT_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ ret = dquot_release(dquot);
+ err = ext3_journal_stop(handle);
+ if (!ret)
+ ret = err;
+ return ret;
+}
+
static int ext3_mark_dquot_dirty(struct dquot *dquot)
{
/* Are we journalling quotas? */
if (IS_ERR(dentry))
return PTR_ERR(dentry);
err = vfs_quota_on_mount(type, EXT3_SB(sb)->s_jquota_fmt, dentry);
- if (err)
- dput(dentry);
- /* We keep the dentry reference if everything went ok - we drop it
- * on quota_off time */
- return err;
-}
-
-/* Turn quotas off during mount time */
-static int ext3_quota_off_mount(struct super_block *sb, int type)
-{
- int err;
- struct dentry *dentry;
-
- dentry = sb_dqopt(sb)->files[type]->f_dentry;
- err = vfs_quota_off_mount(sb, type);
- /* We invalidate dentry - it has at least wrong hash... */
+ /* Now invalidate and put the dentry - quota got its own reference
+ * to inode and dentry has at least wrong hash so we had better
+ * throw it away */
d_invalidate(dentry);
dput(dentry);
return err;
if (err)
return err;
/* Quotafile not on the same filesystem? */
- if (nd.mnt->mnt_sb != sb)
+ if (nd.mnt->mnt_sb != sb) {
+ path_release(&nd);
return -EXDEV;
+ }
/* Quotafile not of fs root? */
if (nd.dentry->d_parent->d_inode != sb->s_root->d_inode)
printk(KERN_WARNING
"EXT3-fs: Quota file not on filesystem root. "
"Journalled quota will not work.\n");
- if (!ext3_should_journal_data(nd.dentry->d_inode))
- printk(KERN_WARNING "EXT3-fs: Quota file does not have "
- "data-journalling. Journalled quota will not work.\n");
path_release(&nd);
return vfs_quota_on(sb, type, format_id, path);
}
+/* Read data from quotafile - avoid pagecache and such because we cannot afford
+ * acquiring the locks... As quota files are never truncated and quota code
+ * itself serializes the operations (and noone else should touch the files)
+ * we don't have to be afraid of races */
+static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
+ size_t len, loff_t off)
+{
+ struct inode *inode = sb_dqopt(sb)->files[type];
+ sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
+ int err = 0;
+ int offset = off & (sb->s_blocksize - 1);
+ int tocopy;
+ size_t toread;
+ struct buffer_head *bh;
+ loff_t i_size = i_size_read(inode);
+
+ if (off > i_size)
+ return 0;
+ if (off+len > i_size)
+ len = i_size-off;
+ toread = len;
+ while (toread > 0) {
+ tocopy = sb->s_blocksize - offset < toread ?
+ sb->s_blocksize - offset : toread;
+ bh = ext3_bread(NULL, inode, blk, 0, &err);
+ if (err)
+ return err;
+ if (!bh) /* A hole? */
+ memset(data, 0, tocopy);
+ else
+ memcpy(data, bh->b_data+offset, tocopy);
+ brelse(bh);
+ offset = 0;
+ toread -= tocopy;
+ data += tocopy;
+ blk++;
+ }
+ return len;
+}
+
+/* Write to quotafile (we know the transaction is already started and has
+ * enough credits) */
+static ssize_t ext3_quota_write(struct super_block *sb, int type,
+ const char *data, size_t len, loff_t off)
+{
+ struct inode *inode = sb_dqopt(sb)->files[type];
+ sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb);
+ int err = 0;
+ int offset = off & (sb->s_blocksize - 1);
+ int tocopy;
+ int journal_quota = EXT3_SB(sb)->s_qf_names[type] != NULL;
+ size_t towrite = len;
+ struct buffer_head *bh;
+ handle_t *handle = journal_current_handle();
+
+ down(&inode->i_sem);
+ while (towrite > 0) {
+ tocopy = sb->s_blocksize - offset < towrite ?
+ sb->s_blocksize - offset : towrite;
+ bh = ext3_bread(handle, inode, blk, 1, &err);
+ if (!bh)
+ goto out;
+ if (journal_quota) {
+ err = ext3_journal_get_write_access(handle, bh);
+ if (err) {
+ brelse(bh);
+ goto out;
+ }
+ }
+ lock_buffer(bh);
+ memcpy(bh->b_data+offset, data, tocopy);
+ flush_dcache_page(bh->b_page);
+ unlock_buffer(bh);
+ if (journal_quota)
+ err = ext3_journal_dirty_metadata(handle, bh);
+ else {
+ /* Always do at least ordered writes for quotas */
+ err = ext3_journal_dirty_data(handle, bh);
+ mark_buffer_dirty(bh);
+ }
+ brelse(bh);
+ if (err)
+ goto out;
+ offset = 0;
+ towrite -= tocopy;
+ data += tocopy;
+ blk++;
+ }
+out:
+ if (len == towrite)
+ return err;
+ if (inode->i_size < off+len-towrite) {
+ i_size_write(inode, off+len-towrite);
+ EXT3_I(inode)->i_disksize = inode->i_size;
+ }
+ inode->i_version++;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ ext3_mark_inode_dirty(handle, inode);
+ up(&inode->i_sem);
+ return len - towrite;
+}
+
#endif
static struct super_block *ext3_get_sb(struct file_system_type *fs_type,