ext4: Fix potential memory leak in ext4_fill_super
[pandora-kernel.git] / fs / ext4 / super.c
index 4e8983a..d573f6c 100644 (file)
@@ -248,7 +248,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
        journal = EXT4_SB(sb)->s_journal;
        if (journal) {
                if (is_journal_aborted(journal)) {
-                       ext4_abort(sb, __func__, "Detected aborted journal");
+                       ext4_abort(sb, "Detected aborted journal");
                        return ERR_PTR(-EROFS);
                }
                return jbd2_journal_start(journal, nblocks);
@@ -262,7 +262,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
  * that sync() will call the filesystem's write_super callback if
  * appropriate.
  */
-int __ext4_journal_stop(const char *where, handle_t *handle)
+int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
 {
        struct super_block *sb;
        int err;
@@ -279,12 +279,13 @@ int __ext4_journal_stop(const char *where, handle_t *handle)
        if (!err)
                err = rc;
        if (err)
-               __ext4_std_error(sb, where, err);
+               __ext4_std_error(sb, where, line, err);
        return err;
 }
 
-void ext4_journal_abort_handle(const char *caller, const char *err_fn,
-               struct buffer_head *bh, handle_t *handle, int err)
+void ext4_journal_abort_handle(const char *caller, unsigned int line,
+                              const char *err_fn, struct buffer_head *bh,
+                              handle_t *handle, int err)
 {
        char nbuf[16];
        const char *errstr = ext4_decode_error(NULL, err, nbuf);
@@ -300,12 +301,47 @@ void ext4_journal_abort_handle(const char *caller, const char *err_fn,
        if (is_handle_aborted(handle))
                return;
 
-       printk(KERN_ERR "%s: aborting transaction: %s in %s\n",
-              caller, errstr, err_fn);
+       printk(KERN_ERR "%s:%d: aborting transaction: %s in %s\n",
+              caller, line, errstr, err_fn);
 
        jbd2_journal_abort_handle(handle);
 }
 
+static void __save_error_info(struct super_block *sb, const char *func,
+                           unsigned int line)
+{
+       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+
+       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
+       es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
+       es->s_last_error_time = cpu_to_le32(get_seconds());
+       strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func));
+       es->s_last_error_line = cpu_to_le32(line);
+       if (!es->s_first_error_time) {
+               es->s_first_error_time = es->s_last_error_time;
+               strncpy(es->s_first_error_func, func,
+                       sizeof(es->s_first_error_func));
+               es->s_first_error_line = cpu_to_le32(line);
+               es->s_first_error_ino = es->s_last_error_ino;
+               es->s_first_error_block = es->s_last_error_block;
+       }
+       /*
+        * Start the daily error reporting function if it hasn't been
+        * started already
+        */
+       if (!es->s_error_count)
+               mod_timer(&EXT4_SB(sb)->s_err_report, jiffies + 24*60*60*HZ);
+       es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1);
+}
+
+static void save_error_info(struct super_block *sb, const char *func,
+                           unsigned int line)
+{
+       __save_error_info(sb, func, line);
+       ext4_commit_super(sb, 1);
+}
+
+
 /* Deal with the reporting of failure conditions on a filesystem such as
  * inconsistencies detected or read IO failures.
  *
@@ -323,11 +359,6 @@ void ext4_journal_abort_handle(const char *caller, const char *err_fn,
 
 static void ext4_handle_error(struct super_block *sb)
 {
-       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
-
-       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-       es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
-
        if (sb->s_flags & MS_RDONLY)
                return;
 
@@ -342,19 +373,19 @@ static void ext4_handle_error(struct super_block *sb)
                ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
                sb->s_flags |= MS_RDONLY;
        }
-       ext4_commit_super(sb, 1);
        if (test_opt(sb, ERRORS_PANIC))
                panic("EXT4-fs (device %s): panic forced after error\n",
                        sb->s_id);
 }
 
 void __ext4_error(struct super_block *sb, const char *function,
-               const char *fmt, ...)
+                 unsigned int line, const char *fmt, ...)
 {
        va_list args;
 
        va_start(args, fmt);
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
+       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: comm %s: ",
+              sb->s_id, function, line, current->comm);
        vprintk(fmt, args);
        printk("\n");
        va_end(args);
@@ -362,14 +393,22 @@ void __ext4_error(struct super_block *sb, const char *function,
        ext4_handle_error(sb);
 }
 
-void ext4_error_inode(const char *function, struct inode *inode,
+void ext4_error_inode(struct inode *inode, const char *function,
+                     unsigned int line, ext4_fsblk_t block,
                      const char *fmt, ...)
 {
        va_list args;
+       struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
 
+       es->s_last_error_ino = cpu_to_le32(inode->i_ino);
+       es->s_last_error_block = cpu_to_le64(block);
+       save_error_info(inode->i_sb, function, line);
        va_start(args, fmt);
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s: inode #%lu: (comm %s) ",
-              inode->i_sb->s_id, function, inode->i_ino, current->comm);
+       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: ",
+              inode->i_sb->s_id, function, line, inode->i_ino);
+       if (block)
+               printk("block %llu: ", block);
+       printk("comm %s: ", current->comm);
        vprintk(fmt, args);
        printk("\n");
        va_end(args);
@@ -377,20 +416,26 @@ void ext4_error_inode(const char *function, struct inode *inode,
        ext4_handle_error(inode->i_sb);
 }
 
-void ext4_error_file(const char *function, struct file *file,
-                    const char *fmt, ...)
+void ext4_error_file(struct file *file, const char *function,
+                    unsigned int line, const char *fmt, ...)
 {
        va_list args;
+       struct ext4_super_block *es;
        struct inode *inode = file->f_dentry->d_inode;
        char pathname[80], *path;
 
+       es = EXT4_SB(inode->i_sb)->s_es;
+       es->s_last_error_ino = cpu_to_le32(inode->i_ino);
+       save_error_info(inode->i_sb, function, line);
        va_start(args, fmt);
        path = d_path(&(file->f_path), pathname, sizeof(pathname));
        if (!path)
                path = "(unknown)";
        printk(KERN_CRIT
-              "EXT4-fs error (device %s): %s: inode #%lu (comm %s path %s): ",
-              inode->i_sb->s_id, function, inode->i_ino, current->comm, path);
+              "EXT4-fs error (device %s): %s:%d: inode #%lu "
+              "(comm %s path %s): ",
+              inode->i_sb->s_id, function, line, inode->i_ino,
+              current->comm, path);
        vprintk(fmt, args);
        printk("\n");
        va_end(args);
@@ -435,7 +480,8 @@ static const char *ext4_decode_error(struct super_block *sb, int errno,
 /* __ext4_std_error decodes expected errors from journaling functions
  * automatically and invokes the appropriate error response.  */
 
