All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/7] ext4: fix inline data error paths
@ 2017-02-05  7:34 Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 2/7] jbd2: don't leak modified metadata buffers on an aborted journal Theodore Ts'o
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o, stable

The write_end() function must always unlock the page and drop its ref
count, even on an error.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
---
 fs/ext4/inline.c |  9 ++++++++-
 fs/ext4/inode.c  | 20 +++++++++++++++-----
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 338cfd862adb..b777b8aa14ae 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -943,8 +943,15 @@ int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
 				  struct page *page)
 {
 	int i_size_changed = 0;
+	int ret;
 
-	copied = ext4_write_inline_data_end(inode, pos, len, copied, page);
+	ret = ext4_write_inline_data_end(inode, pos, len, copied, page);
+	if (ret < 0) {
+		unlock_page(page);
+		put_page(page);
+		return ret;
+	}
+	copied = ret;
 
 	/*
 	 * No need to use i_size_read() here, the i_size
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 918d351d5b94..af97b9170358 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1330,8 +1330,11 @@ static int ext4_write_end(struct file *file,
 	if (ext4_has_inline_data(inode)) {
 		ret = ext4_write_inline_data_end(inode, pos, len,
 						 copied, page);
-		if (ret < 0)
+		if (ret < 0) {
+			unlock_page(page);
+			put_page(page);
 			goto errout;
+		}
 		copied = ret;
 	} else
 		copied = block_write_end(file, mapping, pos,
@@ -1433,10 +1436,16 @@ static int ext4_journalled_write_end(struct file *file,
 
 	BUG_ON(!ext4_handle_valid(handle));
 
-	if (ext4_has_inline_data(inode))
-		copied = ext4_write_inline_data_end(inode, pos, len,
-						    copied, page);
-	else if (unlikely(copied < len) && !PageUptodate(page)) {
+	if (ext4_has_inline_data(inode)) {
+		ret = ext4_write_inline_data_end(inode, pos, len,
+						 copied, page);
+		if (ret < 0) {
+			unlock_page(page);
+			put_page(page);
+			goto errout;
+		}
+		copied = ret;
+	} else if (unlikely(copied < len) && !PageUptodate(page)) {
 		copied = 0;
 		ext4_journalled_zero_new_buffers(handle, page, from, to);
 	} else {
@@ -1471,6 +1480,7 @@ static int ext4_journalled_write_end(struct file *file,
 		 */
 		ext4_orphan_add(handle, inode);
 
+errout:
 	ret2 = ext4_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/7] jbd2: don't leak modified metadata buffers on an aborted journal
  2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
@ 2017-02-05  7:34 ` Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 3/7] ext4: preserve the needs_recovery flag when the journal is aborted Theodore Ts'o
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o, stable

If the journal has been aborted, we shouldn't mark the underlying
buffer head as dirty, since that will cause the metadata block to get
modified.  And if the journal has been aborted, we shouldn't allow
this since it will almost certainly lead to a corrupted file system.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
---
 fs/jbd2/transaction.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index e1652665bd93..5e659ee08d6a 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1863,7 +1863,9 @@ static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh)
 
 	__blist_del_buffer(list, jh);
 	jh->b_jlist = BJ_None;
-	if (test_clear_buffer_jbddirty(bh))
+	if (transaction && is_journal_aborted(transaction->t_journal))
+		clear_buffer_jbddirty(bh);
+	else if (test_clear_buffer_jbddirty(bh))
 		mark_buffer_dirty(bh);	/* Expose it to the VM */
 }
 
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/7] ext4: preserve the needs_recovery flag when the journal is aborted
  2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 2/7] jbd2: don't leak modified metadata buffers on an aborted journal Theodore Ts'o
@ 2017-02-05  7:34 ` Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 4/7] ext4: return EROFS if device is r/o and journal replay is needed Theodore Ts'o
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o, stable

If the journal is aborted, the needs_recovery feature flag should not
be removed.  Otherwise, it's the journal might not get replayed and
this could lead to more data getting lost.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
---
 fs/ext4/super.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 829e4a7b59e4..3fef82e79131 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -825,6 +825,7 @@ static void ext4_put_super(struct super_block *sb)
 {
 	struct ext4_sb_info *sbi = EXT4_SB(sb);
 	struct ext4_super_block *es = sbi->s_es;
+	int aborted = 0;
 	int i, err;
 
 	ext4_unregister_li_request(sb);
@@ -834,9 +835,10 @@ static void ext4_put_super(struct super_block *sb)
 	destroy_workqueue(sbi->rsv_conversion_wq);
 
 	if (sbi->s_journal) {
+		aborted = is_journal_aborted(sbi->s_journal);
 		err = jbd2_journal_destroy(sbi->s_journal);
 		sbi->s_journal = NULL;
-		if (err < 0)
+		if ((err < 0) && !aborted)
 			ext4_abort(sb, "Couldn't clean up the journal");
 	}
 
@@ -847,7 +849,7 @@ static void ext4_put_super(struct super_block *sb)
 	ext4_mb_release(sb);
 	ext4_ext_release(sb);
 
-	if (!(sb->s_flags & MS_RDONLY)) {
+	if (!(sb->s_flags & MS_RDONLY) && !aborted) {
 		ext4_clear_feature_journal_needs_recovery(sb);
 		es->s_state = cpu_to_le16(sbi->s_mount_state);
 	}
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/7] ext4: return EROFS if device is r/o and journal replay is needed
  2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 2/7] jbd2: don't leak modified metadata buffers on an aborted journal Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 3/7] ext4: preserve the needs_recovery flag when the journal is aborted Theodore Ts'o
