diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig index 6bf2867dbdc6..e76ea9b62974 100644 --- a/arch/loongarch/configs/loongson32_defconfig +++ b/arch/loongarch/configs/loongson32_defconfig @@ -973,7 +973,6 @@ CONFIG_FS_VERITY=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_QFMT_V1=m CONFIG_QFMT_V2=m CONFIG_AUTOFS_FS=y diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig index def104c9d405..976340d4d514 100644 --- a/arch/loongarch/configs/loongson64_defconfig +++ b/arch/loongarch/configs/loongson64_defconfig @@ -1004,7 +1004,6 @@ CONFIG_FS_VERITY=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_QFMT_V1=m CONFIG_QFMT_V2=m CONFIG_AUTOFS_FS=y diff --git a/arch/mips/configs/bigsur_defconfig b/arch/mips/configs/bigsur_defconfig index 611dc0dd392d..d2b18d5033be 100644 --- a/arch/mips/configs/bigsur_defconfig +++ b/arch/mips/configs/bigsur_defconfig @@ -140,7 +140,6 @@ CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_FS=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_QFMT_V2=m CONFIG_AUTOFS_FS=m CONFIG_FUSE_FS=m diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index 009e66299b69..09a77e5b249c 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -238,7 +238,6 @@ CONFIG_XFS_FS=m CONFIG_XFS_QUOTA=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_QFMT_V2=m CONFIG_AUTOFS_FS=m CONFIG_FUSE_FS=m diff --git a/arch/mips/configs/loongson2k_defconfig b/arch/mips/configs/loongson2k_defconfig index ca534a6b66de..8fd863f4f7b0 100644 --- a/arch/mips/configs/loongson2k_defconfig +++ b/arch/mips/configs/loongson2k_defconfig @@ -303,7 +303,6 @@ CONFIG_XFS_FS=y CONFIG_XFS_QUOTA=y CONFIG_XFS_POSIX_ACL=y CONFIG_QUOTA=y -# CONFIG_PRINT_QUOTA_WARNING is not set CONFIG_AUTOFS_FS=y CONFIG_FUSE_FS=m CONFIG_ISO9660_FS=m diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 6b283fd03ff8..a77bbccb274d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1032,33 +1032,6 @@ static ssize_t iterate_tty_write(struct tty_ldisc *ld, struct tty_struct *tty, return ret; } -#ifdef CONFIG_PRINT_QUOTA_WARNING -/** - * tty_write_message - write a message to a certain tty, not just the console. - * @tty: the destination tty_struct - * @msg: the message to write - * - * This is used for messages that need to be redirected to a specific tty. We - * don't put it into the syslog queue right now maybe in the future if really - * needed. - * - * We must still hold the BTM and test the CLOSING flag for the moment. - * - * This function is DEPRECATED, do not use in new code. - */ -void tty_write_message(struct tty_struct *tty, char *msg) -{ - if (tty) { - mutex_lock(&tty->atomic_write_lock); - tty_lock(tty); - if (tty->ops->write && tty->count > 0) - tty->ops->write(tty, msg, strlen(msg)); - tty_unlock(tty); - tty_write_unlock(tty); - } -} -#endif - static ssize_t file_tty_write(struct file *file, struct kiocb *iocb, struct iov_iter *from) { struct tty_struct *tty = file_tty(file); diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index be63f89402a3..9b68c490ab26 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -780,16 +780,12 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, mark_inode_dirty(inode); if (IS_SYNC(inode)) { error = sync_inode_metadata(inode, 1); - /* In case sync failed due to ENOSPC the inode was actually - * written (only some dirty data were not) so we just proceed - * as if nothing happened and cleanup the unused block */ - if (error && error != -ENOSPC) { - if (new_bh && new_bh != old_bh) { - dquot_free_block_nodirty(inode, 1); - mark_inode_dirty(inode); - } + /* + * Inode writeout failed. Backing everything out is complex so + * let's just leave it for e2fsck to cleanup the mess. + */ + if (error) goto cleanup; - } } error = 0; diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c index 397568b9c7e7..f9869d62b850 100644 --- a/fs/isofs/compress.c +++ b/fs/isofs/compress.c @@ -65,12 +65,14 @@ static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start, /* Empty block? */ if (block_size == 0) { for ( i = 0 ; i < pcount ; i++ ) { + unsigned int off = i ? 0 : poffset; + if (!pages[i]) continue; - memzero_page(pages[i], 0, PAGE_SIZE); + memzero_page(pages[i], off, PAGE_SIZE - off); SetPageUptodate(pages[i]); } - return ((loff_t)pcount) << PAGE_SHIFT; + return (((loff_t)pcount) << PAGE_SHIFT) - poffset; } /* Because zlib is not thread-safe, do all the I/O at the top. */ @@ -291,6 +293,7 @@ static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount, memzero_page(*pages, poffset, PAGE_SIZE - poffset); SetPageUptodate(*pages); } + brelse(bh); return 0; } diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index cc587cd25162..c7ca7603e97a 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -84,7 +84,7 @@ int get_acorn_filename(struct iso_directory_record *de, */ static int do_isofs_readdir(struct inode *inode, struct file *file, struct dir_context *ctx, - char *tmpname, struct iso_directory_record *tmpde) + char *tmpname) { unsigned long bufsize = ISOFS_BUFFER_SIZE(inode); unsigned char bufbits = ISOFS_BUFFER_BITS(inode); @@ -133,26 +133,7 @@ static int do_isofs_readdir(struct inode *inode, struct file *file, offset_saved = offset; offset += de_len; - /* Make sure we have a full directory entry */ - if (offset >= bufsize) { - int slop = bufsize - offset + de_len; - memcpy(tmpde, de, slop); - offset &= bufsize - 1; - block++; - brelse(bh); - bh = NULL; - if (offset) { - bh = isofs_bread(inode, block); - if (!bh) - return 0; - memcpy((void *) tmpde + slop, bh->b_data, offset); - } - de = tmpde; - } - /* Basic sanity check, whether name doesn't exceed dir entry */ - if (de_len < sizeof(struct iso_directory_record) || - de_len < de->name_len[0] + - sizeof(struct iso_directory_record)) { + if (!isofs_dir_record_valid(de, offset_saved, bufsize)) { printk(KERN_NOTICE "iso9660: Corrupted directory entry" " in block %lu of inode %llu\n", block, inode->i_ino); @@ -254,16 +235,13 @@ static int isofs_readdir(struct file *file, struct dir_context *ctx) { int result; char *tmpname; - struct iso_directory_record *tmpde; struct inode *inode = file_inode(file); - tmpname = kmalloc(PAGE_SIZE, GFP_KERNEL); + tmpname = kmalloc(1024, GFP_KERNEL); if (tmpname == NULL) return -ENOMEM; - tmpde = (struct iso_directory_record *) (tmpname+1024); - - result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde); + result = do_isofs_readdir(inode, file, ctx, tmpname); kfree(tmpname); return result; @@ -300,4 +278,3 @@ const struct inode_operations isofs_dir_inode_operations = .fileattr_get = isofs_fileattr_get, }; - diff --git a/fs/isofs/export.c b/fs/isofs/export.c index 78f80c1a5c54..4f7fa1d508a1 100644 --- a/fs/isofs/export.c +++ b/fs/isofs/export.c @@ -83,13 +83,21 @@ static struct dentry *isofs_export_get_parent(struct dentry *child) /* This is the "." entry. */ de = (struct iso_directory_record*)bh->b_data; + if (!isofs_dir_record_valid(de, 0, child_inode->i_sb->s_blocksize) || + isonum_711(de->name_len) != 1 || de->name[0] != 0) { + printk(KERN_ERR "isofs: Unable to find the \".\" directory for NFS.\n"); + rv = ERR_PTR(-EACCES); + goto out; + } /* The ".." entry is always the second entry. */ parent_offset = (unsigned long)isonum_711(de->length); de = (struct iso_directory_record*)(bh->b_data + parent_offset); /* Verify it is in fact the ".." entry. */ - if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) { + if (!isofs_dir_record_valid(de, parent_offset, + child_inode->i_sb->s_blocksize) || + isonum_711(de->name_len) != 1 || de->name[0] != 1) { printk(KERN_ERR "isofs: Unable to find the \"..\" " "directory for NFS.\n"); rv = ERR_PTR(-EACCES); diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index 0ec8b24a42ed..dacb9cdae4fd 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -115,6 +115,9 @@ struct inode; /* To make gcc happy */ extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated); extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *); extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *); +bool isofs_dir_record_valid(struct iso_directory_record *de, + unsigned long offset, + unsigned long bufsize); int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *); int get_acorn_filename(struct iso_directory_record *, char *, struct inode *); diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 3ace3d6a55e7..010682f5901a 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -10,6 +10,26 @@ #include #include "isofs.h" +bool isofs_dir_record_valid(struct iso_directory_record *de, + unsigned long offset, + unsigned long bufsize) +{ + unsigned int len; + unsigned int name_len; + unsigned long min_len = offsetof(struct iso_directory_record, name); + + if (offset > bufsize || bufsize - offset < min_len) + return false; + + len = isonum_711(de->length); + name_len = isonum_711(de->name_len); + if (len < min_len || name_len > len - min_len) + return false; + if (len > bufsize - offset) + return false; + return true; +} + static int isofs_cmp(struct dentry *dentry, const char *compare, int dlen) { @@ -28,7 +48,7 @@ isofs_cmp(struct dentry *dentry, const char *compare, int dlen) static unsigned long isofs_find_entry(struct inode *dir, struct dentry *dentry, unsigned long *block_rv, unsigned long *offset_rv, - char *tmpname, struct iso_directory_record *tmpde) + char *tmpname) { unsigned long bufsize = ISOFS_BUFFER_SIZE(dir); unsigned char bufbits = ISOFS_BUFFER_BITS(dir); @@ -71,33 +91,15 @@ isofs_find_entry(struct inode *dir, struct dentry *dentry, offset += de_len; f_pos += de_len; - /* Make sure we have a full directory entry */ - if (offset >= bufsize) { - int slop = bufsize - offset + de_len; - memcpy(tmpde, de, slop); - offset &= bufsize - 1; - block++; - brelse(bh); - bh = NULL; - if (offset) { - bh = isofs_bread(dir, block); - if (!bh) - return 0; - memcpy((void *) tmpde + slop, bh->b_data, offset); - } - de = tmpde; - } - - dlen = de->name_len[0]; - dpnt = de->name; - /* Basic sanity check, whether name doesn't exceed dir entry */ - if (de_len < dlen + sizeof(struct iso_directory_record)) { + if (!isofs_dir_record_valid(de, offset_saved, bufsize)) { printk(KERN_NOTICE "iso9660: Corrupted directory entry" " in block %lu of inode %llu\n", block, dir->i_ino); brelse(bh); return 0; } + dlen = de->name_len[0]; + dpnt = de->name; if (sbi->s_rock && ((i = get_rock_ridge_filename(de, tmpname, dir)))) { @@ -149,17 +151,14 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned i unsigned long block; unsigned long offset; struct inode *inode; - struct page *page; + char *tmpname; - page = alloc_page(GFP_USER); - if (!page) + tmpname = kmalloc(1024, GFP_USER); + if (!tmpname) return ERR_PTR(-ENOMEM); - found = isofs_find_entry(dir, dentry, - &block, &offset, - page_address(page), - 1024 + page_address(page)); - __free_page(page); + found = isofs_find_entry(dir, dentry, &block, &offset, tmpname); + kfree(tmpname); inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL; diff --git a/fs/quota/Kconfig b/fs/quota/Kconfig index 818083a36bef..68eb441328ef 100644 --- a/fs/quota/Kconfig +++ b/fs/quota/Kconfig @@ -25,16 +25,6 @@ config QUOTA_NETLINK_INTERFACE hardlimit, etc.) will be reported through netlink interface. If unsure, say Y. -config PRINT_QUOTA_WARNING - bool "Print quota warnings to console (OBSOLETE)" - depends on QUOTA && BROKEN - default y - help - If you say Y here, quota warnings (about exceeding softlimit, reaching - hardlimit, etc.) will be printed to the process' controlling terminal. - Note that this behavior is currently deprecated and may go away in - future. Please use notification via netlink socket instead. - config QUOTA_DEBUG bool "Additional quota sanity checks" depends on QUOTA diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 9850de3955d3..204afc5e984b 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -153,7 +153,7 @@ void __quota_error(struct super_block *sb, const char *func, } EXPORT_SYMBOL(__quota_error); -#if defined(CONFIG_QUOTA_DEBUG) || defined(CONFIG_PRINT_QUOTA_WARNING) +#ifdef CONFIG_QUOTA_DEBUG static char *quotatypes[] = INITQFNAMES; #endif static struct quota_format_type *quota_formats; /* List of registered formats */ @@ -1208,72 +1208,6 @@ static int warning_issued(struct dquot *dquot, const int warntype) return test_and_set_bit(flag, &dquot->dq_flags); } -#ifdef CONFIG_PRINT_QUOTA_WARNING -static int flag_print_warnings = 1; - -static int need_print_warning(struct dquot_warn *warn) -{ - if (!flag_print_warnings) - return 0; - - switch (warn->w_dq_id.type) { - case USRQUOTA: - return uid_eq(current_fsuid(), warn->w_dq_id.uid); - case GRPQUOTA: - return in_group_p(warn->w_dq_id.gid); - case PRJQUOTA: - return 1; - } - return 0; -} - -/* Print warning to user which exceeded quota */ -static void print_warning(struct dquot_warn *warn) -{ - char *msg = NULL; - struct tty_struct *tty; - int warntype = warn->w_type; - - if (warntype == QUOTA_NL_IHARDBELOW || - warntype == QUOTA_NL_ISOFTBELOW || - warntype == QUOTA_NL_BHARDBELOW || - warntype == QUOTA_NL_BSOFTBELOW || !need_print_warning(warn)) - return; - - tty = get_current_tty(); - if (!tty) - return; - tty_write_message(tty, warn->w_sb->s_id); - if (warntype == QUOTA_NL_ISOFTWARN || warntype == QUOTA_NL_BSOFTWARN) - tty_write_message(tty, ": warning, "); - else - tty_write_message(tty, ": write failed, "); - tty_write_message(tty, quotatypes[warn->w_dq_id.type]); - switch (warntype) { - case QUOTA_NL_IHARDWARN: - msg = " file limit reached.\r\n"; - break; - case QUOTA_NL_ISOFTLONGWARN: - msg = " file quota exceeded too long.\r\n"; - break; - case QUOTA_NL_ISOFTWARN: - msg = " file quota exceeded.\r\n"; - break; - case QUOTA_NL_BHARDWARN: - msg = " block limit reached.\r\n"; - break; - case QUOTA_NL_BSOFTLONGWARN: - msg = " block quota exceeded too long.\r\n"; - break; - case QUOTA_NL_BSOFTWARN: - msg = " block quota exceeded.\r\n"; - break; - } - tty_write_message(tty, msg); - tty_kref_put(tty); -} -#endif - static void prepare_warning(struct dquot_warn *warn, struct dquot *dquot, int warntype) { @@ -1296,9 +1230,7 @@ static void flush_warnings(struct dquot_warn *warn) for (i = 0; i < MAXQUOTAS; i++) { if (warn[i].w_type == QUOTA_NL_NOWARN) continue; -#ifdef CONFIG_PRINT_QUOTA_WARNING - print_warning(&warn[i]); -#endif + quota_send_warning(warn[i].w_dq_id, warn[i].w_sb->s_dev, warn[i].w_type); } @@ -3008,15 +2940,6 @@ static const struct ctl_table fs_dqstats_table[] = { .mode = 0444, .proc_handler = do_proc_dqstats, }, -#ifdef CONFIG_PRINT_QUOTA_WARNING - { - .procname = "warnings", - .data = &flag_print_warnings, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, -#endif }; static int __init dquot_init(void) diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c index cc6dc6e1d84d..30cec5600149 100644 --- a/fs/udf/balloc.c +++ b/fs/udf/balloc.c @@ -502,6 +502,8 @@ static int udf_table_prealloc_blocks(struct super_block *sb, int8_t etype = -1; struct udf_inode_info *iinfo; int ret = 0; + /* AED block freed by udf_delete_aext(), released after unlock */ + struct kernel_lb_addr freed = { .partitionReferenceNum = 0xFFFF }; if (first_block >= sbi->s_partmaps[partition].s_partition_len) return 0; @@ -541,7 +543,7 @@ static int udf_table_prealloc_blocks(struct super_block *sb, udf_write_aext(table, &epos, &eloc, (etype << 30) | elen, 1); } else - udf_delete_aext(table, epos); + udf_delete_aext(table, epos, &freed); } else { alloc_count = 0; } @@ -552,6 +554,8 @@ static int udf_table_prealloc_blocks(struct super_block *sb, if (alloc_count) udf_add_free_space(sb, partition, -alloc_count); mutex_unlock(&sbi->s_alloc_mutex); + if (freed.partitionReferenceNum != 0xFFFF) + udf_free_blocks(sb, table, &freed, 0, 1); return alloc_count; } @@ -560,6 +564,8 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb, uint32_t goal, int *err) { struct udf_sb_info *sbi = UDF_SB(sb); + /* AED block freed by udf_delete_aext(), released after unlock */ + struct kernel_lb_addr freed = { .partitionReferenceNum = 0xFFFF }; uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF; udf_pblk_t newblock = 0; uint32_t adsize; @@ -643,12 +649,14 @@ static udf_pblk_t udf_table_new_block(struct super_block *sb, if (goal_elen) udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1); else - udf_delete_aext(table, goal_epos); + udf_delete_aext(table, goal_epos, &freed); brelse(goal_epos.bh); udf_add_free_space(sb, partition, -1); mutex_unlock(&sbi->s_alloc_mutex); + if (freed.partitionReferenceNum != 0xFFFF) + udf_free_blocks(sb, table, &freed, 0, 1); *err = 0; return newblock; } diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 68c6c2ba8ed1..e45e546a739a 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -334,65 +334,6 @@ const struct address_space_operations udf_aops = { .migrate_folio = buffer_migrate_folio, }; -/* - * Expand file stored in ICB to a normal one-block-file - * - * This function requires i_mutex held - */ -int udf_expand_file_adinicb(struct inode *inode) -{ - struct folio *folio; - struct udf_inode_info *iinfo = UDF_I(inode); - int err; - - WARN_ON_ONCE(!inode_is_locked(inode)); - if (!iinfo->i_lenAlloc) { - down_write(&iinfo->i_data_sem); - if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) - iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; - else - iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; - up_write(&iinfo->i_data_sem); - mark_inode_dirty(inode); - return 0; - } - - folio = __filemap_get_folio(inode->i_mapping, 0, - FGP_LOCK | FGP_ACCESSED | FGP_CREAT, GFP_KERNEL); - if (IS_ERR(folio)) - return PTR_ERR(folio); - - if (!folio_test_uptodate(folio)) - udf_adinicb_read_folio(folio); - down_write(&iinfo->i_data_sem); - memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00, - iinfo->i_lenAlloc); - iinfo->i_lenAlloc = 0; - if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) - iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; - else - iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; - folio_mark_dirty(folio); - folio_unlock(folio); - up_write(&iinfo->i_data_sem); - err = filemap_fdatawrite(inode->i_mapping); - if (err) { - /* Restore everything back so that we don't lose data... */ - folio_lock(folio); - down_write(&iinfo->i_data_sem); - memcpy_from_folio(iinfo->i_data + iinfo->i_lenEAttr, - folio, 0, inode->i_size); - folio_unlock(folio); - iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; - iinfo->i_lenAlloc = inode->i_size; - up_write(&iinfo->i_data_sem); - } - folio_put(folio); - mark_inode_dirty(inode); - - return err; -} - #define UDF_MAP_CREATE 0x01 /* Mapping can allocate new blocks */ #define UDF_MAP_NOPREALLOC 0x02 /* Do not preallocate blocks */ @@ -453,6 +394,76 @@ static int udf_map_block(struct inode *inode, struct udf_map_rq *map) return ret; } +/* + * Expand file stored in ICB to a normal one-block-file + * + * This function requires i_mutex held + */ +int udf_expand_file_adinicb(struct inode *inode) +{ + struct folio *folio; + struct udf_inode_info *iinfo = UDF_I(inode); + struct udf_map_rq map = { + .lblk = 0, + .iflags = UDF_MAP_CREATE, + }; + int err; + + WARN_ON_ONCE(!inode_is_locked(inode)); + if (!iinfo->i_lenAlloc) { + down_write(&iinfo->i_data_sem); + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) + iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; + else + iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; + up_write(&iinfo->i_data_sem); + mark_inode_dirty(inode); + return 0; + } + + folio = __filemap_get_folio(inode->i_mapping, 0, + FGP_LOCK | FGP_ACCESSED | FGP_CREAT, GFP_KERNEL); + if (IS_ERR(folio)) + return PTR_ERR(folio); + + if (!folio_test_uptodate(folio)) + udf_adinicb_read_folio(folio); + down_write(&iinfo->i_data_sem); + memset(iinfo->i_data + iinfo->i_lenEAttr, 0x00, + iinfo->i_lenAlloc); + iinfo->i_lenAlloc = 0; + if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD)) + iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT; + else + iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG; + up_write(&iinfo->i_data_sem); + + /* Allocate the block underlying the data */ + err = udf_map_block(inode, &map); + if (err < 0) + goto restore; + + folio_mark_dirty(folio); + folio_unlock(folio); + err = filemap_fdatawrite(inode->i_mapping); + if (err) { + /* Restore everything back so that we don't lose data... */ + folio_lock(folio); +restore: + down_write(&iinfo->i_data_sem); + memcpy_from_folio(iinfo->i_data + iinfo->i_lenEAttr, + folio, 0, inode->i_size); + iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB; + iinfo->i_lenAlloc = inode->i_size; + up_write(&iinfo->i_data_sem); + folio_unlock(folio); + } + folio_put(folio); + mark_inode_dirty(inode); + + return err; +} + static int __udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int flags) { @@ -529,7 +540,7 @@ static int udf_do_extend_file(struct inode *inode, sb->s_blocksize - 1) & ~(sb->s_blocksize - 1)); iinfo->i_lenExtents = (iinfo->i_lenExtents + sb->s_blocksize - 1) & - ~(sb->s_blocksize - 1); + ~((u64)sb->s_blocksize - 1); } add = 0; @@ -1199,7 +1210,7 @@ static int udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr, if (startnum > endnum) { for (i = 0; i < (startnum - endnum); i++) - udf_delete_aext(inode, *epos); + udf_delete_aext(inode, *epos, NULL); } else if (startnum < endnum) { for (i = 0; i < (endnum - startnum); i++) { err = udf_insert_aext(inode, *epos, @@ -1467,6 +1478,10 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) iinfo->i_lenAlloc = le32_to_cpu( ((struct unallocSpaceEntry *)bh->b_data)-> lengthAllocDescs); + if (iinfo->i_lenAlloc > bs - sizeof(struct unallocSpaceEntry)) { + ret = -EFSCORRUPTED; + goto out; + } ret = udf_alloc_i_data(inode, bs - sizeof(struct unallocSpaceEntry)); if (ret) @@ -1474,6 +1489,7 @@ static int udf_read_inode(struct inode *inode, bool hidden_inode) memcpy(iinfo->i_data, bh->b_data + sizeof(struct unallocSpaceEntry), bs - sizeof(struct unallocSpaceEntry)); + brelse(bh); return 0; } @@ -2297,6 +2313,13 @@ int udf_current_aext(struct inode *inode, struct extent_position *epos, return -EINVAL; } + if (eloc->partitionReferenceNum >= UDF_SB(inode->i_sb)->s_partitions) { + udf_debug("invalid partition reference %u (partitions %u)\n", + eloc->partitionReferenceNum, + UDF_SB(inode->i_sb)->s_partitions); + return -EFSCORRUPTED; + } + return 1; } @@ -2326,7 +2349,8 @@ static int udf_insert_aext(struct inode *inode, struct extent_position epos, return ret; } -int8_t udf_delete_aext(struct inode *inode, struct extent_position epos) +int8_t udf_delete_aext(struct inode *inode, struct extent_position epos, + struct kernel_lb_addr *freed) { struct extent_position oepos; int adsize; @@ -2376,7 +2400,19 @@ int8_t udf_delete_aext(struct inode *inode, struct extent_position epos) elen = 0; if (epos.bh != oepos.bh) { - udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1); + /* + * The block that held the now-empty allocation extent must be + * returned to free space. When the caller already holds + * s_alloc_mutex (the space-table allocator in balloc.c), + * freeing it inline would recurse through udf_free_blocks() + * into udf_table_free_blocks() and deadlock re-acquiring + * s_alloc_mutex. In that case report the block to the caller, + * which frees it after dropping the lock. + */ + if (freed) + *freed = epos.block; + else + udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1); udf_write_aext(inode, &oepos, &eloc, elen, 1); udf_write_aext(inode, &oepos, &eloc, elen, 1); if (!oepos.bh) { diff --git a/fs/udf/partition.c b/fs/udf/partition.c index 2b85c9501bed..ad8dcedca263 100644 --- a/fs/udf/partition.c +++ b/fs/udf/partition.c @@ -55,7 +55,7 @@ uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, map = &sbi->s_partmaps[partition]; vdata = &map->s_type_specific.s_virtual; - if (block > vdata->s_num_entries) { + if (block >= vdata->s_num_entries) { udf_debug("Trying to access block beyond end of VAT (%u max %u)\n", block, vdata->s_num_entries); return 0xFFFFFFFF; diff --git a/fs/udf/super.c b/fs/udf/super.c index e7e9f2a0d24e..2ba5973ef4dd 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -2055,6 +2055,17 @@ static int udf_load_vrs(struct super_block *sb, struct udf_options *uopt, return 0; } +static void udf_mark_buffer_dirty(struct buffer_head *bh) +{ + /* + * We set buffer uptodate unconditionally here to avoid spurious + * warnings from mark_buffer_dirty() when previous EIO has marked + * the buffer as !uptodate + */ + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); +} + static void udf_finalize_lvid(struct logicalVolIntegrityDesc *lvid) { struct timespec64 ts; @@ -2090,7 +2101,7 @@ static void udf_open_lvid(struct super_block *sb) UDF_SET_FLAG(sb, UDF_FLAG_INCONSISTENT); udf_finalize_lvid(lvid); - mark_buffer_dirty(bh); + udf_mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); /* Make opening of filesystem visible on the media immediately */ @@ -2123,14 +2134,8 @@ static void udf_close_lvid(struct super_block *sb) if (!UDF_QUERY_FLAG(sb, UDF_FLAG_INCONSISTENT)) lvid->integrityType = cpu_to_le32(LVID_INTEGRITY_TYPE_CLOSE); - /* - * We set buffer uptodate unconditionally here to avoid spurious - * warnings from mark_buffer_dirty() when previous EIO has marked - * the buffer as !uptodate - */ - set_buffer_uptodate(bh); udf_finalize_lvid(lvid); - mark_buffer_dirty(bh); + udf_mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; mutex_unlock(&sbi->s_alloc_mutex); /* Make closing of filesystem visible on the media immediately */ @@ -2412,7 +2417,7 @@ static int udf_sync_fs(struct super_block *sb, int wait) * Blockdevice will be synced later so we don't have to submit * the buffer for IO */ - mark_buffer_dirty(bh); + udf_mark_buffer_dirty(bh); sbi->s_lvid_dirty = 0; } mutex_unlock(&sbi->s_alloc_mutex); diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index fe03745d09b1..a05d1888a2ba 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -36,6 +36,8 @@ static int udf_pc_to_char(struct super_block *sb, unsigned char *from, /* Reserve one byte for terminating \0 */ tolen--; while (elen < fromlen) { + if (fromlen - elen < sizeof(struct pathComponent)) + return -EIO; pc = (struct pathComponent *)(from + elen); elen += sizeof(struct pathComponent); switch (pc->componentType) { diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c index 41b2bfd30449..0990f94b8551 100644 --- a/fs/udf/truncate.c +++ b/fs/udf/truncate.c @@ -159,7 +159,7 @@ void udf_discard_prealloc(struct inode *inode) if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { lbcount -= elen; - udf_delete_aext(inode, prev_epos); + udf_delete_aext(inode, prev_epos, NULL); udf_free_blocks(inode->i_sb, inode, &eloc, 0, DIV_ROUND_UP(elen, bsize)); } diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 86dc2d6a2ef1..7d5a1981434e 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -170,7 +170,8 @@ extern int udf_add_aext(struct inode *, struct extent_position *, struct kernel_lb_addr *, uint32_t, int); extern void udf_write_aext(struct inode *, struct extent_position *, struct kernel_lb_addr *, uint32_t, int); -extern int8_t udf_delete_aext(struct inode *, struct extent_position); +extern int8_t udf_delete_aext(struct inode *, struct extent_position, + struct kernel_lb_addr *); extern int udf_next_aext(struct inode *inode, struct extent_position *epos, struct kernel_lb_addr *eloc, uint32_t *elen, int8_t *etype, int inc);