-void __ext4_std_error(struct super_block *sb, const char *function, int errno)
+void __ext4_std_error(struct super_block *sb, const char *function,
+                     unsigned int line, int errno)
 {
        char nbuf[16];
        const char *errstr;
@@ -448,8 +494,9 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno)
                return;
 
        errstr = ext4_decode_error(sb, errno, nbuf);
-       printk(KERN_CRIT "EXT4-fs error (device %s) in %s: %s\n",
-              sb->s_id, function, errstr);
+       printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
+              sb->s_id, function, line, errstr);
+       save_error_info(sb, function, line);
 
        ext4_handle_error(sb);
 }
@@ -464,29 +511,29 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno)
  * case we take the easy way out and panic immediately.
  */
 
-void ext4_abort(struct super_block *sb, const char *function,
-               const char *fmt, ...)
+void __ext4_abort(struct super_block *sb, const char *function,
+               unsigned int line, const char *fmt, ...)
 {
        va_list args;
 
+       save_error_info(sb, function, line);
        va_start(args, fmt);
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
+       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id,
+              function, line);
        vprintk(fmt, args);
        printk("\n");
        va_end(args);
 
+       if ((sb->s_flags & MS_RDONLY) == 0) {
+               ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
+               sb->s_flags |= MS_RDONLY;
+               EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
+               if (EXT4_SB(sb)->s_journal)
+                       jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
+               save_error_info(sb, function, line);
+       }
        if (test_opt(sb, ERRORS_PANIC))
                panic("EXT4-fs panic from previous error\n");
-
-       if (sb->s_flags & MS_RDONLY)
-               return;
-
-       ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only");
-       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-       sb->s_flags |= MS_RDONLY;
-       EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED;
-       if (EXT4_SB(sb)->s_journal)
-               jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO);
 }
 
 void ext4_msg (struct super_block * sb, const char *prefix,
@@ -502,38 +549,47 @@ void ext4_msg (struct super_block * sb, const char *prefix,
 }
 
 void __ext4_warning(struct super_block *sb, const char *function,
-                 const char *fmt, ...)
+                   unsigned int line, const char *fmt, ...)
 {
        va_list args;
 
        va_start(args, fmt);
-       printk(KERN_WARNING "EXT4-fs warning (device %s): %s: ",
-              sb->s_id, function);
+       printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: ",
+              sb->s_id, function, line);
        vprintk(fmt, args);
        printk("\n");
        va_end(args);
 }
 