@ 2017-02-05  7:34 ` Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 5/7] ext4: rename s_resize_flags to s_ext4_flags Theodore Ts'o
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o, stable

If the file system requires journal recovery, and the device is
read-ony, return EROFS to the mount system call.  This allows xfstests
generic/050 to pass.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@vger.kernel.org
---
 fs/ext4/super.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 3fef82e79131..514e5fc59893 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3925,7 +3925,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	 * root first: it may be modified in the journal!
 	 */
 	if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
-		if (ext4_load_journal(sb, es, journal_devnum))
+		err = ext4_load_journal(sb, es, journal_devnum);
+		if (err)
 			goto failed_mount3a;
 	} else if (test_opt(sb, NOLOAD) && !(sb->s_flags & MS_RDONLY) &&
 		   ext4_has_feature_journal_needs_recovery(sb)) {
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 5/7] ext4: rename s_resize_flags to s_ext4_flags
  2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
                   ` (2 preceding siblings ...)
  2017-02-05  7:34 ` [PATCH 4/7] ext4: return EROFS if device is r/o and journal replay is needed Theodore Ts'o
@ 2017-02-05  7:34 ` Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 6/7] ext4: add shutdown bit and check for it Theodore Ts'o
  2017-02-05  7:34 ` [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl Theodore Ts'o
  5 siblings, 0 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

We are currently using one bit in s_resize_flags; rename it in order
to allow more of the bits in that unsigned long for other purposes.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h   | 10 +++++++---
 fs/ext4/resize.c |  5 +++--
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 1cd077e02517..2e7e02f2f771 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1399,8 +1399,7 @@ struct ext4_sb_info {
 	struct journal_s *s_journal;
 	struct list_head s_orphan;
 	struct mutex s_orphan_lock;
-	unsigned long s_resize_flags;		/* Flags indicating if there
-						   is a resizer */
+	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
 	unsigned long s_commit_interval;
 	u32 s_max_batch_time;
 	u32 s_min_batch_time;
@@ -1834,6 +1833,12 @@ static inline bool ext4_has_incompat_features(struct super_block *sb)
 }
 
 /*
+ * Superblock flags
+ */
+#define EXT4_FLAGS_RESIZING	0
+
+
+/*
  * Default values for user and/or group using reserved blocks
  */
 #define	EXT4_DEF_RESUID		0
@@ -3217,7 +3222,6 @@ static inline void ext4_inode_resume_unlocked_dio(struct inode *inode)
 					    EXT4_WQ_HASH_SZ])
 extern wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ];
 
-#define EXT4_RESIZING	0
 extern int ext4_resize_begin(struct super_block *sb);
 extern void ext4_resize_end(struct super_block *sb);
 
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index cf681004b196..c3ed9021b781 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -45,7 +45,8 @@ int ext4_resize_begin(struct super_block *sb)
 		return -EPERM;
 	}
 
-	if (test_and_set_bit_lock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags))
+	if (test_and_set_bit_lock(EXT4_FLAGS_RESIZING,
+				  &EXT4_SB(sb)->s_ext4_flags))
 		ret = -EBUSY;
 
 	return ret;
@@ -53,7 +54,7 @@ int ext4_resize_begin(struct super_block *sb)
 
 void ext4_resize_end(struct super_block *sb)
 {
-	clear_bit_unlock(EXT4_RESIZING, &EXT4_SB(sb)->s_resize_flags);
+	clear_bit_unlock(EXT4_FLAGS_RESIZING, &EXT4_SB(sb)->s_ext4_flags);
 	smp_mb__after_atomic();
 }
 
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 6/7] ext4: add shutdown bit and check for it
  2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
                   ` (3 preceding siblings ...)
  2017-02-05  7:34 ` [PATCH 5/7] ext4: rename s_resize_flags to s_ext4_flags Theodore Ts'o
@ 2017-02-05  7:34 ` Theodore Ts'o
  2017-02-06 19:51   ` Andreas Dilger
  2017-02-05  7:34 ` [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl Theodore Ts'o
  5 siblings, 1 reply; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

Add a shutdown bit that will cause ext4 processing to fail immediately
with EIO.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h      |  6 ++++++
 fs/ext4/ext4_jbd2.c | 11 +++++++++++
 fs/ext4/file.c      | 12 ++++++++++++
 fs/ext4/fsync.c     |  3 +++
 fs/ext4/ialloc.c    |  3 +++
 fs/ext4/inline.c    |  3 +++
 fs/ext4/inode.c     | 30 ++++++++++++++++++++++++++++--
 fs/ext4/namei.c     | 12 ++++++++++++
 fs/ext4/page-io.c   |  2 +-
 fs/ext4/super.c     | 18 ++++++++++++++++++
 fs/ext4/xattr.c     |  3 +++
 11 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 2e7e02f2f771..35d93ab7f3fb 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1836,6 +1836,12 @@ static inline bool ext4_has_incompat_features(struct super_block *sb)
  * Superblock flags
  */
 #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);
+}
 
 
 /*
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index e770c1ee4613..dd106b1d5d89 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -43,6 +43,10 @@ static int ext4_journal_check_start(struct super_block *sb)
 	journal_t *journal;
 
 	might_sleep();
+
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
+		return -EIO;
+
 	if (sb->s_flags & MS_RDONLY)
 		return -EROFS;
 	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,
 	might_sleep();
 
 	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);
 		if (err)
 			ext4_journal_abort_handle(where, line, __func__, bh,
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index d663d3d7c81c..ff3f6107b0ba 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -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)
 {
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(file_inode(iocb->ki_filp)->i_sb))))
+		return -EIO;
+
 	if (!iov_iter_count(to))
 		return 0; /* skip atime */
 
