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

Commit 3b421b80 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 bugfixes from Ted Ts'o:
 "Revert a potential seek_data/hole regression which shows up when using
  ext4 to handle ext3 file systems, plus two minor bug fixes"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: remove spurious KERN_INFO from ext4_warning call
  Revert "ext4: fix suboptimal seek_{data,hole} extents traversial"
  ext4: prevent online resize with backup superblock
parents fee7e49d 363307e6
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -5166,7 +5166,7 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,

	/* fallback to generic here if not in extents fmt */
	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
		return __generic_block_fiemap(inode, fieinfo, start, len,
		return generic_block_fiemap(inode, fieinfo, start, len,
			ext4_get_block);

	if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS))
+114 −106
Original line number Diff line number Diff line
@@ -273,19 +273,24 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
 * we determine this extent as a data or a hole according to whether the
 * page cache has data or not.
 */
static int ext4_find_unwritten_pgoff(struct inode *inode, int whence,
				     loff_t endoff, loff_t *offset)
static int ext4_find_unwritten_pgoff(struct inode *inode,
				     int whence,
				     struct ext4_map_blocks *map,
				     loff_t *offset)
{
	struct pagevec pvec;
	unsigned int blkbits;
	pgoff_t index;
	pgoff_t end;
	loff_t endoff;
	loff_t startoff;
	loff_t lastoff;
	int found = 0;

	blkbits = inode->i_sb->s_blocksize_bits;
	startoff = *offset;
	lastoff = startoff;

	endoff = (loff_t)(map->m_lblk + map->m_len) << blkbits;

	index = startoff >> PAGE_CACHE_SHIFT;
	end = endoff >> PAGE_CACHE_SHIFT;
@@ -403,144 +408,147 @@ static int ext4_find_unwritten_pgoff(struct inode *inode, int whence,
static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize)
{
	struct inode *inode = file->f_mapping->host;
	struct fiemap_extent_info fie;
	struct fiemap_extent ext[2];
	loff_t next;
	int i, ret = 0;
	struct ext4_map_blocks map;
	struct extent_status es;
	ext4_lblk_t start, last, end;
	loff_t dataoff, isize;
	int blkbits;
	int ret = 0;

	mutex_lock(&inode->i_mutex);
	if (offset >= inode->i_size) {

	isize = i_size_read(inode);
	if (offset >= isize) {
		mutex_unlock(&inode->i_mutex);
		return -ENXIO;
	}
	fie.fi_flags = 0;
	fie.fi_extents_max = 2;
	fie.fi_extents_start = (struct fiemap_extent __user *) &ext;
	while (1) {
		mm_segment_t old_fs = get_fs();

		fie.fi_extents_mapped = 0;
		memset(ext, 0, sizeof(*ext) * fie.fi_extents_max);
	blkbits = inode->i_sb->s_blocksize_bits;
	start = offset >> blkbits;
	last = start;
	end = isize >> blkbits;
	dataoff = offset;

		set_fs(get_ds());
		ret = ext4_fiemap(inode, &fie, offset, maxsize - offset);
		set_fs(old_fs);
		if (ret)
			break;

		/* No extents found, EOF */
		if (!fie.fi_extents_mapped) {
			ret = -ENXIO;
	do {
		map.m_lblk = last;
		map.m_len = end - last + 1;
		ret = ext4_map_blocks(NULL, inode, &map, 0);
		if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) {
			if (last != start)
				dataoff = (loff_t)last << blkbits;
			break;
		}
		for (i = 0; i < fie.fi_extents_mapped; i++) {
			next = (loff_t)(ext[i].fe_length + ext[i].fe_logical);

			if (offset < (loff_t)ext[i].fe_logical)
				offset = (loff_t)ext[i].fe_logical;
		/*
			 * If extent is not unwritten, then it contains valid
			 * data, mapped or delayed.
		 * If there is a delay extent at this offset,
		 * it will be as a data.
		 */
			if (!(ext[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN))
				goto out;
		ext4_es_find_delayed_extent_range(inode, last, last, &es);
		if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) {
			if (last != start)
				dataoff = (loff_t)last << blkbits;
			break;
		}

		/*
		 * If there is a unwritten extent at this offset,
		 * it will be as a data or a hole according to page
		 * cache that has data or not.
		 */
			if (ext4_find_unwritten_pgoff(inode, SEEK_DATA,
						      next, &offset))
				goto out;

			if (ext[i].fe_flags & FIEMAP_EXTENT_LAST) {
				ret = -ENXIO;
				goto out;
			}
			offset = next;
		}
		if (map.m_flags & EXT4_MAP_UNWRITTEN) {
			int unwritten;
			unwritten = ext4_find_unwritten_pgoff(inode, SEEK_DATA,
							      &map, &dataoff);
			if (unwritten)
				break;
		}
	if (offset > inode->i_size)
		offset = inode->i_size;
out:

		last++;
		dataoff = (loff_t)last << blkbits;
	} while (last <= end);

	mutex_unlock(&inode->i_mutex);
	if (ret)
		return ret;

	return vfs_setpos(file, offset, maxsize);
	if (dataoff > isize)
		return -ENXIO;

	return vfs_setpos(file, dataoff, maxsize);
}

/*
 * ext4_seek_hole() retrieves the offset for SEEK_HOLE
 * ext4_seek_hole() retrieves the offset for SEEK_HOLE.
 */
static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize)
{
	struct inode *inode = file->f_mapping->host;
	struct fiemap_extent_info fie;
	struct fiemap_extent ext[2];
	loff_t next;
	int i, ret = 0;
	struct ext4_map_blocks map;
	struct extent_status es;
	ext4_lblk_t start, last, end;
	loff_t holeoff, isize;
	int blkbits;
	int ret = 0;

	mutex_lock(&inode->i_mutex);
	if (offset >= inode->i_size) {

	isize = i_size_read(inode);
	if (offset >= isize) {
		mutex_unlock(&inode->i_mutex);
		return -ENXIO;
	}

	fie.fi_flags = 0;
	fie.fi_extents_max = 2;
	fie.fi_extents_start = (struct fiemap_extent __user *)&ext;
	while (1) {
		mm_segment_t old_fs = get_fs();

		fie.fi_extents_mapped = 0;
		memset(ext, 0, sizeof(*ext));
	blkbits = inode->i_sb->s_blocksize_bits;
	start = offset >> blkbits;
	last = start;
	end = isize >> blkbits;
	holeoff = offset;

		set_fs(get_ds());
		ret = ext4_fiemap(inode, &fie, offset, maxsize - offset);
		set_fs(old_fs);
		if (ret)
			break;

		/* No extents found */
		if (!fie.fi_extents_mapped)
			break;
	do {
		map.m_lblk = last;
		map.m_len = end - last + 1;
		ret = ext4_map_blocks(NULL, inode, &map, 0);
		if (ret > 0 && !(map.m_flags & EXT4_MAP_UNWRITTEN)) {
			last += ret;
			holeoff = (loff_t)last << blkbits;
			continue;
		}

		for (i = 0; i < fie.fi_extents_mapped; i++) {
			next = (loff_t)(ext[i].fe_logical + ext[i].fe_length);
		/*
			 * If extent is not unwritten, then it contains valid
			 * data, mapped or delayed.
		 * If there is a delay extent at this offset,
		 * we will skip this extent.
		 */
			if (!(ext[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
				if (offset < (loff_t)ext[i].fe_logical)
					goto out;
				offset = next;
		ext4_es_find_delayed_extent_range(inode, last, last, &es);
		if (es.es_len != 0 && in_range(last, es.es_lblk, es.es_len)) {
			last = es.es_lblk + es.es_len;
			holeoff = (loff_t)last << blkbits;
			continue;
		}

		/*
		 * If there is a unwritten extent at this offset,
		 * it will be as a data or a hole according to page
		 * cache that has data or not.
		 */
			if (ext4_find_unwritten_pgoff(inode, SEEK_HOLE,
						      next, &offset))
				goto out;

			offset = next;
			if (ext[i].fe_flags & FIEMAP_EXTENT_LAST)
				goto out;
		if (map.m_flags & EXT4_MAP_UNWRITTEN) {
			int unwritten;
			unwritten = ext4_find_unwritten_pgoff(inode, SEEK_HOLE,
							      &map, &holeoff);
			if (!unwritten) {
				last += ret;
				holeoff = (loff_t)last << blkbits;
				continue;
			}
		}
	if (offset > inode->i_size)
		offset = inode->i_size;
out:

		/* find a hole */
		break;
	} while (last <= end);

	mutex_unlock(&inode->i_mutex);
	if (ret)
		return ret;

	return vfs_setpos(file, offset, maxsize);
	if (holeoff > isize)
		holeoff = isize;

	return vfs_setpos(file, holeoff, maxsize);
}

/*
+12 −12
Original line number Diff line number Diff line
@@ -23,6 +23,18 @@ int ext4_resize_begin(struct super_block *sb)
	if (!capable(CAP_SYS_RESOURCE))
		return -EPERM;

	/*
	 * If we are not using the primary superblock/GDT copy don't resize,
         * because the user tools have no way of handling this.  Probably a
         * bad time to do it anyways.
         */
	if (EXT4_SB(sb)->s_sbh->b_blocknr !=
	    le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) {
		ext4_warning(sb, "won't resize using backup superblock at %llu",
			(unsigned long long)EXT4_SB(sb)->s_sbh->b_blocknr);
		return -EPERM;
	}

	/*
	 * We are not allowed to do online-resizing on a filesystem mounted
	 * with error, because it can destroy the filesystem easily.
@@ -758,18 +770,6 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
		       "EXT4-fs: ext4_add_new_gdb: adding group block %lu\n",
		       gdb_num);

	/*
	 * If we are not using the primary superblock/GDT copy don't resize,
         * because the user tools have no way of handling this.  Probably a
         * bad time to do it anyways.
         */
	if (EXT4_SB(sb)->s_sbh->b_blocknr !=
	    le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) {
		ext4_warning(sb, "won't resize using backup superblock at %llu",
			(unsigned long long)EXT4_SB(sb)->s_sbh->b_blocknr);
		return -EPERM;
	}

	gdb_bh = sb_bread(sb, gdblock);
	if (!gdb_bh)
		return -EIO;
+1 −1
Original line number Diff line number Diff line
@@ -3482,7 +3482,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
	if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
	    EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
		ext4_warning(sb, KERN_INFO "metadata_csum and uninit_bg are "
		ext4_warning(sb, "metadata_csum and uninit_bg are "
			     "redundant flags; please run fsck.");

	/* Check for a known checksum algorithm */