-void ext4_grp_locked_error(struct super_block *sb, ext4_group_t grp,
-                          const char *function, const char *fmt, ...)
+void __ext4_grp_locked_error(const char *function, unsigned int line,
+                            struct super_block *sb, ext4_group_t grp,
+                            unsigned long ino, ext4_fsblk_t block,
+                            const char *fmt, ...)
 __releases(bitlock)
 __acquires(bitlock)
 {
        va_list args;
        struct ext4_super_block *es = EXT4_SB(sb)->s_es;
 
+       es->s_last_error_ino = cpu_to_le32(ino);
+       es->s_last_error_block = cpu_to_le64(block);
+       __save_error_info(sb, function, line);
        va_start(args, fmt);
-       printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function);
+       printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u",
+              sb->s_id, function, line, grp);
+       if (ino)
+               printk("inode %lu: ", ino);
+       if (block)
+               printk("block %llu:", (unsigned long long) block);
        vprintk(fmt, args);
        printk("\n");
        va_end(args);
 
        if (test_opt(sb, ERRORS_CONT)) {
-               EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-               es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
                ext4_commit_super(sb, 0);
                return;
        }
+
        ext4_unlock_group(sb, grp);
        ext4_handle_error(sb);
        /*
@@ -660,8 +716,7 @@ static void ext4_put_super(struct super_block *sb)
                err = jbd2_journal_destroy(sbi->s_journal);
                sbi->s_journal = NULL;
                if (err < 0)
-                       ext4_abort(sb, __func__,
-                                  "Couldn't clean up the journal");
+                       ext4_abort(sb, "Couldn't clean up the journal");
        }
 
        ext4_release_system_zone(sb);
@@ -946,8 +1001,6 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)
                seq_puts(seq, ",journal_async_commit");
        else if (test_opt(sb, JOURNAL_CHECKSUM))
                seq_puts(seq, ",journal_checksum");
-       if (test_opt(sb, NOBH))
-               seq_puts(seq, ",nobh");
        if (test_opt(sb, I_VERSION))
                seq_puts(seq, ",i_version");
        if (!test_opt(sb, DELALLOC))
@@ -1624,10 +1677,12 @@ set_qf_format:
                        *n_blocks_count = option;
                        break;
                case Opt_nobh:
-                       set_opt(sbi->s_mount_opt, NOBH);
+                       ext4_msg(sb, KERN_WARNING,
+                                "Ignoring deprecated nobh option");
                        break;
                case Opt_bh:
-                       clear_opt(sbi->s_mount_opt, NOBH);
+                       ext4_msg(sb, KERN_WARNING,
+                                "Ignoring deprecated bh option");
                        break;
                case Opt_i_version:
                        set_opt(sbi->s_mount_opt, I_VERSION);
@@ -2431,6 +2486,53 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)
        return 1;
 }
 
+/*
+ * This function is called once a day if we have errors logged
+ * on the file system
+ */
+static void print_daily_error_info(unsigned long arg)
+{
+       struct super_block *sb = (struct super_block *) arg;
+       struct ext4_sb_info *sbi;
+       struct ext4_super_block *es;
+
+       sbi = EXT4_SB(sb);
+       es = sbi->s_es;
+
+       if (es->s_error_count)
+               ext4_msg(sb, KERN_NOTICE, "error count: %u",
+                        le32_to_cpu(es->s_error_count));
+       if (es->s_first_error_time) {
+               printk(KERN_NOTICE "EXT4-fs (%s): initial error at %u: %.*s:%d",
+                      sb->s_id, le32_to_cpu(es->s_first_error_time),
+                      (int) sizeof(es->s_first_error_func),
+                      es->s_first_error_func,
+                      le32_to_cpu(es->s_first_error_line));
+               if (es->s_first_error_ino)
+                       printk(": inode %u",
+                              le32_to_cpu(es->s_first_error_ino));
+               if (es->s_first_error_block)
+                       printk(": block %llu", (unsigned long long)
+                              le64_to_cpu(es->s_first_error_block));
+               printk("\n");
+       }
+       if (es->s_last_error_time) {
+               printk(KERN_NOTICE "EXT4-fs (%s): last error at %u: %.*s:%d",
+                      sb->s_id, le32_to_cpu(es->s_last_error_time),
+                      (int) sizeof(es->s_last_error_func),
+                      es->s_last_error_func,
+                      le32_to_cpu(es->s_last_error_line));
+               if (es->s_last_error_ino)
+                       printk(": inode %u",
+                              le32_to_cpu(es->s_last_error_ino));
+               if (es->s_last_error_block)
+                       printk(": block %llu", (unsigned long long)
+                              le64_to_cpu(es->s_last_error_block));
+               printk("\n");
+       }
+       mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ);  /* Once a day */
+}
+
 static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                                __releases(kernel_lock)
                                __acquires(kernel_lock)
