aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ufs/truncate.c
diff options
context:
space:
mode:
authorEvgeniy Dushistov <dushistov@mail.ru>2006-07-01 07:36:24 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-07-01 12:56:03 -0400
commit10e5dce07e6f8f9cea1b54161a888bb099484f88 (patch)
tree9c7949cf82763344d86ae302748f8e1d278b565a /fs/ufs/truncate.c
parenteb28931e4a2c89e53d2b0c1a02a843240bff0806 (diff)
[PATCH] ufs: truncate should allocate block for last byte
This patch fixes buggy behaviour of UFS in such kind of scenario: open(, O_TRUNC...) ftruncate(, 1024) ftruncate(, 0) Such a scenario causes ufs_panic and remount read-only. This happen because of according to specification UFS should always allocate block for last byte, and many parts of our implementation rely on this, but `ufs_truncate' doesn't care about this. To make possible return error code and to know about old size, this patch removes `truncate' from ufs inode_operations and uses `setattr' method to call ufs_truncate. Signed-off-by: Evgeniy Dushistov <dushistov@mail.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/ufs/truncate.c')
-rw-r--r--fs/ufs/truncate.c148
1 files changed, 135 insertions, 13 deletions
diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
index 3c3b301f8701..c9b55872079b 100644
--- a/fs/ufs/truncate.c
+++ b/fs/ufs/truncate.c
@@ -369,24 +369,97 @@ static int ufs_trunc_tindirect (struct inode * inode)
369 UFSD("EXIT\n"); 369 UFSD("EXIT\n");
370 return retry; 370 return retry;
371} 371}
372 372
373void ufs_truncate (struct inode * inode) 373static int ufs_alloc_lastblock(struct inode *inode)
374{ 374{
375 int err = 0;
376 struct address_space *mapping = inode->i_mapping;
377 struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi;
375 struct ufs_inode_info *ufsi = UFS_I(inode); 378 struct ufs_inode_info *ufsi = UFS_I(inode);
376 struct super_block * sb; 379 unsigned lastfrag, i, end;
377 struct ufs_sb_private_info * uspi; 380 struct page *lastpage;
378 int retry; 381 struct buffer_head *bh;
382
383 lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift;
384
385 if (!lastfrag) {
386 ufsi->i_lastfrag = 0;
387 goto out;
388 }
389 lastfrag--;
390
391 lastpage = ufs_get_locked_page(mapping, lastfrag >>
392 (PAGE_CACHE_SHIFT - inode->i_blkbits));
393 if (IS_ERR(lastpage)) {
394 err = -EIO;
395 goto out;
396 }
397
398 end = lastfrag & ((1 << (PAGE_CACHE_SHIFT - inode->i_blkbits)) - 1);
399 bh = page_buffers(lastpage);
400 for (i = 0; i < end; ++i)
401 bh = bh->b_this_page;
402
403 if (!buffer_mapped(bh)) {
404 err = ufs_getfrag_block(inode, lastfrag, bh, 1);
405
406 if (unlikely(err))
407 goto out_unlock;
408
409 if (buffer_new(bh)) {
410 clear_buffer_new(bh);
411 unmap_underlying_metadata(bh->b_bdev,
412 bh->b_blocknr);
413 /*
414 * we do not zeroize fragment, because of
415 * if it maped to hole, it already contains zeroes
416 */
417 set_buffer_uptodate(bh);
418 mark_buffer_dirty(bh);
419 set_page_dirty(lastpage);
420 }
421 }
422out_unlock:
423 ufs_put_locked_page(lastpage);
424out:
425 return err;
426}
427
428int ufs_truncate(struct inode *inode, loff_t old_i_size)
429{
430 struct ufs_inode_info *ufsi = UFS_I(inode);
431 struct super_block *sb = inode->i_sb;
432 struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
433 int retry, err = 0;
379 434
380 UFSD("ENTER\n"); 435 UFSD("ENTER\n");
381 sb = inode->i_sb;
382 uspi = UFS_SB(sb)->s_uspi;
383 436
384 if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) 437 if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
385 return; 438 S_ISLNK(inode->i_mode)))
439 return -EINVAL;
386 if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 440 if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
387 return; 441 return -EPERM;
442
443 if (inode->i_size > old_i_size) {
444 /*
445 * if we expand file we should care about
446 * allocation of block for last byte first of all
447 */
448 err = ufs_alloc_lastblock(inode);
449
450 if (err) {
451 i_size_write(inode, old_i_size);
452 goto out;
453 }
454 /*
455 * go away, because of we expand file, and we do not
456 * need free blocks, and zeroizes page
457 */
458 lock_kernel();
459 goto almost_end;
460 }
388 461
389 block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block); 462 block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block);
390 463
391 lock_kernel(); 464 lock_kernel();
392 while (1) { 465 while (1) {
@@ -404,9 +477,58 @@ void ufs_truncate (struct inode * inode)
404 yield(); 477 yield();
405 } 478 }
406 479
480 if (inode->i_size < old_i_size) {
481 /*
482 * now we should have enough space
483 * to allocate block for last byte
484 */
485 err = ufs_alloc_lastblock(inode);
486 if (err)
487 /*
488 * looks like all the same - we have no space,
489 * but we truncate file already
490 */
491 inode->i_size = (ufsi->i_lastfrag - 1) * uspi->s_fsize;
492 }
493almost_end:
407 inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; 494 inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
408 ufsi->i_lastfrag = DIRECT_FRAGMENT;
409 unlock_kernel(); 495 unlock_kernel();
410 mark_inode_dirty(inode); 496 mark_inode_dirty(inode);
411 UFSD("EXIT\n"); 497out:
498 UFSD("EXIT: err %d\n", err);
499 return err;
412} 500}
501
502
503/*
504 * We don't define our `inode->i_op->truncate', and call it here,
505 * because of:
506 * - there is no way to know old size
507 * - there is no way inform user about error, if it happens in `truncate'
508 */
509static int ufs_setattr(struct dentry *dentry, struct iattr *attr)
510{
511 struct inode *inode = dentry->d_inode;
512 unsigned int ia_valid = attr->ia_valid;
513 int error;
514
515 error = inode_change_ok(inode, attr);
516 if (error)
517 return error;
518
519 if (ia_valid & ATTR_SIZE &&
520 attr->ia_size != i_size_read(inode)) {
521 loff_t old_i_size = inode->i_size;
522 error = vmtruncate(inode, attr->ia_size);
523 if (error)
524 return error;
525 error = ufs_truncate(inode, old_i_size);
526 if (error)
527 return error;
528 }
529 return inode_setattr(inode, attr);
530}
531
532struct inode_operations ufs_file_inode_operations = {
533 .setattr = ufs_setattr,
534};