@@ -213,6 +216,9 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	int overwrite = 0;
 	ssize_t ret;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 #ifdef CONFIG_FS_DAX
 	if (IS_DAX(inode))
 		return ext4_dax_write_iter(iocb, from);
@@ -348,6 +354,9 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
 {
 	struct inode *inode = file->f_mapping->host;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	if (ext4_encrypted_inode(inode)) {
 		int err = fscrypt_get_encryption_info(inode);
 		if (err)
@@ -375,6 +384,9 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
 	char buf[64], *cp;
 	int ret;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
 		     !(sb->s_flags & MS_RDONLY))) {
 		sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED;
diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
index 88effb1053c7..9d549608fd30 100644
--- a/fs/ext4/fsync.c
+++ b/fs/ext4/fsync.c
@@ -100,6 +100,9 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
 	tid_t commit_tid;
 	bool needs_barrier = false;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	J_ASSERT(ext4_journal_current_handle() == NULL);
 
 	trace_ext4_sync_file_enter(file, datasync);
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index f372fc431b8e..b14bae2598bc 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -764,6 +764,9 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
 	if (!dir || !dir->i_nlink)
 		return ERR_PTR(-EPERM);
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
+		return ERR_PTR(-EIO);
+
 	if ((ext4_encrypted_inode(dir) ||
 	     DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
 	    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index b777b8aa14ae..30a9f210d1e3 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -215,6 +215,9 @@ static void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
 	struct ext4_inode *raw_inode;
 	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(pos + len > EXT4_I(inode)->i_inline_size);
 
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index af97b9170358..e2b7dccc6a11 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1189,6 +1189,9 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
 	pgoff_t index;
 	unsigned from, to;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	trace_ext4_write_begin(inode, pos, len, flags);
 	/*
 	 * Reserve one block more for addition to orphan list in case
@@ -2047,6 +2050,12 @@ static int ext4_writepage(struct page *page,
 	struct ext4_io_submit io_submit;
 	bool keep_towrite = false;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
+		do_invalidatepage(page, 0, PAGE_SIZE);
+		unlock_page(page);
+		return -EIO;
+	}
+
 	trace_ext4_writepage(page);
 	size = i_size_read(inode);
 	if (page->index == size >> PAGE_SHIFT)
@@ -2422,7 +2431,8 @@ static int mpage_map_and_submit_extent(handle_t *handle,
 		if (err < 0) {
 			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;
 			/*
 			 * Let the uper layers retry transient errors.
@@ -2644,6 +2654,9 @@ static int ext4_writepages(struct address_space *mapping,
 	struct blk_plug plug;
 	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);
 	trace_ext4_writepages(inode, wbc);
 
@@ -2680,7 +2693,8 @@ static int ext4_writepages(struct address_space *mapping,
 	 * *never* be called, so if that ever happens, we would want
 	 * 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;
 		goto out_writepages;
 	}
@@ -2905,6 +2919,9 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
 	struct inode *inode = mapping->host;
 	handle_t *handle;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	index = pos >> PAGE_SHIFT;
 
 	if (ext4_nonda_switch(inode->i_sb) ||
@@ -5212,6 +5229,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
 	int orphan = 0;
 	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);
 	if (error)
 		return error;
@@ -5498,6 +5518,9 @@ int ext4_mark_iloc_dirty(handle_t *handle,
 {
 	int err = 0;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	if (IS_I_VERSION(inode))
 		inode_inc_iversion(inode);
 
@@ -5521,6 +5544,9 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
 {
 	int err;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	err = ext4_get_inode_loc(inode, iloc);
 	if (!err) {
 		BUFFER_TRACE(iloc->bh, "get_write_access");
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 931da9d5d915..6ad612c576fc 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -2939,6 +2939,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
 	struct ext4_dir_entry_2 *de;
 	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
 	 * separate transaction */
 	retval = dquot_initialize(dir);
@@ -3012,6 +3015,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
 	struct ext4_dir_entry_2 *de;
 	handle_t *handle = NULL;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
+		return -EIO;
+
 	trace_ext4_unlink_enter(dir, dentry);
 	/* Initialize quotas before so that eventual writes go
 	 * in separate transaction */
@@ -3082,6 +3088,9 @@ static int ext4_symlink(struct inode *dir,
 	struct fscrypt_str disk_link;
 	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.name = (char *) symname;
 
@@ -3874,6 +3883,9 @@ static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry,
 			struct inode *new_dir, struct dentry *new_dentry,
 			unsigned int flags)
 {
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(old_dir->i_sb))))
+		return -EIO;
+
 	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
 		return -EINVAL;
 
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index d83b0f3c5fe9..f8808835a28b 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -158,7 +158,7 @@ static int ext4_end_io(ext4_io_end_t *io)
 
 	io->handle = NULL;	/* Following call will use up the handle */
 	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,
 			 "failed to convert unwritten extents to written "
 			 "extents -- potential data loss!  "
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 514e5fc59893..879e3a6afd9d 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -438,6 +438,9 @@ void __ext4_error(struct super_block *sb, const char *function,
 	struct va_format vaf;
 	va_list args;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
+		return;
+
 	if (ext4_error_ratelimit(sb)) {
 		va_start(args, fmt);
 		vaf.fmt = fmt;
@@ -459,6 +462,9 @@ void __ext4_error_inode(struct inode *inode, const char *function,
 	struct va_format vaf;
 	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_block = cpu_to_le64(block);
 	if (ext4_error_ratelimit(inode->i_sb)) {
@@ -491,6 +497,9 @@ void __ext4_error_file(struct file *file, const char *function,
 	struct inode *inode = file_inode(file);
 	char pathname[80], *path;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return;
+
 	es = EXT4_SB(inode->i_sb)->s_es;
 	es->s_last_error_ino = cpu_to_le32(inode->i_ino);
 	if (ext4_error_ratelimit(inode->i_sb)) {
@@ -567,6 +576,9 @@ void __ext4_std_error(struct super_block *sb, const char *function,
 	char nbuf[16];
 	const char *errstr;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
+		return;
+
 	/* 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. */
@@ -600,6 +612,9 @@ void __ext4_abort(struct super_block *sb, const char *function,
 	struct va_format vaf;
 	va_list args;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
+		return;
+
 	save_error_info(sb, function, line);
 	va_start(args, fmt);
 	vaf.fmt = fmt;
@@ -695,6 +710,9 @@ __acquires(bitlock)
 	va_list args;
 	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_block = cpu_to_le64(block);
 	__save_error_info(sb, function, line);
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index c40bd55b6400..67636acf7624 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -411,6 +411,9 @@ ext4_xattr_get(struct inode *inode, int name_index, const char *name,
 {
 	int error;
 
+	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
+		return -EIO;
+
 	if (strlen(name) > 255)
 		return -ERANGE;
 
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl
  2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
                   ` (4 preceding siblings ...)
  2017-02-05  7:34 ` [PATCH 6/7] ext4: add shutdown bit and check for it Theodore Ts'o
@ 2017-02-05  7:34 ` Theodore Ts'o
  2017-02-05 13:03   ` kbuild test robot
  5 siblings, 1 reply; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-05  7:34 UTC (permalink / raw)
  To: Ext4 Developers List; +Cc: Theodore Ts'o

This ioctl is modeled after the xfs's XFS_IOC_GOINGDOWN ioctl.  (In
fact, it uses the same code points.)

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 fs/ext4/ext4.h  | 10 ++++++++++
 fs/ext4/ioctl.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/ext4/super.c |  2 +-
 3 files changed, 61 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 35d93ab7f3fb..55b7a77a0444 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -679,6 +679,16 @@ struct fsxattr {
 #define EXT4_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
 #define EXT4_IOC_FSSETXATTR		FS_IOC_FSSETXATTR
 
+#define EXT4_IOC_GOINGDOWN _IOR ('X', 125, __u32)
+
+/*
+ * Flags for going down operation
+ */
+#define EXT4_GOING_FLAGS_DEFAULT		0x0	/* going down */
+#define EXT4_GOING_FLAGS_LOGFLUSH		0x1	/* flush log but not data */
+#define EXT4_GOING_FLAGS_NOLOGFLUSH		0x2	/* don't flush log nor data */
+
+
 #if defined(__KERNEL__) && defined(CONFIG_COMPAT)
 /*
  * ioctl commands in 32 bit emulation
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index d534399cf607..b383ebf4020c 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -16,6 +16,7 @@
 #include <linux/quotaops.h>
 #include <linux/uuid.h>
 #include <linux/uaccess.h>
+#include <linux/delay.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
 
@@ -442,6 +443,52 @@ static inline unsigned long ext4_xflags_to_iflags(__u32 xflags)
 	return iflags;
 }
 
+int ext4_goingdown(struct super_block *sb, unsigned long arg)
+{
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	__u32 flags;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (get_user(flags, (__u32 __user *)arg))
+		return -EFAULT;
+
+	if (flags > EXT4_GOING_FLAGS_NOLOGFLUSH)
+		return -EINVAL;
+
+	if (ext4_forced_shutdown(sbi))
+		return 0;
+
+	ext4_msg(sb, KERN_ALERT, "shut down requested (%d)", flags);
+
+	switch (flags) {
+	case EXT4_GOING_FLAGS_DEFAULT:
+		freeze_bdev(sb->s_bdev);
+		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
+		thaw_bdev(sb->s_bdev, sb);
+		break;
+	case EXT4_GOING_FLAGS_LOGFLUSH:
+		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
+		if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) {
+			(void) ext4_force_commit(sb);
+			jbd2_journal_abort(sbi->s_journal, 0);
+		}
+		break;
+	case EXT4_GOING_FLAGS_NOLOGFLUSH:
+		set_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags);
+		if (sbi->s_journal && !is_journal_aborted(sbi->s_journal)) {
+			msleep(100);
+			jbd2_journal_abort(sbi->s_journal, 0);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	clear_opt(sb, DISCARD);
+	return 0;
+}
+
 long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
 	struct inode *inode = file_inode(filp);
@@ -893,6 +940,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
 		return 0;
 	}
+	case EXT4_IOC_GOINGDOWN:
+		return ext4_goingdown(sb, arg);
 	default:
 		return -ENOTTY;
 	}
@@ -959,6 +1008,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 	case EXT4_IOC_SET_ENCRYPTION_POLICY:
 	case EXT4_IOC_GET_ENCRYPTION_PWSALT:
 	case EXT4_IOC_GET_ENCRYPTION_POLICY:
+	case EXT4_IOC_GOINGDOWN:
 		break;
 	default:
 		return -ENOIOCTLCMD;
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 879e3a6afd9d..de8709269cfb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4818,7 +4818,7 @@ static int ext4_freeze(struct super_block *sb)
  */
 static int ext4_unfreeze(struct super_block *sb)
 {
-	if (sb->s_flags & MS_RDONLY)
+	if ((sb->s_flags & MS_RDONLY) || ext4_forced_shutdown(EXT4_SB(sb)))
 		return 0;
 
 	if (EXT4_SB(sb)->s_journal) {
-- 
2.11.0.rc0.7.gbe5a750

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl
  2017-02-05  7:34 ` [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl Theodore Ts'o
@ 2017-02-05 13:03   ` kbuild test robot
  2017-02-06  0:50     ` Theodore Ts'o
  0 siblings, 1 reply; 11+ messages in thread
From: kbuild test robot @ 2017-02-05 13:03 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: kbuild-all, Ext4 Developers List, Theodore Ts'o

[-- Attachment #1: Type: text/plain, Size: 853 bytes --]

Hi Theodore,

[auto build test ERROR on ext4/dev]
[also build test ERROR on next-20170203]
[cannot apply to v4.10-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Theodore-Ts-o/ext4-fix-inline-data-error-paths/20170205-153921
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
config: i386-allmodconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

>> ERROR: "do_invalidatepage" [fs/ext4/ext4.ko] undefined!

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 57928 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl
  2017-02-05 13:03   ` kbuild test robot
@ 2017-02-06  0:50     ` Theodore Ts'o
  0 siblings, 0 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-06  0:50 UTC (permalink / raw)
  To: kbuild test robot; +Cc: kbuild-all, Ext4 Developers List

On Sun, Feb 05, 2017 at 09:03:39PM +0800, kbuild test robot wrote:
> Hi Theodore,
> 
> [auto build test ERROR on ext4/dev]
> [also build test ERROR on next-20170203]
> [cannot apply to v4.10-rc6]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Theodore-Ts-o/ext4-fix-inline-data-error-paths/20170205-153921
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
> config: i386-allmodconfig (attached as .config)
> compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=i386 
> 
> All errors (new ones prefixed by >>):
> 
> >> ERROR: "do_invalidatepage" [fs/ext4/ext4.ko] undefined!

Fixed in the version I just pushed to the ext4 tree.

      	     	       	    	      	  - Ted

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 6/7] ext4: add shutdown bit and check for it
  2017-02-05  7:34 ` [PATCH 6/7] ext4: add shutdown bit and check for it Theodore Ts'o
@ 2017-02-06 19:51   ` Andreas Dilger
  2017-02-07 19:52     ` Theodore Ts'o
  0 siblings, 1 reply; 11+ messages in thread
From: Andreas Dilger @ 2017-02-06 19:51 UTC (permalink / raw)
  To: Theodore Ts'o; +Cc: Ext4 Developers List

[-- Attachment #1: Type: text/plain, Size: 13705 bytes --]

On Feb 5, 2017, at 12:34 AM, Theodore Ts'o <tytso@mit.edu> wrote:
> 
> Add a shutdown bit that will cause ext4 processing to fail immediately
> with EIO.

Does it make sense to combine the checks for "shutdown", "abort", and
"read-only" into a single check, instead of sprinkling separate checks
throughout the code?  It seems like many of the places where
ext4_forced_shutdown() is checked should also be checking for read-only
and abort, even if they aren't today.

Cheers, Andreas

> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
> ---
> fs/ext4/ext4.h      |  6 ++++++
> fs/ext4/ext4_jbd2.c | 11 +++++++++++
> fs/ext4/file.c      | 12 ++++++++++++
> fs/ext4/fsync.c     |  3 +++
> fs/ext4/ialloc.c    |  3 +++
> fs/ext4/inline.c    |  3 +++
> fs/ext4/inode.c     | 30 ++++++++++++++++++++++++++++--
> fs/ext4/namei.c     | 12 ++++++++++++
> fs/ext4/page-io.c   |  2 +-
> fs/ext4/super.c     | 18 ++++++++++++++++++
> fs/ext4/xattr.c     |  3 +++
> 11 files changed, 100 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 2e7e02f2f771..35d93ab7f3fb 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1836,6 +1836,12 @@ static inline bool ext4_has_incompat_features(struct super_block *sb)
>  * Superblock flags
>  */
> #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);
> +}
> 
> 
> /*
> diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
> index e770c1ee4613..dd106b1d5d89 100644
> --- a/fs/ext4/ext4_jbd2.c
> +++ b/fs/ext4/ext4_jbd2.c
> @@ -43,6 +43,10 @@ static int ext4_journal_check_start(struct super_block *sb)
> 	journal_t *journal;
> 
> 	might_sleep();
> +
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
> +		return -EIO;
> +
> 	if (sb->s_flags & MS_RDONLY)
> 		return -EROFS;
> 	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,
> 	might_sleep();
> 
> 	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);
> 		if (err)
> 			ext4_journal_abort_handle(where, line, __func__, bh,
> diff --git a/fs/ext4/file.c b/fs/ext4/file.c
> index d663d3d7c81c..ff3f6107b0ba 100644
> --- a/fs/ext4/file.c
> +++ b/fs/ext4/file.c
> @@ -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)
> {
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(file_inode(iocb->ki_filp)->i_sb))))
> +		return -EIO;
> +
> 	if (!iov_iter_count(to))
> 		return 0; /* skip atime */
> 
> @@ -213,6 +216,9 @@ ext4_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
> 	int overwrite = 0;
> 	ssize_t ret;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> #ifdef CONFIG_FS_DAX
> 	if (IS_DAX(inode))
> 		return ext4_dax_write_iter(iocb, from);
> @@ -348,6 +354,9 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma)
> {
> 	struct inode *inode = file->f_mapping->host;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	if (ext4_encrypted_inode(inode)) {
> 		int err = fscrypt_get_encryption_info(inode);
> 		if (err)
> @@ -375,6 +384,9 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
> 	char buf[64], *cp;
> 	int ret;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	if (unlikely(!(sbi->s_mount_flags & EXT4_MF_MNTDIR_SAMPLED) &&
> 		     !(sb->s_flags & MS_RDONLY))) {
> 		sbi->s_mount_flags |= EXT4_MF_MNTDIR_SAMPLED;
> diff --git a/fs/ext4/fsync.c b/fs/ext4/fsync.c
> index 88effb1053c7..9d549608fd30 100644
> --- a/fs/ext4/fsync.c
> +++ b/fs/ext4/fsync.c
> @@ -100,6 +100,9 @@ int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
> 	tid_t commit_tid;
> 	bool needs_barrier = false;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	J_ASSERT(ext4_journal_current_handle() == NULL);
> 
> 	trace_ext4_sync_file_enter(file, datasync);
> diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
> index f372fc431b8e..b14bae2598bc 100644
> --- a/fs/ext4/ialloc.c
> +++ b/fs/ext4/ialloc.c
> @@ -764,6 +764,9 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
> 	if (!dir || !dir->i_nlink)
> 		return ERR_PTR(-EPERM);
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
> +		return ERR_PTR(-EIO);
> +
> 	if ((ext4_encrypted_inode(dir) ||
> 	     DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) &&
> 	    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) {
> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
> index b777b8aa14ae..30a9f210d1e3 100644
> --- a/fs/ext4/inline.c
> +++ b/fs/ext4/inline.c
> @@ -215,6 +215,9 @@ static void ext4_write_inline_data(struct inode *inode, struct ext4_iloc *iloc,
> 	struct ext4_inode *raw_inode;
> 	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(pos + len > EXT4_I(inode)->i_inline_size);
> 
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index af97b9170358..e2b7dccc6a11 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -1189,6 +1189,9 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
> 	pgoff_t index;
> 	unsigned from, to;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	trace_ext4_write_begin(inode, pos, len, flags);
> 	/*
> 	 * Reserve one block more for addition to orphan list in case
> @@ -2047,6 +2050,12 @@ static int ext4_writepage(struct page *page,
> 	struct ext4_io_submit io_submit;
> 	bool keep_towrite = false;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) {
> +		do_invalidatepage(page, 0, PAGE_SIZE);
> +		unlock_page(page);
> +		return -EIO;
> +	}
> +
> 	trace_ext4_writepage(page);
> 	size = i_size_read(inode);
> 	if (page->index == size >> PAGE_SHIFT)
> @@ -2422,7 +2431,8 @@ static int mpage_map_and_submit_extent(handle_t *handle,
> 		if (err < 0) {
> 			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;
> 			/*
> 			 * Let the uper layers retry transient errors.
> @@ -2644,6 +2654,9 @@ static int ext4_writepages(struct address_space *mapping,
> 	struct blk_plug plug;
> 	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);
> 	trace_ext4_writepages(inode, wbc);
> 
> @@ -2680,7 +2693,8 @@ static int ext4_writepages(struct address_space *mapping,
> 	 * *never* be called, so if that ever happens, we would want
> 	 * 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;
> 		goto out_writepages;
> 	}
> @@ -2905,6 +2919,9 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
> 	struct inode *inode = mapping->host;
> 	handle_t *handle;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	index = pos >> PAGE_SHIFT;
> 
> 	if (ext4_nonda_switch(inode->i_sb) ||
> @@ -5212,6 +5229,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
> 	int orphan = 0;
> 	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);
> 	if (error)
> 		return error;
> @@ -5498,6 +5518,9 @@ int ext4_mark_iloc_dirty(handle_t *handle,
> {
> 	int err = 0;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	if (IS_I_VERSION(inode))
> 		inode_inc_iversion(inode);
> 
> @@ -5521,6 +5544,9 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
> {
> 	int err;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	err = ext4_get_inode_loc(inode, iloc);
> 	if (!err) {
> 		BUFFER_TRACE(iloc->bh, "get_write_access");
> diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
> index 931da9d5d915..6ad612c576fc 100644
> --- a/fs/ext4/namei.c
> +++ b/fs/ext4/namei.c
> @@ -2939,6 +2939,9 @@ static int ext4_rmdir(struct inode *dir, struct dentry *dentry)
> 	struct ext4_dir_entry_2 *de;
> 	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
> 	 * separate transaction */
> 	retval = dquot_initialize(dir);
> @@ -3012,6 +3015,9 @@ static int ext4_unlink(struct inode *dir, struct dentry *dentry)
> 	struct ext4_dir_entry_2 *de;
> 	handle_t *handle = NULL;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb))))
> +		return -EIO;
> +
> 	trace_ext4_unlink_enter(dir, dentry);
> 	/* Initialize quotas before so that eventual writes go
> 	 * in separate transaction */
> @@ -3082,6 +3088,9 @@ static int ext4_symlink(struct inode *dir,
> 	struct fscrypt_str disk_link;
> 	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.name = (char *) symname;
> 
> @@ -3874,6 +3883,9 @@ static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry,
> 			struct inode *new_dir, struct dentry *new_dentry,
> 			unsigned int flags)
> {
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(old_dir->i_sb))))
> +		return -EIO;
> +
> 	if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
> 		return -EINVAL;
> 
> diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
> index d83b0f3c5fe9..f8808835a28b 100644
> --- a/fs/ext4/page-io.c
> +++ b/fs/ext4/page-io.c
> @@ -158,7 +158,7 @@ static int ext4_end_io(ext4_io_end_t *io)
> 
> 	io->handle = NULL;	/* Following call will use up the handle */
> 	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,
> 			 "failed to convert unwritten extents to written "
> 			 "extents -- potential data loss!  "
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 514e5fc59893..879e3a6afd9d 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -438,6 +438,9 @@ void __ext4_error(struct super_block *sb, const char *function,
> 	struct va_format vaf;
> 	va_list args;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
> +		return;
> +
> 	if (ext4_error_ratelimit(sb)) {
> 		va_start(args, fmt);
> 		vaf.fmt = fmt;
> @@ -459,6 +462,9 @@ void __ext4_error_inode(struct inode *inode, const char *function,
> 	struct va_format vaf;
> 	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_block = cpu_to_le64(block);
> 	if (ext4_error_ratelimit(inode->i_sb)) {
> @@ -491,6 +497,9 @@ void __ext4_error_file(struct file *file, const char *function,
> 	struct inode *inode = file_inode(file);
> 	char pathname[80], *path;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return;
> +
> 	es = EXT4_SB(inode->i_sb)->s_es;
> 	es->s_last_error_ino = cpu_to_le32(inode->i_ino);
> 	if (ext4_error_ratelimit(inode->i_sb)) {
> @@ -567,6 +576,9 @@ void __ext4_std_error(struct super_block *sb, const char *function,
> 	char nbuf[16];
> 	const char *errstr;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
> +		return;
> +
> 	/* 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. */
> @@ -600,6 +612,9 @@ void __ext4_abort(struct super_block *sb, const char *function,
> 	struct va_format vaf;
> 	va_list args;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(sb))))
> +		return;
> +
> 	save_error_info(sb, function, line);
> 	va_start(args, fmt);
> 	vaf.fmt = fmt;
> @@ -695,6 +710,9 @@ __acquires(bitlock)
> 	va_list args;
> 	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_block = cpu_to_le64(block);
> 	__save_error_info(sb, function, line);
> diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
> index c40bd55b6400..67636acf7624 100644
> --- a/fs/ext4/xattr.c
> +++ b/fs/ext4/xattr.c
> @@ -411,6 +411,9 @@ ext4_xattr_get(struct inode *inode, int name_index, const char *name,
> {
> 	int error;
> 
> +	if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb))))
> +		return -EIO;
> +
> 	if (strlen(name) > 255)
> 		return -ERANGE;
> 
> --
> 2.11.0.rc0.7.gbe5a750
> 


Cheers, Andreas






[-- Attachment #2: Message signed with OpenPGP --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 6/7] ext4: add shutdown bit and check for it
  2017-02-06 19:51   ` Andreas Dilger
@ 2017-02-07 19:52     ` Theodore Ts'o
  0 siblings, 0 replies; 11+ messages in thread
From: Theodore Ts'o @ 2017-02-07 19:52 UTC (permalink / raw)
  To: Andreas Dilger; +Cc: Ext4 Developers List

On Mon, Feb 06, 2017 at 12:51:46PM -0700, Andreas Dilger wrote:
> On Feb 5, 2017, at 12:34 AM, Theodore Ts'o <tytso@mit.edu> wrote:
> > 
> > Add a shutdown bit that will cause ext4 processing to fail immediately
> > with EIO.
> 
> Does it make sense to combine the checks for "shutdown", "abort", and
> "read-only" into a single check, instead of sprinkling separate checks
> throughout the code?  It seems like many of the places where
> ext4_forced_shutdown() is checked should also be checking for read-only
> and abort, even if they aren't today.

I've thought about that.  At the moment forced_shutdown will actually
return errors for some read-only operations as well --- for example,
we immediately return an error in ext4_file_open() and
ext4_file_mmap().  And I had considered adding it to some additional
read operations (e.g., readpage, readpages, etc.) on the theory that
if we know the block device should be treated as dead, maybe we should
add shortcut error returns for them as well.  (Especially since the
SCSI layer and error handling have historically not necessarily gone
together well like chocolate and peanut butter.  :-)

So I decided to keep the shutdown and implementation of
errors=remount-ro separate for now, but it is a good question for us
to consider.

							- Ted

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2017-02-07 19:52 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-05  7:34 [PATCH 1/7] ext4: fix inline data error paths Theodore Ts'o
2017-02-05  7:34 ` [PATCH 2/7] jbd2: don't leak modified metadata buffers on an aborted journal Theodore Ts'o
2017-02-05  7:34 ` [PATCH 3/7] ext4: preserve the needs_recovery flag when the journal is aborted Theodore Ts'o
2017-02-05  7:34 ` [PATCH 4/7] ext4: return EROFS if device is r/o and journal replay is needed Theodore Ts'o
2017-02-05  7:34 ` [PATCH 5/7] ext4: rename s_resize_flags to s_ext4_flags Theodore Ts'o
2017-02-05  7:34 ` [PATCH 6/7] ext4: add shutdown bit and check for it Theodore Ts'o
2017-02-06 19:51   ` Andreas Dilger
2017-02-07 19:52     ` Theodore Ts'o
2017-02-05  7:34 ` [PATCH 7/7] ext4: add EXT4_IOC_GOINGDOWN ioctl Theodore Ts'o
2017-02-05 13:03   ` kbuild test robot
2017-02-06  0:50     ` Theodore Ts'o

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.