@@ -2448,7 +2550,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        struct inode *root;
        char *cp;
        const char *descr;
-       int ret = -EINVAL;
+       int ret = -ENOMEM;
        int blocksize;
        unsigned int db_count;
        unsigned int i;
@@ -2459,13 +2561,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 
        sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
        if (!sbi)
-               return -ENOMEM;
+               goto out_free_orig;
 
        sbi->s_blockgroup_lock =
                kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
        if (!sbi->s_blockgroup_lock) {
                kfree(sbi);
-               return -ENOMEM;
+               goto out_free_orig;
        }
        sb->s_fs_info = sbi;
        sbi->s_mount_opt = 0;
@@ -2482,6 +2584,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        for (cp = sb->s_id; (cp = strchr(cp, '/'));)
                *cp = '!';
 
+       ret = -EINVAL;
        blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
        if (!blocksize) {
                ext4_msg(sb, KERN_ERR, "unable to set blocksize");
@@ -2912,18 +3015,7 @@ no_journal:
                ext4_msg(sb, KERN_ERR, "insufficient memory");
                goto failed_mount_wq;
        }
-       if (test_opt(sb, NOBH)) {
-               if (!(test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)) {
-                       ext4_msg(sb, KERN_WARNING, "Ignoring nobh option - "
-                               "its supported only with writeback mode");
-                       clear_opt(sbi->s_mount_opt, NOBH);
-               }
-               if (test_opt(sb, DIOREAD_NOLOCK)) {
-                       ext4_msg(sb, KERN_WARNING, "dioread_nolock option is "
-                               "not supported with nobh mode");
-                       goto failed_mount_wq;
-               }
-       }
+
        EXT4_SB(sb)->dio_unwritten_wq = create_workqueue("ext4-dio-unwritten");
        if (!EXT4_SB(sb)->dio_unwritten_wq) {
                printk(KERN_ERR "EXT4-fs: failed to create DIO workqueue\n");
@@ -3045,6 +3137,12 @@ no_journal:
        ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
                "Opts: %s", descr, orig_data);
 
+       init_timer(&sbi->s_err_report);
+       sbi->s_err_report.function = print_daily_error_info;
+       sbi->s_err_report.data = (unsigned long) sb;
+       if (es->s_error_count)
+               mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
+
        lock_kernel();
        kfree(orig_data);
        return 0;
@@ -3093,6 +3191,7 @@ out_fail:
        kfree(sbi->s_blockgroup_lock);
        kfree(sbi);
        lock_kernel();
+out_free_orig:
        kfree(orig_data);
        return ret;
 }
@@ -3327,8 +3426,17 @@ static int ext4_load_journal(struct super_block *sb,
 
        if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER))
                err = jbd2_journal_wipe(journal, !really_read_only);
-       if (!err)
+       if (!err) {
+               char *save = kmalloc(EXT4_S_ERR_LEN, GFP_KERNEL);
+               if (save)
+                       memcpy(save, ((char *) es) +
+                              EXT4_S_ERR_START, EXT4_S_ERR_LEN);
                err = jbd2_journal_load(journal);
+               if (save)
+                       memcpy(((char *) es) + EXT4_S_ERR_START,
+                              save, EXT4_S_ERR_LEN);
+               kfree(save);
+       }
 
        if (err) {
                ext4_msg(sb, KERN_ERR, "error loading journal");
@@ -3616,7 +3724,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        }
 
        if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
-               ext4_abort(sb, __func__, "Abort forced by user");
+               ext4_abort(sb, "Abort forced by user");
 
        sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
                (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);