aboutsummaryrefslogtreecommitdiffstats
path: root/fs
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
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')
-rw-r--r--fs/ufs/balloc.c48
-rw-r--r--fs/ufs/file.c4
-rw-r--r--fs/ufs/inode.c7
-rw-r--r--fs/ufs/truncate.c148
-rw-r--r--fs/ufs/util.c54
-rw-r--r--fs/ufs/util.h8
6 files changed, 204 insertions, 65 deletions
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
index 95b878e5c7a0..b01804baa120 100644
--- a/fs/ufs/balloc.c
+++ b/fs/ufs/balloc.c
@@ -217,48 +217,6 @@ failed:
217 return; 217 return;
218} 218}
219 219
220static struct page *ufs_get_locked_page(struct address_space *mapping,
221 unsigned long index)
222{
223 struct page *page;
224
225try_again:
226 page = find_lock_page(mapping, index);
227 if (!page) {
228 page = read_cache_page(mapping, index,
229 (filler_t*)mapping->a_ops->readpage,
230 NULL);
231 if (IS_ERR(page)) {
232 printk(KERN_ERR "ufs_change_blocknr: "
233 "read_cache_page error: ino %lu, index: %lu\n",
234 mapping->host->i_ino, index);
235 goto out;
236 }
237
238 lock_page(page);
239
240 if (!PageUptodate(page) || PageError(page)) {
241 unlock_page(page);
242 page_cache_release(page);
243
244 printk(KERN_ERR "ufs_change_blocknr: "
245 "can not read page: ino %lu, index: %lu\n",
246 mapping->host->i_ino, index);
247
248 page = ERR_PTR(-EIO);
249 goto out;
250 }
251 }
252
253 if (unlikely(!page->mapping || !page_has_buffers(page))) {
254 unlock_page(page);
255 page_cache_release(page);
256 goto try_again;/*we really need these buffers*/
257 }
258out:
259 return page;
260}
261
262/* 220/*
263 * Modify inode page cache in such way: 221 * Modify inode page cache in such way:
264 * have - blocks with b_blocknr equal to oldb...oldb+count-1 222 * have - blocks with b_blocknr equal to oldb...oldb+count-1
@@ -311,10 +269,8 @@ static void ufs_change_blocknr(struct inode *inode, unsigned int baseblk,
311 269
312 set_page_dirty(page); 270 set_page_dirty(page);
313 271
314 if (likely(cur_index != index)) { 272 if (likely(cur_index != index))
315 unlock_page(page); 273 ufs_put_locked_page(page);
316 page_cache_release(page);
317 }
318 } 274 }
319 UFSD("EXIT\n"); 275 UFSD("EXIT\n");
320} 276}
diff --git a/fs/ufs/file.c b/fs/ufs/file.c
index 0e5001512a9d..a9c6e5f04fae 100644
--- a/fs/ufs/file.c
+++ b/fs/ufs/file.c
@@ -60,7 +60,3 @@ const struct file_operations ufs_file_operations = {
60 .fsync = ufs_sync_file, 60 .fsync = ufs_sync_file,
61 .sendfile = generic_file_sendfile, 61 .sendfile = generic_file_sendfile,
62}; 62};
63
64struct inode_operations ufs_file_inode_operations = {
65 .truncate = ufs_truncate,
66};
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
index 488b5ff48afb..e7c8615beb65 100644
--- a/fs/ufs/inode.c
+++ b/fs/ufs/inode.c
@@ -843,14 +843,17 @@ int ufs_sync_inode (struct inode *inode)
843 843
844void ufs_delete_inode (struct inode * inode) 844void ufs_delete_inode (struct inode * inode)
845{ 845{
846 loff_t old_i_size;
847
846 truncate_inode_pages(&inode->i_data, 0); 848 truncate_inode_pages(&inode->i_data, 0);
847 /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ 849 /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/
848 lock_kernel(); 850 lock_kernel();
849 mark_inode_dirty(inode); 851 mark_inode_dirty(inode);
850 ufs_update_inode(inode, IS_SYNC(inode)); 852 ufs_update_inode(inode, IS_SYNC(inode));
853 old_i_size = inode->i_size;
851 inode->i_size = 0; 854 inode->i_size = 0;
852 if (inode->i_blocks) 855 if (inode->i_blocks && ufs_truncate(inode, old_i_size))
853 ufs_truncate (inode); 856 ufs_warning(inode->i_sb, __FUNCTION__, "ufs_truncate failed\n");
854 ufs_free_inode (inode); 857 ufs_free_inode (inode);
855 unlock_kernel(); 858 unlock_kernel();
856} 859}
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};
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
index a2f13f45708b..337cf2c46d10 100644
--- a/fs/ufs/util.c
+++ b/fs/ufs/util.c
@@ -233,3 +233,57 @@ ufs_set_inode_dev(struct super_block *sb, struct ufs_inode_info *ufsi, dev_t dev
233 else 233 else
234 ufsi->i_u1.i_data[0] = fs32; 234 ufsi->i_u1.i_data[0] = fs32;
235} 235}
236
237/**
238 * ufs_get_locked_page() - locate, pin and lock a pagecache page, if not exist
239 * read it from disk.
240 * @mapping: the address_space to search
241 * @index: the page index
242 *
243 * Locates the desired pagecache page, if not exist we'll read it,
244 * locks it, increments its reference
245 * count and returns its address.
246 *
247 */
248
249struct page *ufs_get_locked_page(struct address_space *mapping,
250 pgoff_t index)
251{
252 struct page *page;
253
254try_again:
255 page = find_lock_page(mapping, index);
256 if (!page) {
257 page = read_cache_page(mapping, index,
258 (filler_t*)mapping->a_ops->readpage,
259 NULL);
260 if (IS_ERR(page)) {
261 printk(KERN_ERR "ufs_change_blocknr: "
262 "read_cache_page error: ino %lu, index: %lu\n",
263 mapping->host->i_ino, index);
264 goto out;
265 }
266
267 lock_page(page);
268
269 if (!PageUptodate(page) || PageError(page)) {
270 unlock_page(page);
271 page_cache_release(page);
272
273 printk(KERN_ERR "ufs_change_blocknr: "
274 "can not read page: ino %lu, index: %lu\n",
275 mapping->host->i_ino, index);
276
277 page = ERR_PTR(-EIO);
278 goto out;
279 }
280 }
281
282 if (unlikely(!page->mapping || !page_has_buffers(page))) {
283 unlock_page(page);
284 page_cache_release(page);
285 goto try_again;/*we really need these buffers*/
286 }
287out:
288 return page;
289}
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
index 406981fff5e7..28fce6c239b5 100644
--- a/fs/ufs/util.h
+++ b/fs/ufs/util.h
@@ -251,6 +251,14 @@ extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struc
251#define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size) 251#define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size)
252extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned); 252extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned);
253 253
254/* This functions works with cache pages*/
255extern struct page *ufs_get_locked_page(struct address_space *mapping,
256 pgoff_t index);
257static inline void ufs_put_locked_page(struct page *page)
258{
259 unlock_page(page);
260 page_cache_release(page);
261}
254 262
255 263
256/* 264/*