mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 22:21:42 +00:00
[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>
This commit is contained in:
parent
2a32963130
commit
ba87ea699e
@ -140,10 +140,47 @@ xfs_destroy_ioend(
|
||||
mempool_free(ioend, xfs_ioend_pool);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update on-disk file size now that data has been written to disk.
|
||||
* The current in-memory file size is i_size. If a write is beyond
|
||||
* eof io_new_size will be the intended file size until i_size is
|
||||
* updated. If this write does not extend all the way to the valid
|
||||
* file size then restrict this update to the end of the write.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_setfilesize(
|
||||
xfs_ioend_t *ioend)
|
||||
{
|
||||
xfs_inode_t *ip;
|
||||
xfs_fsize_t isize;
|
||||
xfs_fsize_t bsize;
|
||||
|
||||
ip = xfs_vtoi(ioend->io_vnode);
|
||||
|
||||
ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFREG);
|
||||
ASSERT(ioend->io_type != IOMAP_READ);
|
||||
|
||||
if (unlikely(ioend->io_error))
|
||||
return;
|
||||
|
||||
bsize = ioend->io_offset + ioend->io_size;
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
|
||||
isize = MAX(ip->i_size, ip->i_iocore.io_new_size);
|
||||
isize = MIN(isize, bsize);
|
||||
|
||||
if (ip->i_d.di_size < isize) {
|
||||
ip->i_d.di_size = isize;
|
||||
ip->i_update_core = 1;
|
||||
ip->i_update_size = 1;
|
||||
}
|
||||
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffered IO write completion for delayed allocate extents.
|
||||
* TODO: Update ondisk isize now that we know the file data
|
||||
* has been flushed (i.e. the notorious "NULL file" problem).
|
||||
*/
|
||||
STATIC void
|
||||
xfs_end_bio_delalloc(
|
||||
@ -152,6 +189,7 @@ xfs_end_bio_delalloc(
|
||||
xfs_ioend_t *ioend =
|
||||
container_of(work, xfs_ioend_t, io_work);
|
||||
|
||||
xfs_setfilesize(ioend);
|
||||
xfs_destroy_ioend(ioend);
|
||||
}
|
||||
|
||||
@ -165,6 +203,7 @@ xfs_end_bio_written(
|
||||
xfs_ioend_t *ioend =
|
||||
container_of(work, xfs_ioend_t, io_work);
|
||||
|
||||
xfs_setfilesize(ioend);
|
||||
xfs_destroy_ioend(ioend);
|
||||
}
|
||||
|
||||
@ -184,8 +223,23 @@ xfs_end_bio_unwritten(
|
||||
xfs_off_t offset = ioend->io_offset;
|
||||
size_t size = ioend->io_size;
|
||||
|
||||
if (likely(!ioend->io_error))
|
||||
if (likely(!ioend->io_error)) {
|
||||
bhv_vop_bmap(vp, offset, size, BMAPI_UNWRITTEN, NULL, NULL);
|
||||
xfs_setfilesize(ioend);
|
||||
}
|
||||
xfs_destroy_ioend(ioend);
|
||||
}
|
||||
|
||||
/*
|
||||
* IO read completion for regular, written extents.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_end_bio_read(
|
||||
struct work_struct *work)
|
||||
{
|
||||
xfs_ioend_t *ioend =
|
||||
container_of(work, xfs_ioend_t, io_work);
|
||||
|
||||
xfs_destroy_ioend(ioend);
|
||||
}
|
||||
|
||||
@ -224,6 +278,8 @@ xfs_alloc_ioend(
|
||||
INIT_WORK(&ioend->io_work, xfs_end_bio_unwritten);
|
||||
else if (type == IOMAP_DELAY)
|
||||
INIT_WORK(&ioend->io_work, xfs_end_bio_delalloc);
|
||||
else if (type == IOMAP_READ)
|
||||
INIT_WORK(&ioend->io_work, xfs_end_bio_read);
|
||||
else
|
||||
INIT_WORK(&ioend->io_work, xfs_end_bio_written);
|
||||
|
||||
@ -913,7 +969,7 @@ xfs_page_state_convert(
|
||||
bh = head = page_buffers(page);
|
||||
offset = page_offset(page);
|
||||
flags = -1;
|
||||
type = 0;
|
||||
type = IOMAP_READ;
|
||||
|
||||
/* TODO: cleanup count and page_dirty */
|
||||
|
||||
@ -999,7 +1055,7 @@ xfs_page_state_convert(
|
||||
* That means it must already have extents allocated
|
||||
* underneath it. Map the extent by reading it.
|
||||
*/
|
||||
if (!iomap_valid || type != 0) {
|
||||
if (!iomap_valid || type != IOMAP_READ) {
|
||||
flags = BMAPI_READ;
|
||||
size = xfs_probe_cluster(inode, page, bh,
|
||||
head, 1);
|
||||
@ -1010,7 +1066,7 @@ xfs_page_state_convert(
|
||||
iomap_valid = xfs_iomap_valid(&iomap, offset);
|
||||
}
|
||||
|
||||
type = 0;
|
||||
type = IOMAP_READ;
|
||||
if (!test_and_set_bit(BH_Lock, &bh->b_state)) {
|
||||
ASSERT(buffer_mapped(bh));
|
||||
if (iomap_valid)
|
||||
@ -1356,12 +1412,21 @@ xfs_end_io_direct(
|
||||
* completion handler in the future, in which case all this can
|
||||
* go away.
|
||||
*/
|
||||
if (private && size > 0) {
|
||||
ioend->io_offset = offset;
|
||||
ioend->io_size = size;
|
||||
ioend->io_offset = offset;
|
||||
ioend->io_size = size;
|
||||
if (ioend->io_type == IOMAP_READ) {
|
||||
xfs_finish_ioend(ioend);
|
||||
} else if (private && size > 0) {
|
||||
xfs_finish_ioend(ioend);
|
||||
} else {
|
||||
xfs_destroy_ioend(ioend);
|
||||
/*
|
||||
* A direct I/O write ioend starts it's life in unwritten
|
||||
* state in case they map an unwritten extent. This write
|
||||
* didn't map an unwritten extent so switch it's completion
|
||||
* handler.
|
||||
*/
|
||||
INIT_WORK(&ioend->io_work, xfs_end_bio_written);
|
||||
xfs_finish_ioend(ioend);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1392,15 +1457,15 @@ xfs_vm_direct_IO(
|
||||
if (error)
|
||||
return -error;
|
||||
|
||||
iocb->private = xfs_alloc_ioend(inode, IOMAP_UNWRITTEN);
|
||||
|
||||
if (rw == WRITE) {
|
||||
iocb->private = xfs_alloc_ioend(inode, IOMAP_UNWRITTEN);
|
||||
ret = blockdev_direct_IO_own_locking(rw, iocb, inode,
|
||||
iomap.iomap_target->bt_bdev,
|
||||
iov, offset, nr_segs,
|
||||
xfs_get_blocks_direct,
|
||||
xfs_end_io_direct);
|
||||
} else {
|
||||
iocb->private = xfs_alloc_ioend(inode, IOMAP_READ);
|
||||
ret = blockdev_direct_IO_no_locking(rw, iocb, inode,
|
||||
iomap.iomap_target->bt_bdev,
|
||||
iov, offset, nr_segs,
|
||||
|
@ -224,7 +224,7 @@ xfs_read(
|
||||
mp->m_rtdev_targp : mp->m_ddev_targp;
|
||||
if ((*offset & target->bt_smask) ||
|
||||
(size & target->bt_smask)) {
|
||||
if (*offset == ip->i_d.di_size) {
|
||||
if (*offset == ip->i_size) {
|
||||
return (0);
|
||||
}
|
||||
return -XFS_ERROR(EINVAL);
|
||||
@ -387,9 +387,10 @@ xfs_splice_write(
|
||||
{
|
||||
xfs_inode_t *ip = XFS_BHVTOI(bdp);
|
||||
xfs_mount_t *mp = ip->i_mount;
|
||||
xfs_iocore_t *io = &ip->i_iocore;
|
||||
ssize_t ret;
|
||||
struct inode *inode = outfilp->f_mapping->host;
|
||||
xfs_fsize_t isize;
|
||||
xfs_fsize_t isize, new_size;
|
||||
|
||||
XFS_STATS_INC(xs_write_calls);
|
||||
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
|
||||
@ -410,6 +411,14 @@ xfs_splice_write(
|
||||
return -error;
|
||||
}
|
||||
}
|
||||
|
||||
new_size = *ppos + count;
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
if (new_size > ip->i_size)
|
||||
io->io_new_size = new_size;
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
|
||||
xfs_rw_enter_trace(XFS_SPLICE_WRITE_ENTER, &ip->i_iocore,
|
||||
pipe, count, *ppos, ioflags);
|
||||
ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags);
|
||||
@ -420,14 +429,18 @@ xfs_splice_write(
|
||||
if (unlikely(ret < 0 && ret != -EFAULT && *ppos > isize))
|
||||
*ppos = isize;
|
||||
|
||||
if (*ppos > ip->i_d.di_size) {
|
||||
if (*ppos > ip->i_size) {
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
if (*ppos > ip->i_d.di_size) {
|
||||
ip->i_d.di_size = *ppos;
|
||||
i_size_write(inode, *ppos);
|
||||
ip->i_update_core = 1;
|
||||
ip->i_update_size = 1;
|
||||
}
|
||||
if (*ppos > ip->i_size)
|
||||
ip->i_size = *ppos;
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
}
|
||||
|
||||
if (io->io_new_size) {
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
io->io_new_size = 0;
|
||||
if (ip->i_d.di_size > ip->i_size)
|
||||
ip->i_d.di_size = ip->i_size;
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
}
|
||||
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
||||
@ -711,8 +724,6 @@ start:
|
||||
goto out_unlock_mutex;
|
||||
}
|
||||
|
||||
isize = i_size_read(inode);
|
||||
|
||||
if (ioflags & IO_ISDIRECT) {
|
||||
xfs_buftarg_t *target =
|
||||
(xip->i_d.di_flags & XFS_DIFLAG_REALTIME) ?
|
||||
@ -723,7 +734,7 @@ start:
|
||||
return XFS_ERROR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!need_i_mutex && (VN_CACHED(vp) || pos > isize)) {
|
||||
if (!need_i_mutex && (VN_CACHED(vp) || pos > xip->i_size)) {
|
||||
xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
|
||||
iolock = XFS_IOLOCK_EXCL;
|
||||
locktype = VRWLOCK_WRITE;
|
||||
@ -735,7 +746,7 @@ start:
|
||||
}
|
||||
|
||||
new_size = pos + count;
|
||||
if (new_size > isize)
|
||||
if (new_size > xip->i_size)
|
||||
io->io_new_size = new_size;
|
||||
|
||||
if ((DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_WRITE) &&
|
||||
@ -751,8 +762,7 @@ start:
|
||||
pos, count,
|
||||
dmflags, &locktype);
|
||||
if (error) {
|
||||
xfs_iunlock(xip, iolock);
|
||||
goto out_unlock_mutex;
|
||||
goto out_unlock_internal;
|
||||
}
|
||||
xfs_ilock(xip, XFS_ILOCK_EXCL);
|
||||
eventsent = 1;
|
||||
@ -764,9 +774,8 @@ start:
|
||||
* event prevents another call to XFS_SEND_DATA, which is
|
||||
* what allows the size to change in the first place.
|
||||
*/
|
||||
if ((file->f_flags & O_APPEND) && savedsize != isize) {
|
||||
if ((file->f_flags & O_APPEND) && savedsize != xip->i_size)
|
||||
goto start;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(!(ioflags & IO_INVIS))) {
|
||||
@ -784,11 +793,11 @@ start:
|
||||
* to zero it out up to the new size.
|
||||
*/
|
||||
|
||||
if (pos > isize) {
|
||||
error = xfs_zero_eof(BHV_TO_VNODE(bdp), io, pos, isize);
|
||||
if (pos > xip->i_size) {
|
||||
error = xfs_zero_eof(BHV_TO_VNODE(bdp), io, pos, xip->i_size);
|
||||
if (error) {
|
||||
xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
|
||||
goto out_unlock_mutex;
|
||||
xfs_iunlock(xip, XFS_ILOCK_EXCL);
|
||||
goto out_unlock_internal;
|
||||
}
|
||||
}
|
||||
xfs_iunlock(xip, XFS_ILOCK_EXCL);
|
||||
@ -808,8 +817,7 @@ start:
|
||||
if (likely(!error))
|
||||
error = -remove_suid(file->f_path.dentry);
|
||||
if (unlikely(error)) {
|
||||
xfs_iunlock(xip, iolock);
|
||||
goto out_unlock_mutex;
|
||||
goto out_unlock_internal;
|
||||
}
|
||||
}
|
||||
|
||||
@ -879,12 +887,12 @@ retry:
|
||||
error = XFS_SEND_NAMESP(xip->i_mount, DM_EVENT_NOSPACE, vp,
|
||||
DM_RIGHT_NULL, vp, DM_RIGHT_NULL, NULL, NULL,
|
||||
0, 0, 0); /* Delay flag intentionally unused */
|
||||
if (error)
|
||||
goto out_nounlocks;
|
||||
if (need_i_mutex)
|
||||
mutex_lock(&inode->i_mutex);
|
||||
xfs_rwlock(bdp, locktype);
|
||||
pos = xip->i_d.di_size;
|
||||
if (error)
|
||||
goto out_unlock_internal;
|
||||
pos = xip->i_size;
|
||||
ret = 0;
|
||||
goto retry;
|
||||
}
|
||||
@ -893,14 +901,10 @@ retry:
|
||||
if (unlikely(ret < 0 && ret != -EFAULT && *offset > isize))
|
||||
*offset = isize;
|
||||
|
||||
if (*offset > xip->i_d.di_size) {
|
||||
if (*offset > xip->i_size) {
|
||||
xfs_ilock(xip, XFS_ILOCK_EXCL);
|
||||
if (*offset > xip->i_d.di_size) {
|
||||
xip->i_d.di_size = *offset;
|
||||
i_size_write(inode, *offset);
|
||||
xip->i_update_core = 1;
|
||||
xip->i_update_size = 1;
|
||||
}
|
||||
if (*offset > xip->i_size)
|
||||
xip->i_size = *offset;
|
||||
xfs_iunlock(xip, XFS_ILOCK_EXCL);
|
||||
}
|
||||
|
||||
@ -922,16 +926,31 @@ retry:
|
||||
|
||||
error = sync_page_range(inode, mapping, pos, ret);
|
||||
if (!error)
|
||||
error = ret;
|
||||
return error;
|
||||
error = -ret;
|
||||
if (need_i_mutex)
|
||||
mutex_lock(&inode->i_mutex);
|
||||
xfs_rwlock(bdp, locktype);
|
||||
}
|
||||
|
||||
out_unlock_internal:
|
||||
if (io->io_new_size) {
|
||||
xfs_ilock(xip, XFS_ILOCK_EXCL);
|
||||
io->io_new_size = 0;
|
||||
/*
|
||||
* If this was a direct or synchronous I/O that failed (such
|
||||
* as ENOSPC) then part of the I/O may have been written to
|
||||
* disk before the error occured. In this case the on-disk
|
||||
* file size may have been adjusted beyond the in-memory file
|
||||
* size and now needs to be truncated back.
|
||||
*/
|
||||
if (xip->i_d.di_size > xip->i_size)
|
||||
xip->i_d.di_size = xip->i_size;
|
||||
xfs_iunlock(xip, XFS_ILOCK_EXCL);
|
||||
}
|
||||
xfs_rwunlock(bdp, locktype);
|
||||
out_unlock_mutex:
|
||||
if (need_i_mutex)
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
out_nounlocks:
|
||||
return -error;
|
||||
}
|
||||
|
||||
|
@ -4444,8 +4444,11 @@ xfs_bmap_one_block(
|
||||
xfs_bmbt_irec_t s; /* internal version of extent */
|
||||
|
||||
#ifndef DEBUG
|
||||
if (whichfork == XFS_DATA_FORK)
|
||||
return ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize;
|
||||
if (whichfork == XFS_DATA_FORK) {
|
||||
return ((ip->i_d.di_mode & S_IFMT) == S_IFREG) ?
|
||||
(ip->i_size == ip->i_mount->m_sb.sb_blocksize) :
|
||||
(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
|
||||
}
|
||||
#endif /* !DEBUG */
|
||||
if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1)
|
||||
return 0;
|
||||
@ -4457,7 +4460,7 @@ xfs_bmap_one_block(
|
||||
xfs_bmbt_get_all(ep, &s);
|
||||
rval = s.br_startoff == 0 && s.br_blockcount == 1;
|
||||
if (rval && whichfork == XFS_DATA_FORK)
|
||||
ASSERT(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
|
||||
ASSERT(ip->i_size == ip->i_mount->m_sb.sb_blocksize);
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -5817,7 +5820,7 @@ xfs_getbmap(
|
||||
fixlen = XFS_MAXIOFFSET(mp);
|
||||
} else {
|
||||
prealloced = 0;
|
||||
fixlen = ip->i_d.di_size;
|
||||
fixlen = ip->i_size;
|
||||
}
|
||||
} else {
|
||||
prealloced = 0;
|
||||
@ -5841,7 +5844,8 @@ xfs_getbmap(
|
||||
|
||||
xfs_ilock(ip, XFS_IOLOCK_SHARED);
|
||||
|
||||
if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) {
|
||||
if (whichfork == XFS_DATA_FORK &&
|
||||
(ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
|
||||
/* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
|
||||
error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF);
|
||||
}
|
||||
|
@ -442,6 +442,7 @@ xfs_iformat(
|
||||
return XFS_ERROR(EFSCORRUPTED);
|
||||
}
|
||||
ip->i_d.di_size = 0;
|
||||
ip->i_size = 0;
|
||||
ip->i_df.if_u2.if_rdev = INT_GET(dip->di_u.di_dev, ARCH_CONVERT);
|
||||
break;
|
||||
|
||||
@ -980,6 +981,7 @@ xfs_iread(
|
||||
}
|
||||
|
||||
ip->i_delayed_blks = 0;
|
||||
ip->i_size = ip->i_d.di_size;
|
||||
|
||||
/*
|
||||
* Mark the buffer containing the inode as something to keep
|
||||
@ -1170,6 +1172,7 @@ xfs_ialloc(
|
||||
}
|
||||
|
||||
ip->i_d.di_size = 0;
|
||||
ip->i_size = 0;
|
||||
ip->i_d.di_nextents = 0;
|
||||
ASSERT(ip->i_d.di_nblocks == 0);
|
||||
xfs_ichgtime(ip, XFS_ICHGTIME_CHG|XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD);
|
||||
@ -1340,7 +1343,7 @@ xfs_file_last_byte(
|
||||
} else {
|
||||
last_block = 0;
|
||||
}
|
||||
size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_d.di_size);
|
||||
size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_size);
|
||||
last_block = XFS_FILEOFF_MAX(last_block, size_last_block);
|
||||
|
||||
last_byte = XFS_FSB_TO_B(mp, last_block);
|
||||
@ -1434,7 +1437,7 @@ xfs_itruncate_start(
|
||||
int error = 0;
|
||||
|
||||
ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
|
||||
ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
|
||||
ASSERT((new_size == 0) || (new_size <= ip->i_size));
|
||||
ASSERT((flags == XFS_ITRUNC_DEFINITE) ||
|
||||
(flags == XFS_ITRUNC_MAYBE));
|
||||
|
||||
@ -1558,7 +1561,7 @@ xfs_itruncate_finish(
|
||||
|
||||
ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
|
||||
ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE) != 0);
|
||||
ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
|
||||
ASSERT((new_size == 0) || (new_size <= ip->i_size));
|
||||
ASSERT(*tp != NULL);
|
||||
ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
|
||||
ASSERT(ip->i_transp == *tp);
|
||||
@ -1632,8 +1635,20 @@ xfs_itruncate_finish(
|
||||
*/
|
||||
if (fork == XFS_DATA_FORK) {
|
||||
if (ip->i_d.di_nextents > 0) {
|
||||
ip->i_d.di_size = new_size;
|
||||
xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
|
||||
/*
|
||||
* If we are not changing the file size then do
|
||||
* not update the on-disk file size - we may be
|
||||
* called from xfs_inactive_free_eofblocks(). If we
|
||||
* update the on-disk file size and then the system
|
||||
* crashes before the contents of the file are
|
||||
* flushed to disk then the files may be full of
|
||||
* holes (ie NULL files bug).
|
||||
*/
|
||||
if (ip->i_size != new_size) {
|
||||
ip->i_d.di_size = new_size;
|
||||
ip->i_size = new_size;
|
||||
xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
|
||||
}
|
||||
}
|
||||
} else if (sync) {
|
||||
ASSERT(!(mp->m_flags & XFS_MOUNT_WSYNC));
|
||||
@ -1769,7 +1784,19 @@ xfs_itruncate_finish(
|
||||
*/
|
||||
if (fork == XFS_DATA_FORK) {
|
||||
xfs_isize_check(mp, ip, new_size);
|
||||
ip->i_d.di_size = new_size;
|
||||
/*
|
||||
* If we are not changing the file size then do
|
||||
* not update the on-disk file size - we may be
|
||||
* called from xfs_inactive_free_eofblocks(). If we
|
||||
* update the on-disk file size and then the system
|
||||
* crashes before the contents of the file are
|
||||
* flushed to disk then the files may be full of
|
||||
* holes (ie NULL files bug).
|
||||
*/
|
||||
if (ip->i_size != new_size) {
|
||||
ip->i_d.di_size = new_size;
|
||||
ip->i_size = new_size;
|
||||
}
|
||||
}
|
||||
xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
|
||||
ASSERT((new_size != 0) ||
|
||||
@ -1802,7 +1829,7 @@ xfs_igrow_start(
|
||||
|
||||
ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
|
||||
ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
|
||||
ASSERT(new_size > ip->i_d.di_size);
|
||||
ASSERT(new_size > ip->i_size);
|
||||
|
||||
/*
|
||||
* Zero any pages that may have been created by
|
||||
@ -1810,7 +1837,7 @@ xfs_igrow_start(
|
||||
* and any blocks between the old and new file sizes.
|
||||
*/
|
||||
error = xfs_zero_eof(XFS_ITOV(ip), &ip->i_iocore, new_size,
|
||||
ip->i_d.di_size);
|
||||
ip->i_size);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1834,13 +1861,14 @@ xfs_igrow_finish(
|
||||
ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
|
||||
ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
|
||||
ASSERT(ip->i_transp == tp);
|
||||
ASSERT(new_size > ip->i_d.di_size);
|
||||
ASSERT(new_size > ip->i_size);
|
||||
|
||||
/*
|
||||
* Update the file size. Update the inode change timestamp
|
||||
* if change_flag set.
|
||||
*/
|
||||
ip->i_d.di_size = new_size;
|
||||
ip->i_size = new_size;
|
||||
if (change_flag)
|
||||
xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
@ -2323,7 +2351,7 @@ xfs_ifree(
|
||||
ASSERT(ip->i_d.di_nlink == 0);
|
||||
ASSERT(ip->i_d.di_nextents == 0);
|
||||
ASSERT(ip->i_d.di_anextents == 0);
|
||||
ASSERT((ip->i_d.di_size == 0) ||
|
||||
ASSERT((ip->i_d.di_size == 0 && ip->i_size == 0) ||
|
||||
((ip->i_d.di_mode & S_IFMT) != S_IFREG));
|
||||
ASSERT(ip->i_d.di_nblocks == 0);
|
||||
|
||||
|
@ -287,6 +287,7 @@ typedef struct xfs_inode {
|
||||
struct xfs_inode *i_cnext; /* cluster hash link forward */
|
||||
struct xfs_inode *i_cprev; /* cluster hash link backward */
|
||||
|
||||
xfs_fsize_t i_size; /* in-memory size */
|
||||
/* Trace buffers per inode. */
|
||||
#ifdef XFS_BMAP_TRACE
|
||||
struct ktrace *i_xtrace; /* inode extent list trace */
|
||||
@ -305,6 +306,8 @@ typedef struct xfs_inode {
|
||||
#endif
|
||||
} xfs_inode_t;
|
||||
|
||||
#define XFS_ISIZE(ip) (((ip)->i_d.di_mode & S_IFMT) == S_IFREG) ? \
|
||||
(ip)->i_size : (ip)->i_d.di_size;
|
||||
|
||||
/*
|
||||
* i_flags helper functions
|
||||
|
@ -52,7 +52,7 @@ STATIC xfs_fsize_t
|
||||
xfs_size_fn(
|
||||
xfs_inode_t *ip)
|
||||
{
|
||||
return (ip->i_d.di_size);
|
||||
return XFS_ISIZE(ip);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
|
@ -458,7 +458,7 @@ xfs_iomap_write_direct(
|
||||
extsz = ip->i_d.di_extsize;
|
||||
}
|
||||
|
||||
isize = ip->i_d.di_size;
|
||||
isize = ip->i_size;
|
||||
if (io->io_new_size > isize)
|
||||
isize = io->io_new_size;
|
||||
|
||||
@ -524,7 +524,7 @@ xfs_iomap_write_direct(
|
||||
xfs_trans_ihold(tp, ip);
|
||||
|
||||
bmapi_flag = XFS_BMAPI_WRITE;
|
||||
if ((flags & BMAPI_DIRECT) && (offset < ip->i_d.di_size || extsz))
|
||||
if ((flags & BMAPI_DIRECT) && (offset < ip->i_size || extsz))
|
||||
bmapi_flag |= XFS_BMAPI_PREALLOC;
|
||||
|
||||
/*
|
||||
@ -676,7 +676,7 @@ xfs_iomap_write_delay(
|
||||
offset_fsb = XFS_B_TO_FSBT(mp, offset);
|
||||
|
||||
retry:
|
||||
isize = ip->i_d.di_size;
|
||||
isize = ip->i_size;
|
||||
if (io->io_new_size > isize)
|
||||
isize = io->io_new_size;
|
||||
|
||||
@ -817,7 +817,7 @@ xfs_iomap_write_allocate(
|
||||
* we dropped the ilock in the interim.
|
||||
*/
|
||||
|
||||
end_fsb = XFS_B_TO_FSB(mp, ip->i_d.di_size);
|
||||
end_fsb = XFS_B_TO_FSB(mp, ip->i_size);
|
||||
xfs_bmap_last_offset(NULL, ip, &last_block,
|
||||
XFS_DATA_FORK);
|
||||
last_block = XFS_FILEOFF_MAX(last_block, end_fsb);
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
|
||||
typedef enum { /* iomap_flags values */
|
||||
IOMAP_READ = 0, /* mapping for a read */
|
||||
IOMAP_EOF = 0x01, /* mapping contains EOF */
|
||||
IOMAP_HOLE = 0x02, /* mapping covers a hole */
|
||||
IOMAP_DELAY = 0x04, /* mapping covers delalloc region */
|
||||
|
@ -133,7 +133,7 @@ xfs_getattr(
|
||||
if (!(flags & ATTR_LAZY))
|
||||
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
||||
|
||||
vap->va_size = ip->i_d.di_size;
|
||||
vap->va_size = XFS_ISIZE(ip);
|
||||
if (vap->va_mask == XFS_AT_SIZE)
|
||||
goto all_done;
|
||||
|
||||
@ -496,7 +496,7 @@ xfs_setattr(
|
||||
if (mask & XFS_AT_SIZE) {
|
||||
/* Short circuit the truncate case for zero length files */
|
||||
if ((vap->va_size == 0) &&
|
||||
(ip->i_d.di_size == 0) && (ip->i_d.di_nextents == 0)) {
|
||||
(ip->i_size == 0) && (ip->i_d.di_nextents == 0)) {
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
lock_flags &= ~XFS_ILOCK_EXCL;
|
||||
if (mask & XFS_AT_CTIME)
|
||||
@ -614,7 +614,7 @@ xfs_setattr(
|
||||
*/
|
||||
if (mask & XFS_AT_SIZE) {
|
||||
code = 0;
|
||||
if ((vap->va_size > ip->i_d.di_size) &&
|
||||
if ((vap->va_size > ip->i_size) &&
|
||||
(flags & ATTR_NOSIZETOK) == 0) {
|
||||
code = xfs_igrow_start(ip, vap->va_size, credp);
|
||||
}
|
||||
@ -654,10 +654,10 @@ xfs_setattr(
|
||||
* Truncate file. Must have write permission and not be a directory.
|
||||
*/
|
||||
if (mask & XFS_AT_SIZE) {
|
||||
if (vap->va_size > ip->i_d.di_size) {
|
||||
if (vap->va_size > ip->i_size) {
|
||||
xfs_igrow_finish(tp, ip, vap->va_size,
|
||||
!(flags & ATTR_DMI));
|
||||
} else if ((vap->va_size <= ip->i_d.di_size) ||
|
||||
} else if ((vap->va_size <= ip->i_size) ||
|
||||
((vap->va_size == 0) && ip->i_d.di_nextents)) {
|
||||
/*
|
||||
* signal a sync transaction unless
|
||||
@ -1221,7 +1221,7 @@ xfs_inactive_free_eofblocks(
|
||||
* Figure out if there are any blocks beyond the end
|
||||
* of the file. If not, then there is nothing to do.
|
||||
*/
|
||||
end_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)ip->i_d.di_size));
|
||||
end_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)ip->i_size));
|
||||
last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
|
||||
map_len = last_fsb - end_fsb;
|
||||
if (map_len <= 0)
|
||||
@ -1258,7 +1258,7 @@ xfs_inactive_free_eofblocks(
|
||||
*/
|
||||
xfs_ilock(ip, XFS_IOLOCK_EXCL);
|
||||
error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE,
|
||||
ip->i_d.di_size);
|
||||
ip->i_size);
|
||||
if (error) {
|
||||
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
||||
return error;
|
||||
@ -1282,7 +1282,7 @@ xfs_inactive_free_eofblocks(
|
||||
xfs_trans_ihold(tp, ip);
|
||||
|
||||
error = xfs_itruncate_finish(&tp, ip,
|
||||
ip->i_d.di_size,
|
||||
ip->i_size,
|
||||
XFS_DATA_FORK,
|
||||
0);
|
||||
/*
|
||||
@ -1568,7 +1568,7 @@ xfs_release(
|
||||
|
||||
if (ip->i_d.di_nlink != 0) {
|
||||
if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
|
||||
((ip->i_d.di_size > 0) || (VN_CACHED(vp) > 0 ||
|
||||
((ip->i_size > 0) || (VN_CACHED(vp) > 0 ||
|
||||
ip->i_delayed_blks > 0)) &&
|
||||
(ip->i_df.if_flags & XFS_IFEXTENTS)) &&
|
||||
(!(ip->i_d.di_flags &
|
||||
@ -1629,8 +1629,8 @@ xfs_inactive(
|
||||
* only one with a reference to the inode.
|
||||
*/
|
||||
truncate = ((ip->i_d.di_nlink == 0) &&
|
||||
((ip->i_d.di_size != 0) || (ip->i_d.di_nextents > 0) ||
|
||||
(ip->i_delayed_blks > 0)) &&
|
||||
((ip->i_d.di_size != 0) || (ip->i_size != 0) ||
|
||||
(ip->i_d.di_nextents > 0) || (ip->i_delayed_blks > 0)) &&
|
||||
((ip->i_d.di_mode & S_IFMT) == S_IFREG));
|
||||
|
||||
mp = ip->i_mount;
|
||||
@ -1648,7 +1648,7 @@ xfs_inactive(
|
||||
|
||||
if (ip->i_d.di_nlink != 0) {
|
||||
if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
|
||||
((ip->i_d.di_size > 0) || (VN_CACHED(vp) > 0 ||
|
||||
((ip->i_size > 0) || (VN_CACHED(vp) > 0 ||
|
||||
ip->i_delayed_blks > 0)) &&
|
||||
(ip->i_df.if_flags & XFS_IFEXTENTS) &&
|
||||
(!(ip->i_d.di_flags &
|
||||
@ -4055,14 +4055,14 @@ xfs_alloc_file_space(
|
||||
allocatesize_fsb = XFS_B_TO_FSB(mp, count);
|
||||
|
||||
/* Generate a DMAPI event if needed. */
|
||||
if (alloc_type != 0 && offset < ip->i_d.di_size &&
|
||||
if (alloc_type != 0 && offset < ip->i_size &&
|
||||
(attr_flags&ATTR_DMI) == 0 &&
|
||||
DM_EVENT_ENABLED(XFS_MTOVFS(mp), ip, DM_EVENT_WRITE)) {
|
||||
xfs_off_t end_dmi_offset;
|
||||
|
||||
end_dmi_offset = offset+len;
|
||||
if (end_dmi_offset > ip->i_d.di_size)
|
||||
end_dmi_offset = ip->i_d.di_size;
|
||||
if (end_dmi_offset > ip->i_size)
|
||||
end_dmi_offset = ip->i_size;
|
||||
error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, XFS_ITOV(ip),
|
||||
offset, end_dmi_offset - offset,
|
||||
0, NULL);
|
||||
@ -4318,11 +4318,11 @@ xfs_free_file_space(
|
||||
end_dmi_offset = offset + len;
|
||||
endoffset_fsb = XFS_B_TO_FSBT(mp, end_dmi_offset);
|
||||
|
||||
if (offset < ip->i_d.di_size &&
|
||||
if (offset < ip->i_size &&
|
||||
(attr_flags & ATTR_DMI) == 0 &&
|
||||
DM_EVENT_ENABLED(XFS_MTOVFS(mp), ip, DM_EVENT_WRITE)) {
|
||||
if (end_dmi_offset > ip->i_d.di_size)
|
||||
end_dmi_offset = ip->i_d.di_size;
|
||||
if (end_dmi_offset > ip->i_size)
|
||||
end_dmi_offset = ip->i_size;
|
||||
error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, vp,
|
||||
offset, end_dmi_offset - offset,
|
||||
AT_DELAY_FLAG(attr_flags), NULL);
|
||||
@ -4541,7 +4541,7 @@ xfs_change_file_space(
|
||||
bf->l_start += offset;
|
||||
break;
|
||||
case 2: /*SEEK_END*/
|
||||
bf->l_start += ip->i_d.di_size;
|
||||
bf->l_start += ip->i_size;
|
||||
break;
|
||||
default:
|
||||
return XFS_ERROR(EINVAL);
|
||||
@ -4558,7 +4558,7 @@ xfs_change_file_space(
|
||||
bf->l_whence = 0;
|
||||
|
||||
startoffset = bf->l_start;
|
||||
fsize = ip->i_d.di_size;
|
||||
fsize = ip->i_size;
|
||||
|
||||
/*
|
||||
* XFS_IOC_RESVSP and XFS_IOC_UNRESVSP will reserve or unreserve
|
||||
|
Loading…
Reference in New Issue
Block a user