From f44840ad1f822d9ecee6a3f91f2d17825a361307 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Mon, 6 May 2019 09:39:03 +0800 Subject: quota: add dqi_dirty_list description to comment of Dquot List Management Actually there are four lists for dquot management, so add the description of dqui_dirty_list to comment. Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/quota/dquot.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 9ad72ea7f71f..f9b8602d5f2d 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -223,9 +223,9 @@ static void put_quota_format(struct quota_format_type *fmt) /* * Dquot List Management: - * The quota code uses three lists for dquot management: the inuse_list, - * free_dquots, and dquot_hash[] array. A single dquot structure may be - * on all three lists, depending on its current state. + * The quota code uses four lists for dquot management: the inuse_list, + * free_dquots, dqi_dirty_list, and dquot_hash[] array. A single dquot + * structure may be on some of those lists, depending on its current state. * * All dquots are placed to the end of inuse_list when first created, and this * list is used for invalidate operation, which must look at every dquot. @@ -236,6 +236,11 @@ static void put_quota_format(struct quota_format_type *fmt) * dqstats.free_dquots gives the number of dquots on the list. When * dquot is invalidated it's completely released from memory. * + * Dirty dquots are added to the dqi_dirty_list of quota_info when mark + * dirtied, and this list is searched when writing dirty dquots back to + * quota file. Note that some filesystems do dirty dquot tracking on their + * own (e.g. in a journal) and thus don't use dqi_dirty_list. + * * Dquots with a specific identity (device, type and id) are placed on * one of the dquot_hash[] hash chains. The provides an efficient search * mechanism to locate a specific dquot. -- cgit v1.2.3-59-g8ed1b From 02475de9bb23ed5381f0fb04a84a3c4fad90582d Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 14 May 2019 06:40:41 +0800 Subject: ext2: introduce helper for xattr header validation Introduce helper function ext2_xattr_header_valid() for xattr header validation and clean up the header check ralated code. Reviewed-by: Andreas Dilger Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 1e33e0ac8cf1..db27260d6a5b 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -134,6 +134,16 @@ ext2_xattr_handler(int name_index) return handler; } +static bool +ext2_xattr_header_valid(struct ext2_xattr_header *header) +{ + if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || + header->h_blocks != cpu_to_le32(1)) + return false; + + return true; +} + /* * ext2_xattr_get() * @@ -176,9 +186,9 @@ ext2_xattr_get(struct inode *inode, int name_index, const char *name, ea_bdebug(bh, "b_count=%d, refcount=%d", atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); end = bh->b_data + bh->b_size; - if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || - HDR(bh)->h_blocks != cpu_to_le32(1)) { -bad_block: ext2_error(inode->i_sb, "ext2_xattr_get", + if (!ext2_xattr_header_valid(HDR(bh))) { +bad_block: + ext2_error(inode->i_sb, "ext2_xattr_get", "inode %ld: bad block %d", inode->i_ino, EXT2_I(inode)->i_file_acl); error = -EIO; @@ -266,9 +276,9 @@ ext2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size) ea_bdebug(bh, "b_count=%d, refcount=%d", atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); end = bh->b_data + bh->b_size; - if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || - HDR(bh)->h_blocks != cpu_to_le32(1)) { -bad_block: ext2_error(inode->i_sb, "ext2_xattr_list", + if (!ext2_xattr_header_valid(HDR(bh))) { +bad_block: + ext2_error(inode->i_sb, "ext2_xattr_list", "inode %ld: bad block %d", inode->i_ino, EXT2_I(inode)->i_file_acl); error = -EIO; @@ -406,9 +416,9 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name, le32_to_cpu(HDR(bh)->h_refcount)); header = HDR(bh); end = bh->b_data + bh->b_size; - if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || - header->h_blocks != cpu_to_le32(1)) { -bad_block: ext2_error(sb, "ext2_xattr_set", + if (!ext2_xattr_header_valid(header)) { +bad_block: + ext2_error(sb, "ext2_xattr_set", "inode %ld: bad block %d", inode->i_ino, EXT2_I(inode)->i_file_acl); error = -EIO; @@ -784,8 +794,7 @@ ext2_xattr_delete_inode(struct inode *inode) goto cleanup; } ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count))); - if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) || - HDR(bh)->h_blocks != cpu_to_le32(1)) { + if (!ext2_xattr_header_valid(HDR(bh))) { ext2_error(inode->i_sb, "ext2_xattr_delete_inode", "inode %ld: bad block %d", inode->i_ino, EXT2_I(inode)->i_file_acl); -- cgit v1.2.3-59-g8ed1b From f4c3fb8c433f1818da46b720754a5d4a8525fdf0 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 14 May 2019 06:40:42 +0800 Subject: ext2: introduce helper for xattr entry validation Introduce helper function ext2_xattr_entry_valid() for xattr entry validation and clean up the entry check related code. Reviewed-by: Andreas Dilger Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index db27260d6a5b..fb2e008d4406 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -144,6 +144,22 @@ ext2_xattr_header_valid(struct ext2_xattr_header *header) return true; } +static bool +ext2_xattr_entry_valid(struct ext2_xattr_entry *entry, size_t end_offs) +{ + size_t size; + + if (entry->e_value_block != 0) + return false; + + size = le32_to_cpu(entry->e_value_size); + if (size > end_offs || + le16_to_cpu(entry->e_value_offs) + size > end_offs) + return false; + + return true; +} + /* * ext2_xattr_get() * @@ -213,14 +229,10 @@ bad_block: error = -ENODATA; goto cleanup; found: - /* check the buffer size */ - if (entry->e_value_block != 0) - goto bad_block; - size = le32_to_cpu(entry->e_value_size); - if (size > inode->i_sb->s_blocksize || - le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize) + if (!ext2_xattr_entry_valid(entry, inode->i_sb->s_blocksize)) goto bad_block; + size = le32_to_cpu(entry->e_value_size); if (ext2_xattr_cache_insert(ea_block_cache, bh)) ea_idebug(inode, "cache insert failed"); if (buffer) { @@ -481,12 +493,10 @@ bad_block: if (flags & XATTR_CREATE) goto cleanup; if (!here->e_value_block && here->e_value_size) { - size_t size = le32_to_cpu(here->e_value_size); - - if (le16_to_cpu(here->e_value_offs) + size > - sb->s_blocksize || size > sb->s_blocksize) + if (!ext2_xattr_entry_valid(here, sb->s_blocksize)) goto bad_block; - free += EXT2_XATTR_SIZE(size); + free += EXT2_XATTR_SIZE( + le32_to_cpu(here->e_value_size)); } free += EXT2_XATTR_LEN(name_len); } -- cgit v1.2.3-59-g8ed1b From 8cd0f2ba787a1a1758dbe063fe6a26ceccd37c24 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 15 May 2019 15:33:32 +0200 Subject: ext2: Merge loops in ext2_xattr_set() There are two very similar loops when searching xattr to set. Just merge them. Reviewed-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index fb2e008d4406..f9fda6d16d78 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -436,28 +436,12 @@ bad_block: error = -EIO; goto cleanup; } - /* Find the named attribute. */ - here = FIRST_ENTRY(bh); - while (!IS_LAST_ENTRY(here)) { - struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here); - if ((char *)next >= end) - goto bad_block; - if (!here->e_value_block && here->e_value_size) { - size_t offs = le16_to_cpu(here->e_value_offs); - if (offs < min_offs) - min_offs = offs; - } - not_found = name_index - here->e_name_index; - if (!not_found) - not_found = name_len - here->e_name_len; - if (!not_found) - not_found = memcmp(name, here->e_name,name_len); - if (not_found <= 0) - break; - here = next; - } - last = here; - /* We still need to compute min_offs and last. */ + /* + * Find the named attribute. If not found, 'here' will point + * to entry where the new attribute should be inserted to + * maintain sorting. + */ + last = FIRST_ENTRY(bh); while (!IS_LAST_ENTRY(last)) { struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last); if ((char *)next >= end) @@ -467,8 +451,21 @@ bad_block: if (offs < min_offs) min_offs = offs; } + if (not_found > 0) { + not_found = name_index - last->e_name_index; + if (!not_found) + not_found = name_len - last->e_name_len; + if (!not_found) { + not_found = memcmp(name, last->e_name, + name_len); + } + if (not_found <= 0) + here = last; + } last = next; } + if (not_found > 0) + here = last; /* Check whether we have enough space left. */ free = min_offs - ((char*)last - (char*)header) - sizeof(__u32); -- cgit v1.2.3-59-g8ed1b From 6c71b489ecfd927966571348f4e4135169a9fbd5 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 15 May 2019 15:39:42 +0200 Subject: ext2: Strengthen xattr block checks Check every entry in xattr block for validity in ext2_xattr_set() to detect on disk corruption early. Also since e_value_block field in xattr entry is never != 0 in a valid filesystem, just remove checks for it once we have established entries are valid. Reviewed-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index f9fda6d16d78..d21dbf297b74 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -218,6 +218,8 @@ bad_block: EXT2_XATTR_NEXT(entry); if ((char *)next >= end) goto bad_block; + if (!ext2_xattr_entry_valid(entry, inode->i_sb->s_blocksize)) + goto bad_block; if (name_index == entry->e_name_index && name_len == entry->e_name_len && memcmp(name, entry->e_name, name_len) == 0) @@ -229,9 +231,6 @@ bad_block: error = -ENODATA; goto cleanup; found: - if (!ext2_xattr_entry_valid(entry, inode->i_sb->s_blocksize)) - goto bad_block; - size = le32_to_cpu(entry->e_value_size); if (ext2_xattr_cache_insert(ea_block_cache, bh)) ea_idebug(inode, "cache insert failed"); @@ -304,6 +303,8 @@ bad_block: if ((char *)next >= end) goto bad_block; + if (!ext2_xattr_entry_valid(entry, inode->i_sb->s_blocksize)) + goto bad_block; entry = next; } if (ext2_xattr_cache_insert(ea_block_cache, bh)) @@ -446,7 +447,9 @@ bad_block: struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last); if ((char *)next >= end) goto bad_block; - if (!last->e_value_block && last->e_value_size) { + if (!ext2_xattr_entry_valid(last, sb->s_blocksize)) + goto bad_block; + if (last->e_value_size) { size_t offs = le16_to_cpu(last->e_value_offs); if (offs < min_offs) min_offs = offs; @@ -489,12 +492,7 @@ bad_block: error = -EEXIST; if (flags & XATTR_CREATE) goto cleanup; - if (!here->e_value_block && here->e_value_size) { - if (!ext2_xattr_entry_valid(here, sb->s_blocksize)) - goto bad_block; - free += EXT2_XATTR_SIZE( - le32_to_cpu(here->e_value_size)); - } + free += EXT2_XATTR_SIZE(le32_to_cpu(here->e_value_size)); free += EXT2_XATTR_LEN(name_len); } error = -ENOSPC; @@ -559,7 +557,7 @@ bad_block: here->e_name_len = name_len; memcpy(here->e_name, name, name_len); } else { - if (!here->e_value_block && here->e_value_size) { + if (here->e_value_size) { char *first_val = (char *)header + min_offs; size_t offs = le16_to_cpu(here->e_value_offs); char *val = (char *)header + offs; @@ -586,7 +584,7 @@ bad_block: last = ENTRY(header+1); while (!IS_LAST_ENTRY(last)) { size_t o = le16_to_cpu(last->e_value_offs); - if (!last->e_value_block && o < offs) + if (o < offs) last->e_value_offs = cpu_to_le16(o + size); last = EXT2_XATTR_NEXT(last); -- cgit v1.2.3-59-g8ed1b From 38fa0e8e4a3b932791443db033ca7c4ed1bace0e Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Mon, 20 May 2019 13:45:03 +0800 Subject: ext2: code cleanup by using test_opt() and clear_opt() Using test_opt() and clear_opt() instead of directly comparing flag bit of mount option. Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/super.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 3988633789cb..ca7229c38fce 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -302,16 +302,16 @@ static int ext2_show_options(struct seq_file *seq, struct dentry *root) if (test_opt(sb, NOBH)) seq_puts(seq, ",nobh"); - if (sbi->s_mount_opt & EXT2_MOUNT_USRQUOTA) + if (test_opt(sb, USRQUOTA)) seq_puts(seq, ",usrquota"); - if (sbi->s_mount_opt & EXT2_MOUNT_GRPQUOTA) + if (test_opt(sb, GRPQUOTA)) seq_puts(seq, ",grpquota"); - if (sbi->s_mount_opt & EXT2_MOUNT_XIP) + if (test_opt(sb, XIP)) seq_puts(seq, ",xip"); - if (sbi->s_mount_opt & EXT2_MOUNT_DAX) + if (test_opt(sb, DAX)) seq_puts(seq, ",dax"); if (!test_opt(sb, RESERVATION)) @@ -934,8 +934,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) sbi->s_resgid = opts.s_resgid; sb->s_flags = (sb->s_flags & ~SB_POSIXACL) | - ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? - SB_POSIXACL : 0); + (test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0); sb->s_iflags |= SB_I_CGROUPWB; if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && @@ -966,11 +965,11 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size); - if (sbi->s_mount_opt & EXT2_MOUNT_DAX) { + if (test_opt(sb, DAX)) { if (!bdev_dax_supported(sb->s_bdev, blocksize)) { ext2_msg(sb, KERN_ERR, "DAX unsupported by block device. Turning off DAX."); - sbi->s_mount_opt &= ~EXT2_MOUNT_DAX; + clear_opt(sbi->s_mount_opt, DAX); } } @@ -1403,7 +1402,7 @@ out_set: sbi->s_resuid = new_opts.s_resuid; sbi->s_resgid = new_opts.s_resgid; sb->s_flags = (sb->s_flags & ~SB_POSIXACL) | - ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? SB_POSIXACL : 0); + (test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0); spin_unlock(&sbi->s_lock); return 0; -- cgit v1.2.3-59-g8ed1b From 7f58351a7c57d2a7f15c89683b7a1d2d4fb38066 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 28 May 2019 13:32:31 +0800 Subject: ext2: code cleanup for ext2_preread_inode() Calling bdi_rw_congested() instead of calling bdi_read_congested() and bdi_write_congested(). Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/ialloc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index a0c5ea91fcd4..334dea4e499d 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -172,9 +172,7 @@ static void ext2_preread_inode(struct inode *inode) struct backing_dev_info *bdi; bdi = inode_to_bdi(inode); - if (bdi_read_congested(bdi)) - return; - if (bdi_write_congested(bdi)) + if (bdi_rw_congested(bdi)) return; block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb); -- cgit v1.2.3-59-g8ed1b From 9bb1d7a6bcde2e45ecb29967b77e6716e155c252 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 28 May 2019 10:59:45 +0800 Subject: ext2: merge xattr next entry check to ext2_xattr_entry_valid() We have introduced ext2_xattr_entry_valid() for xattr entry sanity check, so it's better to do relevant things in one place. Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index d21dbf297b74..28503979696d 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -145,10 +145,16 @@ ext2_xattr_header_valid(struct ext2_xattr_header *header) } static bool -ext2_xattr_entry_valid(struct ext2_xattr_entry *entry, size_t end_offs) +ext2_xattr_entry_valid(struct ext2_xattr_entry *entry, + char *end, size_t end_offs) { + struct ext2_xattr_entry *next; size_t size; + next = EXT2_XATTR_NEXT(entry); + if ((char *)next >= end) + return false; + if (entry->e_value_block != 0) return false; @@ -214,17 +220,14 @@ bad_block: /* find named attribute */ entry = FIRST_ENTRY(bh); while (!IS_LAST_ENTRY(entry)) { - struct ext2_xattr_entry *next = - EXT2_XATTR_NEXT(entry); - if ((char *)next >= end) - goto bad_block; - if (!ext2_xattr_entry_valid(entry, inode->i_sb->s_blocksize)) + if (!ext2_xattr_entry_valid(entry, end, + inode->i_sb->s_blocksize)) goto bad_block; if (name_index == entry->e_name_index && name_len == entry->e_name_len && memcmp(name, entry->e_name, name_len) == 0) goto found; - entry = next; + entry = EXT2_XATTR_NEXT(entry); } if (ext2_xattr_cache_insert(ea_block_cache, bh)) ea_idebug(inode, "cache insert failed"); @@ -299,13 +302,10 @@ bad_block: /* check the on-disk data structure */ entry = FIRST_ENTRY(bh); while (!IS_LAST_ENTRY(entry)) { - struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry); - - if ((char *)next >= end) - goto bad_block; - if (!ext2_xattr_entry_valid(entry, inode->i_sb->s_blocksize)) + if (!ext2_xattr_entry_valid(entry, end, + inode->i_sb->s_blocksize)) goto bad_block; - entry = next; + entry = EXT2_XATTR_NEXT(entry); } if (ext2_xattr_cache_insert(ea_block_cache, bh)) ea_idebug(inode, "cache insert failed"); @@ -390,7 +390,7 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name, struct super_block *sb = inode->i_sb; struct buffer_head *bh = NULL; struct ext2_xattr_header *header = NULL; - struct ext2_xattr_entry *here, *last; + struct ext2_xattr_entry *here = NULL, *last = NULL; size_t name_len, free, min_offs = sb->s_blocksize; int not_found = 1, error; char *end; @@ -444,10 +444,7 @@ bad_block: */ last = FIRST_ENTRY(bh); while (!IS_LAST_ENTRY(last)) { - struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last); - if ((char *)next >= end) - goto bad_block; - if (!ext2_xattr_entry_valid(last, sb->s_blocksize)) + if (!ext2_xattr_entry_valid(last, end, sb->s_blocksize)) goto bad_block; if (last->e_value_size) { size_t offs = le16_to_cpu(last->e_value_offs); @@ -465,7 +462,7 @@ bad_block: if (not_found <= 0) here = last; } - last = next; + last = EXT2_XATTR_NEXT(last); } if (not_found > 0) here = last; @@ -476,7 +473,6 @@ bad_block: /* We will use a new extended attribute block. */ free = sb->s_blocksize - sizeof(struct ext2_xattr_header) - sizeof(__u32); - here = last = NULL; /* avoid gcc uninitialized warning. */ } if (not_found) { -- cgit v1.2.3-59-g8ed1b From d561d4dd4f560e4d97bf665c5eeab7ac9fe8aca3 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 28 May 2019 10:59:46 +0800 Subject: ext2: introduce new helper for xattr entry comparison Introduce new helper ext2_xattr_cmp_entry() for xattr entry comparison. Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 28503979696d..59356cd2a842 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -166,6 +166,21 @@ ext2_xattr_entry_valid(struct ext2_xattr_entry *entry, return true; } +static int +ext2_xattr_cmp_entry(int name_index, size_t name_len, const char *name, + struct ext2_xattr_entry *entry) +{ + int cmp; + + cmp = name_index - entry->e_name_index; + if (!cmp) + cmp = name_len - entry->e_name_len; + if (!cmp) + cmp = memcmp(name, entry->e_name, name_len); + + return cmp; +} + /* * ext2_xattr_get() * @@ -452,13 +467,9 @@ bad_block: min_offs = offs; } if (not_found > 0) { - not_found = name_index - last->e_name_index; - if (!not_found) - not_found = name_len - last->e_name_len; - if (!not_found) { - not_found = memcmp(name, last->e_name, - name_len); - } + not_found = ext2_xattr_cmp_entry(name_index, + name_len, + name, last); if (not_found <= 0) here = last; } -- cgit v1.2.3-59-g8ed1b From 1eaf5faab18cd55a3658e0c7517654325f59a793 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Tue, 28 May 2019 10:59:47 +0800 Subject: ext2: optimize ext2_xattr_get() Since xattr entry names are sorted, we don't have to continue when current entry name is greater than target. Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 59356cd2a842..839e71e78673 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -199,7 +199,7 @@ ext2_xattr_get(struct inode *inode, int name_index, const char *name, struct ext2_xattr_entry *entry; size_t name_len, size; char *end; - int error; + int error, not_found; struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode); ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld", @@ -238,10 +238,14 @@ bad_block: if (!ext2_xattr_entry_valid(entry, end, inode->i_sb->s_blocksize)) goto bad_block; - if (name_index == entry->e_name_index && - name_len == entry->e_name_len && - memcmp(name, entry->e_name, name_len) == 0) + + not_found = ext2_xattr_cmp_entry(name_index, name_len, name, + entry); + if (!not_found) goto found; + if (not_found < 0) + break; + entry = EXT2_XATTR_NEXT(entry); } if (ext2_xattr_cache_insert(ea_block_cache, bh)) -- cgit v1.2.3-59-g8ed1b From dc1f73802bd76bea60b9c93eb24a1d26472d2723 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Thu, 30 May 2019 18:10:42 +0800 Subject: ext2: add missing brelse() in ext2_new_inode() There is a missing brelse of bitmap_bh in an error path of ext2_new_inode(). Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/ialloc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 334dea4e499d..fda7d3f5b4be 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -509,6 +509,7 @@ repeat_in_this_group: /* * Scanned all blockgroups. */ + brelse(bitmap_bh); err = -ENOSPC; goto fail; got: -- cgit v1.2.3-59-g8ed1b From 1fe03415447ba06f17fb6446d4bb01d1ba551be8 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Wed, 12 Jun 2019 12:57:53 +0800 Subject: ext2: fix a typo in comment Just fix a typo in comment and remove redundant blank line in ext2_data_block_valid(). Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 33db13365c5e..547c165299c0 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -1197,7 +1197,7 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi) /* * Returns 1 if the passed-in block region is valid; 0 if some part overlaps - * with filesystem metadata blocksi. + * with filesystem metadata blocks. */ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, unsigned int count) @@ -1212,7 +1212,6 @@ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk, (start_blk + count >= sbi->s_sb_block)) return 0; - return 1; } -- cgit v1.2.3-59-g8ed1b From fdbd3e8c9ffdf9c75a1cfc5951abcf141d68f8d9 Mon Sep 17 00:00:00 2001 From: Fumiya Shigemitsu Date: Tue, 11 Jun 2019 21:33:40 +0900 Subject: ext2: Fix a typo in ext2_getattr argument Fix a typo in a ext2_getattr argument Signed-off-by: Fumiya Shigemitsu Signed-off-by: Jan Kara --- fs/ext2/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index e474127dd255..8d91f5dab0dc 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1640,7 +1640,7 @@ int ext2_write_inode(struct inode *inode, struct writeback_control *wbc) } int ext2_getattr(const struct path *path, struct kstat *stat, - u32 request_mask, unsigned int query_falgs) + u32 request_mask, unsigned int query_flags) { struct inode *inode = d_inode(path->dentry); struct ext2_inode_info *ei = EXT2_I(inode); -- cgit v1.2.3-59-g8ed1b From edb895d3bfbab558df2149947a3f245ddf42292e Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Sun, 16 Jun 2019 23:08:01 +0800 Subject: ext2: add missing brelse() in ext2_iget() Add missing brelse() on error path of ext2_iget(). Reviewed-by: Darrick J. Wong Signed-off-by: Chengguang Xu Signed-off-by: Jan Kara --- fs/ext2/inode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 8d91f5dab0dc..e680478866db 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1473,6 +1473,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) else ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); if (i_size_read(inode) < 0) { + brelse(bh); ret = -EFSCORRUPTED; goto bad_inode; } -- cgit v1.2.3-59-g8ed1b From 936bbf3aea84696ce1081ab9648d08bbf08ca7aa Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 19 Jun 2019 18:29:45 +0200 Subject: ext2: Always brelse bh on failure in ext2_iget() All but one bail out paths in ext2_iget() is releasing bh. Move the releasing of bh into a common error handling code. Signed-off-by: Jan Kara --- fs/ext2/inode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index e680478866db..7004ce581a32 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1400,7 +1400,7 @@ void ext2_set_file_ops(struct inode *inode) struct inode *ext2_iget (struct super_block *sb, unsigned long ino) { struct ext2_inode_info *ei; - struct buffer_head * bh; + struct buffer_head * bh = NULL; struct ext2_inode *raw_inode; struct inode *inode; long ret = -EIO; @@ -1446,7 +1446,6 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) */ if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) { /* this inode is deleted */ - brelse (bh); ret = -ESTALE; goto bad_inode; } @@ -1463,7 +1462,6 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) !ext2_data_block_valid(EXT2_SB(sb), ei->i_file_acl, 1)) { ext2_error(sb, "ext2_iget", "bad extended attribute block %u", ei->i_file_acl); - brelse(bh); ret = -EFSCORRUPTED; goto bad_inode; } @@ -1473,7 +1471,6 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) else ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl); if (i_size_read(inode) < 0) { - brelse(bh); ret = -EFSCORRUPTED; goto bad_inode; } @@ -1527,6 +1524,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino) return inode; bad_inode: + brelse(bh); iget_failed(inode); return ERR_PTR(ret); } -- cgit v1.2.3-59-g8ed1b From 555b2c3da1fc3d1c5cc133e9353354c40fbfe336 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 21 Jun 2019 18:27:13 -0500 Subject: quota: honor quota type in Q_XGETQSTAT[V] calls The code in quota_getstate and quota_getstatev is strange; it says the returned fs_quota_stat[v] structure has room for only one type of time limits, so fills it in with the first enabled quota, even though every quotactl command must have a type sent in by the user. Instead of just picking the first enabled quota, fill in the reply with the timers for the quota type that was actually requested. Reviewed-by: Christoph Hellwig Signed-off-by: Eric Sandeen Signed-off-by: Jan Kara --- fs/quota/quota.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index fd5dd806f1b9..cb13fb76dbee 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -331,9 +331,9 @@ static int quota_state_to_flags(struct qc_state *state) return flags; } -static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs) +static int quota_getstate(struct super_block *sb, int type, + struct fs_quota_stat *fqs) { - int type; struct qc_state state; int ret; @@ -349,14 +349,7 @@ static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs) if (!fqs->qs_flags) return -ENOSYS; fqs->qs_incoredqs = state.s_incoredqs; - /* - * GETXSTATE quotactl has space for just one set of time limits so - * report them for the first enabled quota type - */ - for (type = 0; type < MAXQUOTAS; type++) - if (state.s_state[type].flags & QCI_ACCT_ENABLED) - break; - BUG_ON(type == MAXQUOTAS); + fqs->qs_btimelimit = state.s_state[type].spc_timelimit; fqs->qs_itimelimit = state.s_state[type].ino_timelimit; fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit; @@ -391,22 +384,22 @@ static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs) return 0; } -static int quota_getxstate(struct super_block *sb, void __user *addr) +static int quota_getxstate(struct super_block *sb, int type, void __user *addr) { struct fs_quota_stat fqs; int ret; if (!sb->s_qcop->get_state) return -ENOSYS; - ret = quota_getstate(sb, &fqs); + ret = quota_getstate(sb, type, &fqs); if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return ret; } -static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs) +static int quota_getstatev(struct super_block *sb, int type, + struct fs_quota_statv *fqs) { - int type; struct qc_state state; int ret; @@ -422,14 +415,7 @@ static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs) if (!fqs->qs_flags) return -ENOSYS; fqs->qs_incoredqs = state.s_incoredqs; - /* - * GETXSTATV quotactl has space for just one set of time limits so - * report them for the first enabled quota type - */ - for (type = 0; type < MAXQUOTAS; type++) - if (state.s_state[type].flags & QCI_ACCT_ENABLED) - break; - BUG_ON(type == MAXQUOTAS); + fqs->qs_btimelimit = state.s_state[type].spc_timelimit; fqs->qs_itimelimit = state.s_state[type].ino_timelimit; fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit; @@ -455,7 +441,7 @@ static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs) return 0; } -static int quota_getxstatev(struct super_block *sb, void __user *addr) +static int quota_getxstatev(struct super_block *sb, int type, void __user *addr) { struct fs_quota_statv fqs; int ret; @@ -474,7 +460,7 @@ static int quota_getxstatev(struct super_block *sb, void __user *addr) default: return -EINVAL; } - ret = quota_getstatev(sb, &fqs); + ret = quota_getstatev(sb, type, &fqs); if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return ret; @@ -744,9 +730,9 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, case Q_XQUOTARM: return quota_rmxquota(sb, addr); case Q_XGETQSTAT: - return quota_getxstate(sb, addr); + return quota_getxstate(sb, type, addr); case Q_XGETQSTATV: - return quota_getxstatev(sb, addr); + return quota_getxstatev(sb, type, addr); case Q_XSETQLIM: return quota_setxquota(sb, type, id, addr); case Q_XGETQUOTA: -- cgit v1.2.3-59-g8ed1b From 90f15ac9fa7e7ff66e0980309d8184bff0611624 Mon Sep 17 00:00:00 2001 From: Fuqian Huang Date: Wed, 3 Jul 2019 21:17:27 +0800 Subject: ext2: Use kmemdup rather than duplicating its implementation kmemdup is introduced to duplicate a region of memory in a neat way. Rather than kmalloc/kzalloc + memset, which the programmer needs to write the size twice (sometimes lead to mistakes), kmemdup improves readability, leads to smaller code and also reduce the chances of mistakes. Suggestion to use kmemdup rather than using kmalloc/kzalloc + memset. Signed-off-by: Fuqian Huang Link: https://lore.kernel.org/r/20190703131727.25735-1-huangfq.daxian@gmail.com Signed-off-by: Jan Kara --- fs/ext2/xattr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 839e71e78673..79369c13cc55 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -532,11 +532,10 @@ bad_block: unlock_buffer(bh); ea_bdebug(bh, "cloning"); - header = kmalloc(bh->b_size, GFP_KERNEL); + header = kmemdup(HDR(bh), bh->b_size, GFP_KERNEL); error = -ENOMEM; if (header == NULL) goto cleanup; - memcpy(header, HDR(bh), bh->b_size); header->h_refcount = cpu_to_le32(1); offset = (char *)here - bh->b_data; -- cgit v1.2.3-59-g8ed1b From fa33cdbf3eceb0206a4f844fe91aeebcf6ff2b7a Mon Sep 17 00:00:00 2001 From: "Steven J. Magnani" Date: Sun, 30 Jun 2019 21:39:35 -0500 Subject: udf: Fix incorrect final NOT_ALLOCATED (hole) extent length In some cases, using the 'truncate' command to extend a UDF file results in a mismatch between the length of the file's extents (specifically, due to incorrect length of the final NOT_ALLOCATED extent) and the information (file) length. The discrepancy can prevent other operating systems (i.e., Windows 10) from opening the file. Two particular errors have been observed when extending a file: 1. The final extent is larger than it should be, having been rounded up to a multiple of the block size. B. The final extent is not shorter than it should be, due to not having been updated when the file's information length was increased. [JK: simplified udf_do_extend_final_block(), fixed up some types] Fixes: 2c948b3f86e5 ("udf: Avoid IO in udf_clear_inode") CC: stable@vger.kernel.org Signed-off-by: Steven J. Magnani Link: https://lore.kernel.org/r/1561948775-5878-1-git-send-email-steve@digidescorp.com Signed-off-by: Jan Kara --- fs/udf/inode.c | 93 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 33 deletions(-) (limited to 'fs') diff --git a/fs/udf/inode.c b/fs/udf/inode.c index e7276932e433..9bb18311a22f 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -470,13 +470,15 @@ static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block, return NULL; } -/* Extend the file by 'blocks' blocks, return the number of extents added */ +/* Extend the file with new blocks totaling 'new_block_bytes', + * return the number of extents added + */ static int udf_do_extend_file(struct inode *inode, struct extent_position *last_pos, struct kernel_long_ad *last_ext, - sector_t blocks) + loff_t new_block_bytes) { - sector_t add; + uint32_t add; int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK); struct super_block *sb = inode->i_sb; struct kernel_lb_addr prealloc_loc = {}; @@ -486,7 +488,7 @@ static int udf_do_extend_file(struct inode *inode, /* The previous extent is fake and we should not extend by anything * - there's nothing to do... */ - if (!blocks && fake) + if (!new_block_bytes && fake) return 0; iinfo = UDF_I(inode); @@ -517,13 +519,12 @@ static int udf_do_extend_file(struct inode *inode, /* Can we merge with the previous extent? */ if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) == EXT_NOT_RECORDED_NOT_ALLOCATED) { - add = ((1 << 30) - sb->s_blocksize - - (last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >> - sb->s_blocksize_bits; - if (add > blocks) - add = blocks; - blocks -= add; - last_ext->extLength += add << sb->s_blocksize_bits; + add = (1 << 30) - sb->s_blocksize - + (last_ext->extLength & UDF_EXTENT_LENGTH_MASK); + if (add > new_block_bytes) + add = new_block_bytes; + new_block_bytes -= add; + last_ext->extLength += add; } if (fake) { @@ -544,28 +545,27 @@ static int udf_do_extend_file(struct inode *inode, } /* Managed to do everything necessary? */ - if (!blocks) + if (!new_block_bytes) goto out; /* All further extents will be NOT_RECORDED_NOT_ALLOCATED */ last_ext->extLocation.logicalBlockNum = 0; last_ext->extLocation.partitionReferenceNum = 0; - add = (1 << (30-sb->s_blocksize_bits)) - 1; - last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | - (add << sb->s_blocksize_bits); + add = (1 << 30) - sb->s_blocksize; + last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | add; /* Create enough extents to cover the whole hole */ - while (blocks > add) { - blocks -= add; + while (new_block_bytes > add) { + new_block_bytes -= add; err = udf_add_aext(inode, last_pos, &last_ext->extLocation, last_ext->extLength, 1); if (err) return err; count++; } - if (blocks) { + if (new_block_bytes) { last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | - (blocks << sb->s_blocksize_bits); + new_block_bytes; err = udf_add_aext(inode, last_pos, &last_ext->extLocation, last_ext->extLength, 1); if (err) @@ -596,6 +596,24 @@ out: return count; } +/* Extend the final block of the file to final_block_len bytes */ +static void udf_do_extend_final_block(struct inode *inode, + struct extent_position *last_pos, + struct kernel_long_ad *last_ext, + uint32_t final_block_len) +{ + struct super_block *sb = inode->i_sb; + uint32_t added_bytes; + + added_bytes = final_block_len - + (last_ext->extLength & (sb->s_blocksize - 1)); + last_ext->extLength += added_bytes; + UDF_I(inode)->i_lenExtents += added_bytes; + + udf_write_aext(inode, last_pos, &last_ext->extLocation, + last_ext->extLength, 1); +} + static int udf_extend_file(struct inode *inode, loff_t newsize) { @@ -605,10 +623,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) int8_t etype; struct super_block *sb = inode->i_sb; sector_t first_block = newsize >> sb->s_blocksize_bits, offset; + unsigned long partial_final_block; int adsize; struct udf_inode_info *iinfo = UDF_I(inode); struct kernel_long_ad extent; - int err; + int err = 0; + int within_final_block; if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT) adsize = sizeof(struct short_ad); @@ -618,18 +638,8 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) BUG(); etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset); + within_final_block = (etype != -1); - /* File has extent covering the new size (could happen when extending - * inside a block)? */ - if (etype != -1) - return 0; - if (newsize & (sb->s_blocksize - 1)) - offset++; - /* Extended file just to the boundary of the last file block? */ - if (offset == 0) - return 0; - - /* Truncate is extending the file by 'offset' blocks */ if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) || (epos.bh && epos.offset == sizeof(struct allocExtDesc))) { /* File has no extents at all or has empty last @@ -643,7 +653,22 @@ static int udf_extend_file(struct inode *inode, loff_t newsize) &extent.extLength, 0); extent.extLength |= etype << 30; } - err = udf_do_extend_file(inode, &epos, &extent, offset); + + partial_final_block = newsize & (sb->s_blocksize - 1); + + /* File has extent covering the new size (could happen when extending + * inside a block)? + */ + if (within_final_block) { + /* Extending file within the last file block */ + udf_do_extend_final_block(inode, &epos, &extent, + partial_final_block); + } else { + loff_t add = ((loff_t)offset << sb->s_blocksize_bits) | + partial_final_block; + err = udf_do_extend_file(inode, &epos, &extent, add); + } + if (err < 0) goto out; err = 0; @@ -745,6 +770,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, /* Are we beyond EOF? */ if (etype == -1) { int ret; + loff_t hole_len; isBeyondEOF = true; if (count) { if (c) @@ -760,7 +786,8 @@ static sector_t inode_getblk(struct inode *inode, sector_t block, startnum = (offset > 0); } /* Create extents for the hole between EOF and offset */ - ret = udf_do_extend_file(inode, &prev_epos, laarr, offset); + hole_len = (loff_t)offset << inode->i_blkbits; + ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len); if (ret < 0) { *err = ret; newblock = 0; -- cgit v1.2.3-59-g8ed1b