diff options
author | Lukas Czerner <lczerner@redhat.com> | 2012-03-02 07:09:05 -0500 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2012-03-02 11:47:40 -0500 |
commit | e703c206135acb458adb705ec44bcc5d2615b37d (patch) | |
tree | ac569403ce4e4c1da33307e862c009810c56c492 /fs/ext3 | |
parent | a0391a3ae91d301c0e59368531a4de5f0b122bcf (diff) |
ext3: fix start and len arguments handling in ext3_trim_fs()
The overflow might happen when passing blocknr into
ext3_get_group_no_and_offset(), because it expects type ext3_fsblk_t
which might be smaller than uint64_t. This will most likely happen when
calling FITRIM with the default argument len = ULLONG_MAX.
Fix this by using "end" variable instead of "start+len" as it is easier
to get right and specifically check that the end is not beyond the end
of the file system, so we are sure that the result of
get_group_no_and_offset() will not overflow. Otherwise truncate it to
the size of the file system.
Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Cc: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ext3')
-rw-r--r-- | fs/ext3/balloc.c | 77 |
1 files changed, 39 insertions, 38 deletions
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 954a7d355f59..1e036b79384c 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c | |||
@@ -1973,7 +1973,7 @@ static ext3_grpblk_t ext3_trim_all_free(struct super_block *sb, | |||
1973 | sbi = EXT3_SB(sb); | 1973 | sbi = EXT3_SB(sb); |
1974 | 1974 | ||
1975 | /* Walk through the whole group */ | 1975 | /* Walk through the whole group */ |
1976 | while (start < max) { | 1976 | while (start <= max) { |
1977 | start = bitmap_search_next_usable_block(start, bitmap_bh, max); | 1977 | start = bitmap_search_next_usable_block(start, bitmap_bh, max); |
1978 | if (start < 0) | 1978 | if (start < 0) |
1979 | break; | 1979 | break; |
@@ -1983,7 +1983,7 @@ static ext3_grpblk_t ext3_trim_all_free(struct super_block *sb, | |||
1983 | * Allocate contiguous free extents by setting bits in the | 1983 | * Allocate contiguous free extents by setting bits in the |
1984 | * block bitmap | 1984 | * block bitmap |
1985 | */ | 1985 | */ |
1986 | while (next < max | 1986 | while (next <= max |
1987 | && claim_block(sb_bgl_lock(sbi, group), | 1987 | && claim_block(sb_bgl_lock(sbi, group), |
1988 | next, bitmap_bh)) { | 1988 | next, bitmap_bh)) { |
1989 | next++; | 1989 | next++; |
@@ -2094,73 +2094,74 @@ err_out: | |||
2094 | */ | 2094 | */ |
2095 | int ext3_trim_fs(struct super_block *sb, struct fstrim_range *range) | 2095 | int ext3_trim_fs(struct super_block *sb, struct fstrim_range *range) |
2096 | { | 2096 | { |
2097 | ext3_grpblk_t last_block, first_block, free_blocks; | 2097 | ext3_grpblk_t last_block, first_block; |
2098 | unsigned long first_group, last_group; | 2098 | unsigned long group, first_group, last_group; |
2099 | unsigned long group, ngroups; | ||
2100 | struct ext3_group_desc *gdp; | 2099 | struct ext3_group_desc *gdp; |
2101 | struct ext3_super_block *es = EXT3_SB(sb)->s_es; | 2100 | struct ext3_super_block *es = EXT3_SB(sb)->s_es; |
2102 | uint64_t start, len, minlen, trimmed; | 2101 | uint64_t start, minlen, end, trimmed = 0; |
2102 | ext3_fsblk_t first_data_blk = | ||
2103 | le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block); | ||
2103 | ext3_fsblk_t max_blks = le32_to_cpu(es->s_blocks_count); | 2104 | ext3_fsblk_t max_blks = le32_to_cpu(es->s_blocks_count); |
2104 | int ret = 0; | 2105 | int ret = 0; |
2105 | 2106 | ||
2106 | start = (range->start >> sb->s_blocksize_bits) + | 2107 | start = range->start >> sb->s_blocksize_bits; |
2107 | le32_to_cpu(es->s_first_data_block); | 2108 | end = start + (range->len >> sb->s_blocksize_bits) - 1; |
2108 | len = range->len >> sb->s_blocksize_bits; | ||
2109 | minlen = range->minlen >> sb->s_blocksize_bits; | 2109 | minlen = range->minlen >> sb->s_blocksize_bits; |
2110 | trimmed = 0; | ||
2111 | 2110 | ||
2112 | if (unlikely(minlen > EXT3_BLOCKS_PER_GROUP(sb))) | 2111 | if (unlikely(minlen > EXT3_BLOCKS_PER_GROUP(sb)) || |
2112 | unlikely(start >= max_blks)) | ||
2113 | return -EINVAL; | 2113 | return -EINVAL; |
2114 | if (start >= max_blks) | 2114 | if (end >= max_blks) |
2115 | return -EINVAL; | 2115 | end = max_blks - 1; |
2116 | if (start + len > max_blks) | 2116 | if (end <= first_data_blk) |
2117 | len = max_blks - start; | 2117 | goto out; |
2118 | if (start < first_data_blk) | ||
2119 | start = first_data_blk; | ||
2118 | 2120 | ||
2119 | ngroups = EXT3_SB(sb)->s_groups_count; | ||
2120 | smp_rmb(); | 2121 | smp_rmb(); |
2121 | 2122 | ||
2122 | /* Determine first and last group to examine based on start and len */ | 2123 | /* Determine first and last group to examine based on start and len */ |
2123 | ext3_get_group_no_and_offset(sb, (ext3_fsblk_t) start, | 2124 | ext3_get_group_no_and_offset(sb, (ext3_fsblk_t) start, |
2124 | &first_group, &first_block); | 2125 | &first_group, &first_block); |
2125 | ext3_get_group_no_and_offset(sb, (ext3_fsblk_t) (start + len), | 2126 | ext3_get_group_no_and_offset(sb, (ext3_fsblk_t) end, |
2126 | &last_group, &last_block); | 2127 | &last_group, &last_block); |
2127 | last_group = (last_group > ngroups - 1) ? ngroups - 1 : last_group; | ||
2128 | last_block = EXT3_BLOCKS_PER_GROUP(sb); | ||
2129 | 2128 | ||
2130 | if (first_group > last_group) | 2129 | /* end now represents the last block to discard in this group */ |
2131 | return -EINVAL; | 2130 | end = EXT3_BLOCKS_PER_GROUP(sb) - 1; |
2132 | 2131 | ||
2133 | for (group = first_group; group <= last_group; group++) { | 2132 | for (group = first_group; group <= last_group; group++) { |
2134 | gdp = ext3_get_group_desc(sb, group, NULL); | 2133 | gdp = ext3_get_group_desc(sb, group, NULL); |
2135 | if (!gdp) | 2134 | if (!gdp) |
2136 | break; | 2135 | break; |
2137 | 2136 | ||
2138 | free_blocks = le16_to_cpu(gdp->bg_free_blocks_count); | ||
2139 | if (free_blocks < minlen) | ||
2140 | continue; | ||
2141 | |||
2142 | /* | 2137 | /* |
2143 | * For all the groups except the last one, last block will | 2138 | * For all the groups except the last one, last block will |
2144 | * always be EXT3_BLOCKS_PER_GROUP(sb), so we only need to | 2139 | * always be EXT3_BLOCKS_PER_GROUP(sb)-1, so we only need to |
2145 | * change it for the last group in which case first_block + | 2140 | * change it for the last group, note that last_block is |
2146 | * len < EXT3_BLOCKS_PER_GROUP(sb). | 2141 | * already computed earlier by ext3_get_group_no_and_offset() |
2147 | */ | 2142 | */ |
2148 | if (first_block + len < EXT3_BLOCKS_PER_GROUP(sb)) | 2143 | if (group == last_group) |
2149 | last_block = first_block + len; | 2144 | end = last_block; |
2150 | len -= last_block - first_block; | ||
2151 | 2145 | ||
2152 | ret = ext3_trim_all_free(sb, group, first_block, | 2146 | if (le16_to_cpu(gdp->bg_free_blocks_count) >= minlen) { |
2153 | last_block, minlen); | 2147 | ret = ext3_trim_all_free(sb, group, first_block, |
2154 | if (ret < 0) | 2148 | end, minlen); |
2155 | break; | 2149 | if (ret < 0) |
2150 | break; | ||
2151 | trimmed += ret; | ||
2152 | } | ||
2156 | 2153 | ||
2157 | trimmed += ret; | 2154 | /* |
2155 | * For every group except the first one, we are sure | ||
2156 | * that the first block to discard will be block #0. | ||
2157 | */ | ||
2158 | first_block = 0; | 2158 | first_block = 0; |
2159 | } | 2159 | } |
2160 | 2160 | ||
2161 | if (ret >= 0) | 2161 | if (ret > 0) |
2162 | ret = 0; | 2162 | ret = 0; |
2163 | range->len = trimmed * sb->s_blocksize; | ||
2164 | 2163 | ||
2164 | out: | ||
2165 | range->len = trimmed * sb->s_blocksize; | ||
2165 | return ret; | 2166 | return ret; |
2166 | } | 2167 | } |