aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_bmap.c
diff options
context:
space:
mode:
authorLachlan McIlroy <lachlan@sgi.com>2007-05-07 23:49:46 -0400
committerTim Shimmin <tes@sgi.com>2007-05-07 23:49:46 -0400
commitba87ea699ebd9dd577bf055ebc4a98200e337542 (patch)
tree713b7d32937372fd7c5b8647f14d0e7262fc7075 /fs/xfs/xfs_bmap.c
parent2a32963130aec5e157b58ff7dfa3dfa1afdf7ca1 (diff)
[XFS] Fix to prevent the notorious 'NULL files' problem after a crash.
The problem that has been addressed is that of synchronising updates of the file size with writes that extend a file. Without the fix the update of a file's size, as a result of a write beyond eof, is independent of when the cached data is flushed to disk. Often the file size update would be written to the filesystem log before the data is flushed to disk. When a system crashes between these two events and the filesystem log is replayed on mount the file's size will be set but since the contents never made it to disk the file is full of holes. If some of the cached data was flushed to disk then it may just be a section of the file at the end that has holes. There are existing fixes to help alleviate this problem, particularly in the case where a file has been truncated, that force cached data to be flushed to disk when the file is closed. If the system crashes while the file(s) are still open then this flushing will never occur. The fix that we have implemented is to introduce a second file size, called the in-memory file size, that represents the current file size as viewed by the user. The existing file size, called the on-disk file size, is the one that get's written to the filesystem log and we only update it when it is safe to do so. When we write to a file beyond eof we only update the in- memory file size in the write operation. Later when the I/O operation, that flushes the cached data to disk completes, an I/O completion routine will update the on-disk file size. The on-disk file size will be updated to the maximum offset of the I/O or to the value of the in-memory file size if the I/O includes eof. SGI-PV: 958522 SGI-Modid: xfs-linux-melb:xfs-kern:28322a Signed-off-by: Lachlan McIlroy <lachlan@sgi.com> Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Tim Shimmin <tes@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_bmap.c')
-rw-r--r--fs/xfs/xfs_bmap.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 50f2213589f8..b1ea26e40aaf 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -4444,8 +4444,11 @@ xfs_bmap_one_block(
4444 xfs_bmbt_irec_t s; /* internal version of extent */ 4444 xfs_bmbt_irec_t s; /* internal version of extent */
4445 4445
4446#ifndef DEBUG 4446#ifndef DEBUG
4447 if (whichfork == XFS_DATA_FORK) 4447 if (whichfork == XFS_DATA_FORK) {
4448 return ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize; 4448 return ((ip->i_d.di_mode & S_IFMT) == S_IFREG) ?
4449 (ip->i_size == ip->i_mount->m_sb.sb_blocksize) :
4450 (ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
4451 }
4449#endif /* !DEBUG */ 4452#endif /* !DEBUG */
4450 if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1) 4453 if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1)
4451 return 0; 4454 return 0;
@@ -4457,7 +4460,7 @@ xfs_bmap_one_block(
4457 xfs_bmbt_get_all(ep, &s); 4460 xfs_bmbt_get_all(ep, &s);
4458 rval = s.br_startoff == 0 && s.br_blockcount == 1; 4461 rval = s.br_startoff == 0 && s.br_blockcount == 1;
4459 if (rval && whichfork == XFS_DATA_FORK) 4462 if (rval && whichfork == XFS_DATA_FORK)
4460 ASSERT(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize); 4463 ASSERT(ip->i_size == ip->i_mount->m_sb.sb_blocksize);
4461 return rval; 4464 return rval;
4462} 4465}
4463 4466
@@ -5817,7 +5820,7 @@ xfs_getbmap(
5817 fixlen = XFS_MAXIOFFSET(mp); 5820 fixlen = XFS_MAXIOFFSET(mp);
5818 } else { 5821 } else {
5819 prealloced = 0; 5822 prealloced = 0;
5820 fixlen = ip->i_d.di_size; 5823 fixlen = ip->i_size;
5821 } 5824 }
5822 } else { 5825 } else {
5823 prealloced = 0; 5826 prealloced = 0;
@@ -5841,7 +5844,8 @@ xfs_getbmap(
5841 5844
5842 xfs_ilock(ip, XFS_IOLOCK_SHARED); 5845 xfs_ilock(ip, XFS_IOLOCK_SHARED);
5843 5846
5844 if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) { 5847 if (whichfork == XFS_DATA_FORK &&
5848 (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
5845 /* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */ 5849 /* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
5846 error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF); 5850 error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF);
5847 } 5851 }