diff --git a/MAINTAINERS b/MAINTAINERS index fe23913b1e43..d7798fdde245 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7838,7 +7838,7 @@ W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html DISKQUOTA -M: Jan Kara +M: Jan Kara S: Maintained F: Documentation/filesystems/quota.rst F: fs/quota/ @@ -10109,7 +10109,7 @@ F: drivers/leds/leds-expresswire.c F: include/linux/leds-expresswire.h EXT2 FILE SYSTEM -M: Jan Kara +M: Jan Kara L: linux-ext4@vger.kernel.org S: Maintained F: Documentation/filesystems/ext2.rst @@ -14324,7 +14324,7 @@ F: include/uapi/linux/jffs2.h JOURNALLING LAYER FOR BLOCK DEVICES (JBD2) M: "Theodore Ts'o" -M: Jan Kara +M: Jan Kara L: linux-ext4@vger.kernel.org S: Maintained F: fs/jbd2/ @@ -28714,7 +28714,7 @@ F: arch/m68k/coldfire/ F: arch/m68k/include/asm/*_no.* UDF FILESYSTEM -M: Jan Kara +M: Jan Kara S: Maintained F: Documentation/filesystems/udf.rst F: fs/udf/ diff --git a/fs/dcache.c b/fs/dcache.c index 9264f0853741..720b32cb2e03 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "internal.h" #include "mount.h" @@ -3690,6 +3691,7 @@ void __init vfs_caches_init(void) inode_init(); files_init(); files_maxfiles_init(); + dquot_cache_init(); mnt_init(); bdev_cache_init(); chrdev_init(); diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index e17bbc7598c1..5de32c221a83 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -442,6 +442,9 @@ static int ext2_handle_dirsync(struct inode *dir) { int err; + if (!IS_DIRSYNC(dir)) + return 0; + err = filemap_write_and_wait(dir->i_mapping); if (!err) err = sync_inode_metadata(dir, 1); diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 67fe423c3828..8ddc149d4884 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -145,7 +145,8 @@ static ssize_t ext2_dio_write_iter(struct kiocb *iocb, struct iov_iter *from) pos = iocb->ki_pos; status = generic_perform_write(iocb, from); if (unlikely(status < 0)) { - ret = status; + if (!ret) + ret = status; goto out_unlock; } diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 12ac4cfe1500..1989258aef8a 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -58,8 +58,10 @@ void ext2_write_failed(struct address_space *mapping, loff_t to) struct inode *inode = mapping->host; if (to > inode->i_size) { + filemap_invalidate_lock(mapping); truncate_pagecache(inode, inode->i_size); ext2_truncate_blocks(inode, inode->i_size); + filemap_invalidate_unlock(mapping); } } @@ -805,6 +807,12 @@ static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, (first_block << blkbits) < i_size_read(inode)) create = 0; + ret = ext2_get_blocks(inode, first_block, max_blocks, + &bno, &new, &boundary, create); + if (ret < 0) + return ret; + + iomap->flags = 0; /* * Writes that span EOF might trigger an IO size update on completion, * so consider them to be dirty for the purposes of O_DSYNC even if @@ -812,13 +820,6 @@ static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, */ if ((flags & IOMAP_WRITE) && offset + length > i_size_read(inode)) iomap->flags |= IOMAP_F_DIRTY; - - ret = ext2_get_blocks(inode, first_block, max_blocks, - &bno, &new, &boundary, create); - if (ret < 0) - return ret; - - iomap->flags = 0; iomap->offset = (u64)first_block << blkbits; iomap->bdev = inode->i_sb->s_bdev; @@ -1236,9 +1237,7 @@ static void ext2_truncate_blocks(struct inode *inode, loff_t offset) if (ext2_inode_is_fast_symlink(inode)) return; - filemap_invalidate_lock(inode->i_mapping); __ext2_truncate_blocks(inode, offset); - filemap_invalidate_unlock(inode->i_mapping); } static int ext2_setsize(struct inode *inode, loff_t newsize) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fb217ef2d69f..bde5b433776c 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -883,17 +883,18 @@ static int ext2_fill_super(struct super_block *sb, struct fs_context *fc) __le32 features; int err; - sbi = kzalloc_obj(*sbi); - if (!sbi) + sb->s_fs_info = kzalloc_obj(*sbi); + if (!sb->s_fs_info) return -ENOMEM; + sbi = EXT2_SB(sb); sbi->s_blockgroup_lock = kzalloc_obj(struct blockgroup_lock); if (!sbi->s_blockgroup_lock) { - kfree(sbi); + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; return -ENOMEM; } - sb->s_fs_info = sbi; sbi->s_sb_block = sb_block; guard(spinlock_init)(&EXT2_SB(sb)->s_lock); diff --git a/fs/isofs/util.c b/fs/isofs/util.c index 42f479da0b28..6f9a90e58a89 100644 --- a/fs/isofs/util.c +++ b/fs/isofs/util.c @@ -25,7 +25,7 @@ struct timespec64 iso_date(u8 *p, int flags) year = (p[0] - '0') * 1000 + (p[1] - '0') * 100 + (p[2] - '0') * 10 + - (p[3] - '0') - 1900; + (p[3] - '0'); month = ((p[4] - '0') * 10 + (p[5] - '0')); day = ((p[6] - '0') * 10 + (p[7] - '0')); hour = ((p[8] - '0') * 10 + (p[9] - '0')); @@ -34,7 +34,7 @@ struct timespec64 iso_date(u8 *p, int flags) ts.tv_nsec = ((p[14] - '0') * 10 + (p[15] - '0')) * 10000000; tz = p[16]; } else { - year = p[0]; + year = p[0] + 1900; month = p[1]; day = p[2]; hour = p[3]; @@ -45,10 +45,10 @@ struct timespec64 iso_date(u8 *p, int flags) tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6]; } - if (year < 0) { + if (year < 1) { ts.tv_sec = 0; } else { - ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second); + ts.tv_sec = mktime64(year, month, day, hour, minute, second); /* sign extend */ if (tz & 0x80) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 3ad19d7e472f..a3d503591871 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2958,7 +2958,7 @@ static const struct ctl_table fs_dqstats_table[] = { }, }; -static int __init dquot_init(void) +void __init dquot_cache_init(void) { int i, ret; unsigned long nr_hash; @@ -2966,8 +2966,6 @@ static int __init dquot_init(void) printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__); - register_sysctl_init("fs/quota", fs_dqstats_table); - dquot_cachep = kmem_cache_create("dquot", sizeof(struct dquot), sizeof(unsigned long) * 4, (SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT| @@ -3008,7 +3006,11 @@ static int __init dquot_init(void) WQ_UNBOUND | WQ_MEM_RECLAIM, WQ_MAX_ACTIVE); if (!quota_unbound_wq) panic("Cannot create quota_unbound_wq\n"); +} +static int __init init_fs_dquot_sysctls(void) +{ + register_sysctl_init("fs/quota", fs_dqstats_table); return 0; } -fs_initcall(dquot_init); +fs_initcall(init_fs_dquot_sysctls); diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index 30cec5600149..b6551cc7c6cf 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -457,6 +457,7 @@ static void udf_table_free_blocks(struct super_block *sb, int adsize; + eloc.partitionReferenceNum = bloc->partitionReferenceNum; eloc.logicalBlockNum = start; elen = EXT_RECORDED_ALLOCATED | (count << sb->s_blocksize_bits); @@ -524,30 +525,22 @@ static int udf_table_prealloc_blocks(struct super_block *sb, while (first_block != eloc.logicalBlockNum) { ret = udf_next_aext(table, &epos, &eloc, &elen, &etype, 1); - if (ret < 0) + if (ret <= 0) goto err_out; - if (ret == 0) - break; udf_debug("eloc=%u, elen=%u, first_block=%u\n", eloc.logicalBlockNum, elen, first_block); } - if (first_block == eloc.logicalBlockNum) { - epos.offset -= adsize; - - alloc_count = (elen >> sb->s_blocksize_bits); - if (alloc_count > block_count) { - alloc_count = block_count; - eloc.logicalBlockNum += alloc_count; - elen -= (alloc_count << sb->s_blocksize_bits); - udf_write_aext(table, &epos, &eloc, - (etype << 30) | elen, 1); - } else - udf_delete_aext(table, epos, &freed); - } else { - alloc_count = 0; - } + epos.offset -= adsize; + alloc_count = (elen >> sb->s_blocksize_bits); + if (alloc_count > block_count) { + alloc_count = block_count; + eloc.logicalBlockNum += alloc_count; + elen -= (alloc_count << sb->s_blocksize_bits); + udf_write_aext(table, &epos, &eloc, (etype << 30) | elen, 1); + } else + udf_delete_aext(table, epos, &freed); err_out: brelse(epos.bh); @@ -572,7 +565,7 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb, uint32_t elen, goal_elen = 0; struct kernel_lb_addr eloc, goal_eloc; struct extent_position epos, goal_epos; - int8_t etype; + int8_t etype, goal_etype = 0; struct udf_inode_info *iinfo = UDF_I(table); int ret = 0; @@ -623,7 +616,8 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb, goal_epos.block = epos.block; goal_epos.offset = epos.offset - adsize; goal_eloc = eloc; - goal_elen = (etype << 30) | elen; + goal_elen = elen; + goal_etype = etype; } } @@ -647,7 +641,8 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb, goal_elen -= sb->s_blocksize; if (goal_elen) - udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1); + udf_write_aext(table, &goal_epos, &goal_eloc, + (goal_etype << 30) | goal_elen, 1); else udf_delete_aext(table, goal_epos, &freed); brelse(goal_epos.bh); diff --git a/fs/udf/inode.c b/fs/udf/inode.c index bacf2a86161e..c425096a22ba 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -1607,6 +1607,11 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) */ if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs) goto out; + /* We need to have space at least for generic descriptor */ + if (iinfo->i_lenEAttr && iinfo->i_lenEAttr < + sizeof(struct extendedAttrHeaderDesc)) + goto out; + /* Now do exact checks */ if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs) goto out; @@ -2325,10 +2330,10 @@ int udf_current_aext(struct inode *inode, struct extent_position *epos, ptr = epos->bh->b_data + epos->offset; if (check_add_overflow(sizeof(struct allocExtDesc), le32_to_cpu(header->lengthAllocDescs), &alen)) - return -1; + return -EFSCORRUPTED; if (alen > epos->bh->b_size) - return -1; + return -EFSCORRUPTED; } switch (iinfo->i_alloc_type) { diff --git a/fs/udf/super.c b/fs/udf/super.c index 5351755aca3e..a81a8509524a 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1106,6 +1106,73 @@ static int check_partition_desc(struct super_block *sb, return 0; } +/* + * Check the Unallocated Space Table once when it is loaded: the allocator + * hands out blocks from the start of each extent and trusts that every + * extent covers at least one whole block inside the partition. + */ +static int udf_check_unalloc_table(struct super_block *sb, + struct inode *table, u32 partition_len) +{ + struct extent_position epos = { + .block = UDF_I(table)->i_location, + .offset = sizeof(struct unallocSpaceEntry), + }; + struct kernel_lb_addr eloc; + uint32_t elen, blocks; + struct kernel_lb_addr seen_block = {}; + uint32_t seen_offset = 0; + u64 steps = 0, period = 1; + int8_t etype; + int ret; + + /* The walk below assumes the layout of an Unallocated Space Entry */ + if (!UDF_I(table)->i_use) { + udf_err(sb, "unallocated space table is not an unallocated space entry\n"); + return -EFSCORRUPTED; + } + + while ((ret = udf_next_aext(table, &epos, &eloc, &elen, &etype, 1)) > 0) { + blocks = elen >> sb->s_blocksize_bits; + if (!blocks || (elen & (sb->s_blocksize - 1)) || + eloc.logicalBlockNum >= partition_len || + blocks > partition_len - eloc.logicalBlockNum) { + udf_err(sb, "invalid unallocated space table extent (block %u, length %u)\n", + eloc.logicalBlockNum, elen); + ret = -EFSCORRUPTED; + break; + } + /* + * A chain of allocation extents can lead back to itself, and + * then the walk returns the same extents forever. Remember a + * position at doubling intervals; a walk that comes back to it + * is in a loop. + */ + if (epos.block.logicalBlockNum == seen_block.logicalBlockNum && + epos.block.partitionReferenceNum == + seen_block.partitionReferenceNum && + epos.offset == seen_offset) { + udf_err(sb, "unallocated space table loops back on itself\n"); + ret = -EFSCORRUPTED; + break; + } + if (++steps == period) { + seen_block = epos.block; + seen_offset = epos.offset; + steps = 0; + period <<= 1; + } + if (fatal_signal_pending(current)) { + udf_err(sb, "interrupted while checking the unallocated space table\n"); + ret = -EINTR; + break; + } + cond_resched(); + } + brelse(epos.bh); + return ret; +} + static int udf_fill_partdesc_info(struct super_block *sb, struct partitionDesc *p, int p_index) { @@ -1166,6 +1233,16 @@ static int udf_fill_partdesc_info(struct super_block *sb, p_index); return PTR_ERR(inode); } + err = udf_check_unalloc_table(sb, inode, map->s_partition_len); + if (err) { + iput(inode); + if (err == -EINTR) + return err; + if (!sb_rdonly(sb)) + return -EACCES; + UDF_SET_FLAG(sb, UDF_FLAG_RW_INCOMPAT); + return 0; + } map->s_uspace.s_table = inode; map->s_partition_flags |= UDF_PART_FLAG_UNALLOC_TABLE; udf_debug("unallocSpaceTable (part %d) @ %llu\n", @@ -1252,6 +1329,12 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) (sbi->s_vat_inode->i_size - 36) >> 2; } else if (map->s_partition_type == UDF_VIRTUAL_MAP20) { vati = UDF_I(sbi->s_vat_inode); + if (sbi->s_vat_inode->i_size < + sizeof(struct virtualAllocationTable20)) { + udf_err(sb, "Too short VAT inode size %lld\n", + sbi->s_vat_inode->i_size); + return -EFSCORRUPTED; + } if (vati->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { int err = 0; @@ -1269,8 +1352,12 @@ static int udf_load_vat(struct super_block *sb, int p_index, int type1_index) map->s_type_specific.s_virtual.s_start_offset = le16_to_cpu(vat20->lengthHeader); - if (map->s_type_specific.s_virtual.s_start_offset - > sbi->s_vat_inode->i_size) { + if (map->s_type_specific.s_virtual.s_start_offset < + sizeof(struct virtualAllocationTable20) || + map->s_type_specific.s_virtual.s_start_offset > + sb->s_blocksize || + map->s_type_specific.s_virtual.s_start_offset > + sbi->s_vat_inode->i_size) { udf_err(sb, "Corrupted VAT header length %u (VAT inode size %lld)\n", map->s_type_specific.s_virtual.s_start_offset, sbi->s_vat_inode->i_size); @@ -2233,8 +2320,9 @@ static int udf_fill_super(struct super_block *sb, struct fs_context *fc) /* * EACCES is special - we want to propagate to * upper layers that we cannot handle RW mount. + * EINTR means that a fatal signal is pending. */ - if (ret == -EACCES) + if (ret == -EACCES || ret == -EINTR) break; } else break; diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 0990f94b8551..75151a59f0da 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -124,8 +124,8 @@ void udf_discard_prealloc(struct inode *inode) { struct extent_position epos = {}; struct extent_position prev_epos = {}; - struct kernel_lb_addr eloc; - uint32_t elen; + struct kernel_lb_addr eloc, tmpeloc; + uint32_t elen, tmpelen; uint64_t lbcount = 0; int8_t etype = -1; struct udf_inode_info *iinfo = UDF_I(inode); @@ -141,7 +141,8 @@ void udf_discard_prealloc(struct inode *inode) /* Find the last extent in the file */ while (1) { - ret = udf_next_aext(inode, &epos, &eloc, &elen, &tmpetype, 0); + ret = udf_next_aext(inode, &epos, &tmpeloc, &tmpelen, + &tmpetype, 0); if (ret < 0) goto out; if (ret == 0) diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 0c64ca674e77..7346efc85d43 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -40,6 +40,7 @@ void __quota_error(struct super_block *sb, const char *func, /* * declaration of quota_function calls in kernel. */ +void dquot_cache_init(void); int dquot_initialize(struct inode *inode); bool dquot_initialize_needed(struct inode *inode); void dquot_drop(struct inode *inode); @@ -206,6 +207,10 @@ static inline int sb_has_quota_active(struct super_block *sb, int type) return 0; } +static inline void dquot_cache_init(void) +{ +} + static inline int dquot_initialize(struct inode *inode) { return 0;