Commit 0db1ff22 authored by Theodore Ts'o's avatar Theodore Ts'o

ext4: add shutdown bit and check for it

Add a shutdown bit that will cause ext4 processing to fail immediately
with EIO.
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 9549a168
...@@ -1836,6 +1836,12 @@ static inline bool ext4_has_incompat_features(struct super_block *sb) ...@@ -1836,6 +1836,12 @@ static inline bool ext4_has_incompat_features(struct super_block *sb)
* Superblock flags * Superblock flags
*/ */
#define EXT4_FLAGS_RESIZING 0 #define EXT4_FLAGS_RESIZING 0
#define EXT4_FLAGS_SHUTDOWN 1
static inline int ext4_forced_shutdown(struct ext4_sb_info *sbi)
{
return test_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
}
/* /*
......
...@@ -43,6 +43,10 @@ static int ext4_journal_check_start(struct super_block *sb) ...@@ -43,6 +43,10 @@ static int ext4_journal_check_start(struct super_block *sb)
journal_t *journal; journal_t *journal;
might_sleep(); might_sleep();
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
return -EIO;
if (sb->s_flags & MS_RDONLY) if (sb->s_flags & MS_RDONLY)
return -EROFS; return -EROFS;
WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE); WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);
...@@ -161,6 +165,13 @@ int __ext4_journal_get_write_access(const char *where, unsigned int line, ...@@ -161,6 +165,13 @@ int __ext4_journal_get_write_access(const char *where, unsigned int line,
might_sleep(); might_sleep();
if (ext4_handle_valid(handle)) { if (ext4_handle_valid(handle)) {
struct super_block *sb;
sb = handle->h_transaction->t_journal->j_private;
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb)))) {
jbd2_journal_abort_handle(handle);
return -EIO;
}
err = jbd2_journal_get_write_access(handle, bh); err = jbd2_journal_get_write_access(handle, bh);
if (err) if (err)
ext4_journal_abort_handle(where, line, __func__, bh, ext4_journal_abort_handle(where, line, __func__, bh,
......
...@@ -57,6 +57,9 @@ static ssize_t ext4_dax_read_iter(struct kiocb *iocb, struct iov_iter *to) ...@@ -57,6 +57,9 @@ static ssize_t ext4_dax_read_iter(struct kiocb *iocb, struct iov_iter *to)
static ssize_t ext4_file_read_iter(struct kiocb *iocb, struct iov_iter *to) static ssize_t ext4_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
{ {
if (unlikely(ext4_forced_shutdown(EXT4_SB(file_inode(iocb->ki_filp)->i_sb))))
return -EIO;
if (!iov_iter_count(to)) if (!iov_iter_count(to))
return 0; /* skip atime */ return 0; /* skip atime */
...@@ -213,6 +216,9 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -213,6 +216,9 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
int overwrite = 0; int overwrite = 0;
ssize_t ret; ssize_t ret;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
#ifdef CONFIG_FS_DAX #ifdef CONFIG_FS_DAX
if (IS_DAX(inode)) if (IS_DAX(inode))
return ext4_dax_write_iter(iocb, from); return ext4_dax_write_iter(iocb, from);
...@@ -348,6 +354,9 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -348,6 +354,9 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
if (ext4_encrypted_inode(inode)) { if (ext4_encrypted_inode(inode)) {
int err = fscrypt_get_encryption_info(inode); int err = fscrypt_get_encryption_info(inode);
if (err) if (err)
...@@ -375,6 +384,9 @@ static int ext4_file_open(struct inode * inode, struct file * filp) ...@@ -375,6 +384,9 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
char buf[64], *cp; char buf[64], *cp;
int ret; int ret;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) && if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
!(sb->s_flags & MS_RDONLY))) { !(sb->s_flags & MS_RDONLY))) {
sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED; sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED;
......
...@@ -100,6 +100,9 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -100,6 +100,9 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
tid_t commit_tid; tid_t commit_tid;
bool needs_barrier = false; bool needs_barrier = false;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
J_ASSERT(ext4_journal_current_handle() == NULL); J_ASSERT(ext4_journal_current_handle() == NULL);
trace_ext4_sync_file_enter(file, datasync); trace_ext4_sync_file_enter(file, datasync);
......
...@@ -764,6 +764,9 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir, ...@@ -764,6 +764,9 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
if (!dir || !dir->i_nlink) if (!dir || !dir->i_nlink)
return ERR_PTR(-EPERM); return ERR_PTR(-EPERM);
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return ERR_PTR(-EIO);
if ((ext4_encrypted_inode(dir) || if ((ext4_encrypted_inode(dir) ||
DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) && DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) { (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
......
...@@ -215,6 +215,9 @@ static void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc, ...@@ -215,6 +215,9 @@ static void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
struct ext4_inode *raw_inode; struct ext4_inode *raw_inode;
int cp_len = 0; int cp_len = 0;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return;
BUG_ON(!EXT4_I(inode)->i_inline_off); BUG_ON(!EXT4_I(inode)->i_inline_off);
BUG_ON(pos + len > EXT4_I(inode)->i_inline_size); BUG_ON(pos + len > EXT4_I(inode)->i_inline_size);
......
...@@ -1189,6 +1189,9 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping, ...@@ -1189,6 +1189,9 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
pgoff_t index; pgoff_t index;
unsigned from, to; unsigned from, to;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
trace_ext4_write_begin(inode, pos, len, flags); trace_ext4_write_begin(inode, pos, len, flags);
/* /*
* Reserve one block more for addition to orphan list in case * Reserve one block more for addition to orphan list in case
...@@ -2047,6 +2050,12 @@ static int ext4_writepage(struct page *page, ...@@ -2047,6 +2050,12 @@ static int ext4_writepage(struct page *page,
struct ext4_io_submit io_submit; struct ext4_io_submit io_submit;
bool keep_towrite = false; bool keep_towrite = false;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
ext4_invalidatepage(page, 0, PAGE_SIZE);
unlock_page(page);
return -EIO;
}
trace_ext4_writepage(page); trace_ext4_writepage(page);
size = i_size_read(inode); size = i_size_read(inode);
if (page->index == size >> PAGE_SHIFT) if (page->index == size >> PAGE_SHIFT)
...@@ -2422,7 +2431,8 @@ static int mpage_map_and_submit_extent(handle_t *handle, ...@@ -2422,7 +2431,8 @@ static int mpage_map_and_submit_extent(handle_t *handle,
if (err < 0) { if (err < 0) {
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
if (EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED) if (ext4_forced_shutdown(EXT4_SB(sb)) ||
EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED)
goto invalidate_dirty_pages; goto invalidate_dirty_pages;
/* /*
* Let the uper layers retry transient errors. * Let the uper layers retry transient errors.
...@@ -2644,6 +2654,9 @@ static int ext4_writepages(struct address_space *mapping, ...@@ -2644,6 +2654,9 @@ static int ext4_writepages(struct address_space *mapping,
struct blk_plug plug; struct blk_plug plug;
bool give_up_on_write = false; bool give_up_on_write = false;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
percpu_down_read(&sbi->s_journal_flag_rwsem); percpu_down_read(&sbi->s_journal_flag_rwsem);
trace_ext4_writepages(inode, wbc); trace_ext4_writepages(inode, wbc);
...@@ -2680,7 +2693,8 @@ static int ext4_writepages(struct address_space *mapping, ...@@ -2680,7 +2693,8 @@ static int ext4_writepages(struct address_space *mapping,
* *never* be called, so if that ever happens, we would want * *never* be called, so if that ever happens, we would want
* the stack trace. * the stack trace.
*/ */
if (unlikely(sbi->s_mount_flags & EXT4_MF_FS_ABORTED)) { if (unlikely(ext4_forced_shutdown(EXT4_SB(mapping->host->i_sb)) ||
sbi->s_mount_flags & EXT4_MF_FS_ABORTED)) {
ret = -EROFS; ret = -EROFS;
goto out_writepages; goto out_writepages;
} }
...@@ -2905,6 +2919,9 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, ...@@ -2905,6 +2919,9 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
handle_t *handle; handle_t *handle;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
index = pos >> PAGE_SHIFT; index = pos >> PAGE_SHIFT;
if (ext4_nonda_switch(inode->i_sb) || if (ext4_nonda_switch(inode->i_sb) ||
...@@ -5212,6 +5229,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -5212,6 +5229,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
int orphan = 0; int orphan = 0;
const unsigned int ia_valid = attr->ia_valid; const unsigned int ia_valid = attr->ia_valid;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
error = setattr_prepare(dentry, attr); error = setattr_prepare(dentry, attr);
if (error) if (error)
return error; return error;
...@@ -5498,6 +5518,9 @@ int ext4_mark_iloc_dirty(handle_t *handle, ...@@ -5498,6 +5518,9 @@ int ext4_mark_iloc_dirty(handle_t *handle,
{ {
int err = 0; int err = 0;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
if (IS_I_VERSION(inode)) if (IS_I_VERSION(inode))
inode_inc_iversion(inode); inode_inc_iversion(inode);
...@@ -5521,6 +5544,9 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, ...@@ -5521,6 +5544,9 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
{ {
int err; int err;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
err = ext4_get_inode_loc(inode, iloc); err = ext4_get_inode_loc(inode, iloc);
if (!err) { if (!err) {
BUFFER_TRACE(iloc->bh, "get_write_access"); BUFFER_TRACE(iloc->bh, "get_write_access");
......
...@@ -2939,6 +2939,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -2939,6 +2939,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
handle_t *handle = NULL; handle_t *handle = NULL;
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return -EIO;
/* Initialize quotas before so that eventual writes go in /* Initialize quotas before so that eventual writes go in
* separate transaction */ * separate transaction */
retval = dquot_initialize(dir); retval = dquot_initialize(dir);
...@@ -3012,6 +3015,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry) ...@@ -3012,6 +3015,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
struct ext4_dir_entry_2 *de; struct ext4_dir_entry_2 *de;
handle_t *handle = NULL; handle_t *handle = NULL;
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return -EIO;
trace_ext4_unlink_enter(dir, dentry); trace_ext4_unlink_enter(dir, dentry);
/* Initialize quotas before so that eventual writes go /* Initialize quotas before so that eventual writes go
* in separate transaction */ * in separate transaction */
...@@ -3082,6 +3088,9 @@ static int ext4_symlink(struct inode *dir, ...@@ -3082,6 +3088,9 @@ static int ext4_symlink(struct inode *dir,
struct fscrypt_str disk_link; struct fscrypt_str disk_link;
struct fscrypt_symlink_data *sd = NULL; struct fscrypt_symlink_data *sd = NULL;
if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
return -EIO;
disk_link.len = len + 1; disk_link.len = len + 1;
disk_link.name = (char *) symname; disk_link.name = (char *) symname;
...@@ -3874,6 +3883,9 @@ static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry, ...@@ -3874,6 +3883,9 @@ static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry, struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags) unsigned int flags)
{ {
if (unlikely(ext4_forced_shutdown(EXT4_SB(old_dir->i_sb))))
return -EIO;
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
return -EINVAL; return -EINVAL;
......
...@@ -158,7 +158,7 @@ static int ext4_end_io(ext4_io_end_t *io) ...@@ -158,7 +158,7 @@ static int ext4_end_io(ext4_io_end_t *io)
io->handle = NULL; /* Following call will use up the handle */ io->handle = NULL; /* Following call will use up the handle */
ret = ext4_convert_unwritten_extents(handle, inode, offset, size); ret = ext4_convert_unwritten_extents(handle, inode, offset, size);
if (ret < 0) { if (ret < 0 && !ext4_forced_shutdown(EXT4_SB(inode->i_sb))) {
ext4_msg(inode->i_sb, KERN_EMERG, ext4_msg(inode->i_sb, KERN_EMERG,
"failed to convert unwritten extents to written " "failed to convert unwritten extents to written "
"extents -- potential data loss! " "extents -- potential data loss! "
......
...@@ -438,6 +438,9 @@ void __ext4_error(struct super_block *sb, const char *function, ...@@ -438,6 +438,9 @@ void __ext4_error(struct super_block *sb, const char *function,
struct va_format vaf; struct va_format vaf;
va_list args; va_list args;
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
return;
if (ext4_error_ratelimit(sb)) { if (ext4_error_ratelimit(sb)) {
va_start(args, fmt); va_start(args, fmt);
vaf.fmt = fmt; vaf.fmt = fmt;
...@@ -459,6 +462,9 @@ void __ext4_error_inode(struct inode *inode, const char *function, ...@@ -459,6 +462,9 @@ void __ext4_error_inode(struct inode *inode, const char *function,
struct va_format vaf; struct va_format vaf;
struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return;
es->s_last_error_ino = cpu_to_le32(inode->i_ino); es->s_last_error_ino = cpu_to_le32(inode->i_ino);
es->s_last_error_block = cpu_to_le64(block); es->s_last_error_block = cpu_to_le64(block);
if (ext4_error_ratelimit(inode->i_sb)) { if (ext4_error_ratelimit(inode->i_sb)) {
...@@ -491,6 +497,9 @@ void __ext4_error_file(struct file *file, const char *function, ...@@ -491,6 +497,9 @@ void __ext4_error_file(struct file *file, const char *function,
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
char pathname[80], *path; char pathname[80], *path;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return;
es = EXT4_SB(inode->i_sb)->s_es; es = EXT4_SB(inode->i_sb)->s_es;
es->s_last_error_ino = cpu_to_le32(inode->i_ino); es->s_last_error_ino = cpu_to_le32(inode->i_ino);
if (ext4_error_ratelimit(inode->i_sb)) { if (ext4_error_ratelimit(inode->i_sb)) {
...@@ -567,6 +576,9 @@ void __ext4_std_error(struct super_block *sb, const char *function, ...@@ -567,6 +576,9 @@ void __ext4_std_error(struct super_block *sb, const char *function,
char nbuf[16]; char nbuf[16];
const char *errstr; const char *errstr;
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
return;
/* Special case: if the error is EROFS, and we're not already /* Special case: if the error is EROFS, and we're not already
* inside a transaction, then there's really no point in logging * inside a transaction, then there's really no point in logging
* an error. */ * an error. */
...@@ -600,6 +612,9 @@ void __ext4_abort(struct super_block *sb, const char *function, ...@@ -600,6 +612,9 @@ void __ext4_abort(struct super_block *sb, const char *function,
struct va_format vaf; struct va_format vaf;
va_list args; va_list args;
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
return;
save_error_info(sb, function, line); save_error_info(sb, function, line);
va_start(args, fmt); va_start(args, fmt);
vaf.fmt = fmt; vaf.fmt = fmt;
...@@ -695,6 +710,9 @@ __acquires(bitlock) ...@@ -695,6 +710,9 @@ __acquires(bitlock)
va_list args; va_list args;
struct ext4_super_block *es = EXT4_SB(sb)->s_es; struct ext4_super_block *es = EXT4_SB(sb)->s_es;
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
return;
es->s_last_error_ino = cpu_to_le32(ino); es->s_last_error_ino = cpu_to_le32(ino);
es->s_last_error_block = cpu_to_le64(block); es->s_last_error_block = cpu_to_le64(block);
__save_error_info(sb, function, line); __save_error_info(sb, function, line);
...@@ -4717,6 +4735,9 @@ static int ext4_sync_fs(struct super_block *sb, int wait) ...@@ -4717,6 +4735,9 @@ static int ext4_sync_fs(struct super_block *sb, int wait)
bool needs_barrier = false; bool needs_barrier = false;
struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_sb_info *sbi = EXT4_SB(sb);
if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
return 0;
trace_ext4_sync_fs(sb, wait); trace_ext4_sync_fs(sb, wait);
flush_workqueue(sbi->rsv_conversion_wq); flush_workqueue(sbi->rsv_conversion_wq);
/* /*
......
...@@ -411,6 +411,9 @@ ext4_xattr_get(struct inode *inode, int name_index, const char *name, ...@@ -411,6 +411,9 @@ ext4_xattr_get(struct inode *inode, int name_index, const char *name,
{ {
int error; int error;
if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
return -EIO;
if (strlen(name) > 255) if (strlen(name) > 255)
return -ERANGE; return -ERANGE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment