[PATCH] fix garbage instead of zeroes in UFS

Looks like this is the problem, which point Al Viro some time ago:

ufs's get_block callback allocates 16k of disk at a time, and links that
entire 16k into the file's metadata. But because get_block is called for only
a single buffer_head (a 2k buffer_head in this case?) we are only able to tell
the VFS that this 2k is buffer_new().

So when ufs_getfrag_block() is later called to map some more data in the file,
and when that data resides within the remaining 14k of this fragment,
ufs_getfrag_block() will incorrectly return a !buffer_new() buffer_head.

I don't see _right_ way to do nullification of whole block, if use inode
page cache, some pages may be outside of inode limits (inode size), and
will be lost; if use blockdev page cache it is possible to zero real data,
if later inode page cache will be used.

The simpliest way, as can I see usage of block device page cache, but not only
mark dirty, but also sync it during "nullification". I use my simple tests
collection, which I used for check that create,open,write,read,close works on
ufs, and I see that this patch makes ufs code 18% slower then before.

Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by Evgeniy Dushistov and committed by Linus Torvalds d63b7090 7ba34859

+30 -36
+25
fs/ufs/balloc.c
··· 275 275 UFSD("EXIT\n"); 276 276 } 277 277 278 + static void ufs_clear_frags(struct inode *inode, sector_t beg, unsigned int n, 279 + int sync) 280 + { 281 + struct buffer_head *bh; 282 + sector_t end = beg + n; 283 + 284 + for (; beg < end; ++beg) { 285 + bh = sb_getblk(inode->i_sb, beg); 286 + lock_buffer(bh); 287 + memset(bh->b_data, 0, inode->i_sb->s_blocksize); 288 + set_buffer_uptodate(bh); 289 + mark_buffer_dirty(bh); 290 + unlock_buffer(bh); 291 + if (IS_SYNC(inode) || sync) 292 + sync_dirty_buffer(bh); 293 + brelse(bh); 294 + } 295 + } 296 + 278 297 unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment, 279 298 unsigned goal, unsigned count, int * err, struct page *locked_page) 280 299 { ··· 369 350 *p = cpu_to_fs32(sb, result); 370 351 *err = 0; 371 352 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); 353 + ufs_clear_frags(inode, result + oldcount, newcount - oldcount, 354 + locked_page != NULL); 372 355 } 373 356 unlock_super(sb); 374 357 UFSD("EXIT, result %u\n", result); ··· 384 363 if (result) { 385 364 *err = 0; 386 365 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); 366 + ufs_clear_frags(inode, result + oldcount, newcount - oldcount, 367 + locked_page != NULL); 387 368 unlock_super(sb); 388 369 UFSD("EXIT, result %u\n", result); 389 370 return result; ··· 421 398 *p = cpu_to_fs32(sb, result); 422 399 *err = 0; 423 400 UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); 401 + ufs_clear_frags(inode, result + oldcount, newcount - oldcount, 402 + locked_page != NULL); 424 403 unlock_super(sb); 425 404 if (newcount < request) 426 405 ufs_free_fragments (inode, result + newcount, request - newcount);
+5 -36
fs/ufs/inode.c
··· 156 156 return ret; 157 157 } 158 158 159 - static void ufs_clear_frag(struct inode *inode, struct buffer_head *bh) 160 - { 161 - lock_buffer(bh); 162 - memset(bh->b_data, 0, inode->i_sb->s_blocksize); 163 - set_buffer_uptodate(bh); 164 - mark_buffer_dirty(bh); 165 - unlock_buffer(bh); 166 - if (IS_SYNC(inode)) 167 - sync_dirty_buffer(bh); 168 - } 169 - 170 - static struct buffer_head * 171 - ufs_clear_frags(struct inode *inode, sector_t beg, 172 - unsigned int n, sector_t want) 173 - { 174 - struct buffer_head *res = NULL, *bh; 175 - sector_t end = beg + n; 176 - 177 - for (; beg < end; ++beg) { 178 - bh = sb_getblk(inode->i_sb, beg); 179 - ufs_clear_frag(inode, bh); 180 - if (want != beg) 181 - brelse(bh); 182 - else 183 - res = bh; 184 - } 185 - BUG_ON(!res); 186 - return res; 187 - } 188 - 189 159 /** 190 160 * ufs_inode_getfrag() - allocate new fragment(s) 191 161 * @inode - pointer to inode ··· 272 302 } 273 303 274 304 if (!phys) { 275 - result = ufs_clear_frags(inode, tmp, required, tmp + blockoff); 305 + result = sb_getblk(sb, tmp + blockoff); 276 306 } else { 277 307 *phys = tmp + blockoff; 278 308 result = NULL; ··· 373 403 374 404 375 405 if (!phys) { 376 - result = ufs_clear_frags(inode, tmp, uspi->s_fpb, 377 - tmp + blockoff); 406 + result = sb_getblk(sb, tmp + blockoff); 378 407 } else { 379 408 *phys = tmp + blockoff; 380 409 *new = 1; ··· 440 471 #define GET_INODE_DATABLOCK(x) \ 441 472 ufs_inode_getfrag(inode, x, fragment, 1, &err, &phys, &new, bh_result->b_page) 442 473 #define GET_INODE_PTR(x) \ 443 - ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, NULL, NULL, bh_result->b_page) 474 + ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, NULL, NULL, NULL) 444 475 #define GET_INDIRECT_DATABLOCK(x) \ 445 476 ufs_inode_getblock(inode, bh, x, fragment, \ 446 - &err, &phys, &new, bh_result->b_page); 477 + &err, &phys, &new, bh_result->b_page) 447 478 #define GET_INDIRECT_PTR(x) \ 448 479 ufs_inode_getblock(inode, bh, x, fragment, \ 449 - &err, NULL, NULL, bh_result->b_page); 480 + &err, NULL, NULL, NULL) 450 481 451 482 if (ptr < UFS_NDIR_FRAGMENT) { 452 483 bh = GET_INODE_DATABLOCK(ptr);