Commit 74584ae509befc2ed711810e7df4b075473869b2

Authored by Jan Kara
Committed by Linus Torvalds
1 parent 4b356be019

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>

Showing 3 changed files with 74 additions and 17 deletions Side-by-side Diff

... ... @@ -100,14 +100,23 @@
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 }
... ... @@ -61,7 +61,11 @@
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  
69 73  
70 74  
71 75  
72 76  
73 77  
74 78  
75 79  
... ... @@ -71,44 +75,87 @@
71 75 int adsize;
72 76  
73 77 if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ||
74   - inode->i_size == UDF_I_LENEXTENTS(inode))
  78 + inode->i_size == UDF_I_LENEXTENTS(inode))
75 79 return;
  80 + /* Are we going to delete the file anyway? */
  81 + if (inode->i_nlink == 0)
  82 + return;
76 83  
77 84 if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
78 85 adsize = sizeof(short_ad);
79 86 else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
80 87 adsize = sizeof(long_ad);
81 88 else
82   - adsize = 0;
  89 + BUG();
83 90  
84   - epos.block = UDF_I_LOCATION(inode);
85   -
86 91 /* Find the last extent in the file */
87 92 while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1)
88 93 {
89 94 etype = netype;
90 95 lbcount += elen;
91   - if (lbcount > inode->i_size && lbcount - elen < inode->i_size)
92   - {
93   - WARN_ON(lbcount - inode->i_size >= inode->i_sb->s_blocksize);
  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);
94 107 nelen = elen - (lbcount - inode->i_size);
95 108 epos.offset -= adsize;
96 109 extent_trunc(inode, &epos, eloc, etype, elen, nelen);
97 110 epos.offset += adsize;
98   - lbcount = inode->i_size;
  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;
99 116 }
100 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;
  129 + uint64_t lbcount = 0;
  130 + int8_t etype = -1, netype;
  131 + int adsize;
  132 +
  133 + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ||
  134 + inode->i_size == UDF_I_LENEXTENTS(inode))
  135 + return;
  136 +
  137 + if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
  138 + adsize = sizeof(short_ad);
  139 + else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
  140 + adsize = sizeof(long_ad);
  141 + else
  142 + adsize = 0;
  143 +
  144 + epos.block = UDF_I_LOCATION(inode);
  145 +
  146 + /* Find the last extent in the file */
  147 + while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
  148 + etype = netype;
  149 + lbcount += elen;
  150 + }
101 151 if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
102 152 epos.offset -= adsize;
103 153 lbcount -= elen;
104 154 extent_trunc(inode, &epos, eloc, etype, elen, 0);
105   - if (!epos.bh)
106   - {
  155 + if (!epos.bh) {
107 156 UDF_I_LENALLOC(inode) = epos.offset - udf_file_entry_alloc_offset(inode);
108 157 mark_inode_dirty(inode);
109   - }
110   - else
111   - {
  158 + } else {
112 159 struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data);
113 160 aed->lengthAllocDescs = cpu_to_le32(epos.offset - sizeof(struct allocExtDesc));
114 161 if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
115 162  
... ... @@ -118,9 +165,9 @@
118 165 mark_buffer_dirty_inode(epos.bh, inode);
119 166 }
120 167 }
  168 + /* This inode entry is in-memory only and thus we don't have to mark
  169 + * the inode dirty */
121 170 UDF_I_LENEXTENTS(inode) = lbcount;
122   -
123   - WARN_ON(lbcount != inode->i_size);
124 171 brelse(epos.bh);
125 172 }
126 173  
... ... @@ -146,6 +146,7 @@
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