Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cab7076a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ext4 updates from Ted Ts'o:
 "For this cycle we add support for the shutdown ioctl, which is
  primarily used for testing, but which can be useful on production
  systems when a scratch volume is being destroyed and the data on it
  doesn't need to be saved.

  This found (and we fixed) a number of bugs with ext4's recovery to
  corrupted file system --- the bugs increased the amount of data that
  could be potentially lost, and in the case of the inline data feature,
  could cause the kernel to BUG.

  Also included are a number of other bug fixes, including in ext4's
  fscrypt, DAX, inline data support"

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (26 commits)
  ext4: rename EXT4_IOC_GOINGDOWN to EXT4_IOC_SHUTDOWN
  ext4: fix fencepost in s_first_meta_bg validation
  ext4: don't BUG when truncating encrypted inodes on the orphan list
  ext4: do not use stripe_width if it is not set
  ext4: fix stripe-unaligned allocations
  dax: assert that i_rwsem is held exclusive for writes
  ext4: fix DAX write locking
  ext4: add EXT4_IOC_GOINGDOWN ioctl
  ext4: add shutdown bit and check for it
  ext4: rename s_resize_flags to s_ext4_flags
  ext4: return EROFS if device is r/o and journal replay is needed
  ext4: preserve the needs_recovery flag when the journal is aborted
  jbd2: don't leak modified metadata buffers on an aborted journal
  ext4: fix inline data error paths
  ext4: move halfmd4 into hash.c directly
  ext4: fix use-after-iput when fscrypt contexts are inconsistent
  jbd2: fix use after free in kjournald2()
  ext4: fix data corruption in data=journal mode
  ext4: trim allocation requests to group size
  ext4: replace BUG_ON with WARN_ON in mb_find_extent()
  ...
parents 6c24337f e9be2ac7
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -1086,8 +1086,12 @@ dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter,
	loff_t pos = iocb->ki_pos, ret = 0, done = 0;
	unsigned flags = 0;

	if (iov_iter_rw(iter) == WRITE)
	if (iov_iter_rw(iter) == WRITE) {
		lockdep_assert_held_exclusive(&inode->i_rwsem);
		flags |= IOMAP_WRITE;
	} else {
		lockdep_assert_held(&inode->i_rwsem);
	}

	while (iov_iter_count(iter)) {
		ret = iomap_apply(inode, pos, iov_iter_count(iter), flags, ops,
+24 −4
Original line number Diff line number Diff line
@@ -683,6 +683,16 @@ struct fsxattr {
#define EXT4_IOC_FSGETXATTR		FS_IOC_FSGETXATTR
#define EXT4_IOC_FSSETXATTR		FS_IOC_FSSETXATTR

#define EXT4_IOC_SHUTDOWN _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
@@ -1403,8 +1413,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;
@@ -1837,6 +1846,18 @@ static inline bool ext4_has_incompat_features(struct super_block *sb)
	return (EXT4_SB(sb)->s_es->s_feature_incompat != 0);
}

/*
 * 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);
}


/*
 * Default values for user and/or group using reserved blocks
 */
@@ -3005,7 +3026,7 @@ extern int ext4_inline_data_fiemap(struct inode *inode,
extern int ext4_try_to_evict_inline_data(handle_t *handle,
					 struct inode *inode,
					 int needed);
extern void ext4_inline_data_truncate(struct inode *inode, int *has_inline);
extern int ext4_inline_data_truncate(struct inode *inode, int *has_inline);

extern int ext4_convert_inline_data(struct inode *inode);

@@ -3199,7 +3220,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);

+11 −0
Original line number Diff line number Diff line
@@ -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,
+18 −9
Original line number Diff line number Diff line
@@ -5334,7 +5334,8 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
	ext4_lblk_t stop, *iterator, ex_start, ex_end;

	/* Let path point to the last extent */
	path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL, 0);
	path = ext4_find_extent(inode, EXT_MAX_BLOCKS - 1, NULL,
				EXT4_EX_NOCACHE);
	if (IS_ERR(path))
		return PTR_ERR(path);

@@ -5343,15 +5344,15 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
	if (!extent)
		goto out;

	stop = le32_to_cpu(extent->ee_block) +
			ext4_ext_get_actual_len(extent);
	stop = le32_to_cpu(extent->ee_block);

       /*
	 * In case of left shift, Don't start shifting extents until we make
	 * sure the hole is big enough to accommodate the shift.
	*/
	if (SHIFT == SHIFT_LEFT) {
		path = ext4_find_extent(inode, start - 1, &path, 0);
		path = ext4_find_extent(inode, start - 1, &path,
					EXT4_EX_NOCACHE);
		if (IS_ERR(path))
			return PTR_ERR(path);
		depth = path->p_depth;
@@ -5383,9 +5384,14 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
	else
		iterator = &stop;

	/* Its safe to start updating extents */
	while (start < stop) {
		path = ext4_find_extent(inode, *iterator, &path, 0);
	/*
	 * Its safe to start updating extents.  Start and stop are unsigned, so
	 * in case of right shift if extent with 0 block is reached, iterator
	 * becomes NULL to indicate the end of the loop.
	 */
	while (iterator && start <= stop) {
		path = ext4_find_extent(inode, *iterator, &path,
					EXT4_EX_NOCACHE);
		if (IS_ERR(path))
			return PTR_ERR(path);
		depth = path->p_depth;
@@ -5412,8 +5418,11 @@ ext4_ext_shift_extents(struct inode *inode, handle_t *handle,
					ext4_ext_get_actual_len(extent);
		} else {
			extent = EXT_FIRST_EXTENT(path[depth].p_hdr);
			*iterator =  le32_to_cpu(extent->ee_block) > 0 ?
				le32_to_cpu(extent->ee_block) - 1 : 0;
			if (le32_to_cpu(extent->ee_block) > 0)
				*iterator = le32_to_cpu(extent->ee_block) - 1;
			else
				/* Beginning is reached, end of the loop */
				iterator = NULL;
			/* Update path extent in case we need to stop */
			while (le32_to_cpu(extent->ee_block) < start)
				extent++;
+13 −9
Original line number Diff line number Diff line
@@ -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 */

@@ -175,7 +178,6 @@ ext4_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
	struct inode *inode = file_inode(iocb->ki_filp);
	ssize_t ret;
	bool overwrite = false;

	inode_lock(inode);
	ret = ext4_write_checks(iocb, from);
@@ -188,16 +190,9 @@ ext4_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
	if (ret)
		goto out;

	if (ext4_overwrite_io(inode, iocb->ki_pos, iov_iter_count(from))) {
		overwrite = true;
		downgrade_write(&inode->i_rwsem);
	}
	ret = dax_iomap_rw(iocb, from, &ext4_iomap_ops);
out:
	if (!overwrite)
	inode_unlock(inode);
	else
		inode_unlock_shared(inode);
	if (ret > 0)
		ret = generic_write_sync(iocb, ret);
	return ret;
@@ -213,6 +208,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 +346,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 +376,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;
Loading