Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

udf: fix possible leakage of blocks

We have to take care that when we call udf_discard_prealloc() from
udf_clear_inode() we have to write inode ourselves afterwards (otherwise,
some changes might be lost leading to leakage of blocks, use of free blocks
or improperly aligned extents).

Also udf_discard_prealloc() does two different things - it removes
preallocated blocks and truncates the last extent to exactly match i_size.
We move the latter functionality to udf_truncate_tail_extent(), call
udf_discard_prealloc() when last reference to a file is dropped and call
udf_truncate_tail_extent() when inode is being removed from inode cache
(udf_clear_inode() call).

We cannot call udf_truncate_tail_extent() earlier as subsequent open+write
would find the last block of the file mapped and happily write to the end
of it, although the last extent says it's shorter.

[akpm@linux-foundation.org: Make checkpatch.pl happier]
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: Eric Sandeen <sandeen@sandeen.net>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Jan Kara and committed by
Linus Torvalds
74584ae5 4b356be0

+77 -20
+10 -1
fs/udf/inode.c
··· 100 100 clear_inode(inode); 101 101 } 102 102 103 + /* 104 + * If we are going to release inode from memory, we discard preallocation and 105 + * truncate last inode extent to proper length. We could use drop_inode() but 106 + * it's called under inode_lock and thus we cannot mark inode dirty there. We 107 + * use clear_inode() but we have to make sure to write inode as it's not written 108 + * automatically. 109 + */ 103 110 void udf_clear_inode(struct inode *inode) 104 111 { 105 112 if (!(inode->i_sb->s_flags & MS_RDONLY)) { 106 113 lock_kernel(); 114 + /* Discard preallocation for directories, symlinks, etc. */ 107 115 udf_discard_prealloc(inode); 116 + udf_truncate_tail_extent(inode); 108 117 unlock_kernel(); 118 + write_inode_now(inode, 1); 109 119 } 110 - 111 120 kfree(UDF_I_DATA(inode)); 112 121 UDF_I_DATA(inode) = NULL; 113 122 }
+66 -19
fs/udf/truncate.c
··· 61 61 } 62 62 } 63 63 64 - void udf_discard_prealloc(struct inode * inode) 64 + /* 65 + * Truncate the last extent to match i_size. This function assumes 66 + * that preallocation extent is already truncated. 67 + */ 68 + void udf_truncate_tail_extent(struct inode *inode) 65 69 { 66 70 struct extent_position epos = { NULL, 0, {0, 0}}; 67 71 kernel_lb_addr eloc; 68 72 uint32_t elen, nelen; 73 + uint64_t lbcount = 0; 74 + int8_t etype = -1, netype; 75 + int adsize; 76 + 77 + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB || 78 + inode->i_size == UDF_I_LENEXTENTS(inode)) 79 + return; 80 + /* Are we going to delete the file anyway? */ 81 + if (inode->i_nlink == 0) 82 + return; 83 + 84 + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT) 85 + adsize = sizeof(short_ad); 86 + else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG) 87 + adsize = sizeof(long_ad); 88 + else 89 + BUG(); 90 + 91 + /* Find the last extent in the file */ 92 + while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) 93 + { 94 + etype = netype; 95 + lbcount += elen; 96 + if (lbcount > inode->i_size) { 97 + if (lbcount - inode->i_size >= inode->i_sb->s_blocksize) 98 + printk(KERN_WARNING 99 + "udf_truncate_tail_extent(): Too long " 100 + "extent after EOF in inode %u: i_size: " 101 + "%Ld lbcount: %Ld extent %u+%u\n", 102 + (unsigned)inode->i_ino, 103 + (long long)inode->i_size, 104 + (long long)lbcount, 105 + (unsigned)eloc.logicalBlockNum, 106 + (unsigned)elen); 107 + nelen = elen - (lbcount - inode->i_size); 108 + epos.offset -= adsize; 109 + extent_trunc(inode, &epos, eloc, etype, elen, nelen); 110 + epos.offset += adsize; 111 + if (udf_next_aext(inode, &epos, &eloc, &elen, 1) != -1) 112 + printk(KERN_ERR "udf_truncate_tail_extent(): " 113 + "Extent after EOF in inode %u.\n", 114 + (unsigned)inode->i_ino); 115 + break; 116 + } 117 + } 118 + /* This inode entry is in-memory only and thus we don't have to mark 119 + * the inode dirty */ 120 + UDF_I_LENEXTENTS(inode) = inode->i_size; 121 + brelse(epos.bh); 122 + } 123 + 124 + void udf_discard_prealloc(struct inode *inode) 125 + { 126 + struct extent_position epos = { NULL, 0, {0, 0}}; 127 + kernel_lb_addr eloc; 128 + uint32_t elen; 69 129 uint64_t lbcount = 0; 70 130 int8_t etype = -1, netype; 71 131 int adsize; ··· 144 84 epos.block = UDF_I_LOCATION(inode); 145 85 146 86 /* Find the last extent in the file */ 147 - while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) 148 - { 87 + while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) { 149 88 etype = netype; 150 89 lbcount += elen; 151 - if (lbcount > inode->i_size && lbcount - elen < inode->i_size) 152 - { 153 - WARN_ON(lbcount - inode->i_size >= inode->i_sb->s_blocksize); 154 - nelen = elen - (lbcount - inode->i_size); 155 - epos.offset -= adsize; 156 - extent_trunc(inode, &epos, eloc, etype, elen, nelen); 157 - epos.offset += adsize; 158 - lbcount = inode->i_size; 159 - } 160 90 } 161 91 if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) { 162 92 epos.offset -= adsize; 163 93 lbcount -= elen; 164 94 extent_trunc(inode, &epos, eloc, etype, elen, 0); 165 - if (!epos.bh) 166 - { 95 + if (!epos.bh) { 167 96 UDF_I_LENALLOC(inode) = epos.offset - udf_file_entry_alloc_offset(inode); 168 97 mark_inode_dirty(inode); 169 - } 170 - else 171 - { 98 + } else { 172 99 struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data); 173 100 aed->lengthAllocDescs = cpu_to_le32(epos.offset - sizeof(struct allocExtDesc)); 174 101 if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201) ··· 165 118 mark_buffer_dirty_inode(epos.bh, inode); 166 119 } 167 120 } 121 + /* This inode entry is in-memory only and thus we don't have to mark 122 + * the inode dirty */ 168 123 UDF_I_LENEXTENTS(inode) = lbcount; 169 - 170 - WARN_ON(lbcount != inode->i_size); 171 124 brelse(epos.bh); 172 125 } 173 126
+1
fs/udf/udfdecl.h
··· 146 146 extern struct inode * udf_new_inode (struct inode *, int, int *); 147 147 148 148 /* truncate.c */ 149 + extern void udf_truncate_tail_extent(struct inode *); 149 150 extern void udf_discard_prealloc(struct inode *); 150 151 extern void udf_truncate_extents(struct inode *); 151 152