#include "xfs_refcache.h"
#include "xfs_trans_space.h"
#include "xfs_log_priv.h"
+#include "xfs_filestream.h"
STATIC int
xfs_open(
return 0;
}
-STATIC int
-xfs_close(
- bhv_desc_t *bdp,
- int flags,
- lastclose_t lastclose,
- cred_t *credp)
-{
- bhv_vnode_t *vp = BHV_TO_VNODE(bdp);
- xfs_inode_t *ip = XFS_BHVTOI(bdp);
-
- if (XFS_FORCED_SHUTDOWN(ip->i_mount))
- return XFS_ERROR(EIO);
-
- if (lastclose != L_TRUE || !VN_ISREG(vp))
- return 0;
-
- /*
- * If we previously truncated this file and removed old data in
- * the process, we want to initiate "early" writeout on the last
- * close. This is an attempt to combat the notorious NULL files
- * problem which is particularly noticable from a truncate down,
- * buffered (re-)write (delalloc), followed by a crash. What we
- * are effectively doing here is significantly reducing the time
- * window where we'd otherwise be exposed to that problem.
- */
- if (VUNTRUNCATE(vp) && VN_DIRTY(vp) && ip->i_delayed_blks > 0)
- return bhv_vop_flush_pages(vp, 0, -1, XFS_B_ASYNC, FI_NONE);
- return 0;
-}
-
/*
* xfs_getattr
*/
code = xfs_igrow_start(ip, vap->va_size, credp);
}
xfs_iunlock(ip, XFS_ILOCK_EXCL);
- vn_iowait(vp); /* wait for the completion of any pending DIOs */
+
+ /*
+ * We are going to log the inode size change in this
+ * transaction so any previous writes that are beyond the on
+ * disk EOF and the new EOF that have not been written out need
+ * to be written here. If we do not write the data out, we
+ * expose ourselves to the null files problem.
+ *
+ * Only flush from the on disk size to the smaller of the in
+ * memory file size or the new size as that's the range we
+ * really care about here and prevents waiting for other data
+ * not within the range we care about here.
+ */
+ if (!code &&
+ (ip->i_size != ip->i_d.di_size) &&
+ (vap->va_size > ip->i_d.di_size)) {
+ code = bhv_vop_flush_pages(XFS_ITOV(ip),
+ ip->i_d.di_size, vap->va_size,
+ XFS_B_ASYNC, FI_NONE);
+ }
+
+ /* wait for all I/O to complete */
+ vn_iowait(vp);
+
if (!code)
code = xfs_itruncate_data(ip, vap->va_size);
if (code) {
di_flags |= XFS_DIFLAG_PROJINHERIT;
if (vap->va_xflags & XFS_XFLAG_NODEFRAG)
di_flags |= XFS_DIFLAG_NODEFRAG;
+ if (vap->va_xflags & XFS_XFLAG_FILESTREAM)
+ di_flags |= XFS_DIFLAG_FILESTREAM;
if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
if (vap->va_xflags & XFS_XFLAG_RTINHERIT)
di_flags |= XFS_DIFLAG_RTINHERIT;
error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE,
ip->i_size);
if (error) {
+ xfs_trans_cancel(tp, 0);
if (use_iolock)
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return error;
if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
return 0;
+ if (!XFS_FORCED_SHUTDOWN(mp)) {
+ /*
+ * If we are using filestreams, and we have an unlinked
+ * file that we are processing the last close on, then nothing
+ * will be able to reopen and write to this file. Purge this
+ * inode from the filestreams cache so that it doesn't delay
+ * teardown of the inode.
+ */
+ if ((ip->i_d.di_nlink == 0) && xfs_inode_is_filestream(ip))
+ xfs_filestream_deassociate(ip);
+
+ /*
+ * If we previously truncated this file and removed old data
+ * in the process, we want to initiate "early" writeout on
+ * the last close. This is an attempt to combat the notorious
+ * NULL files problem which is particularly noticable from a
+ * truncate down, buffered (re-)write (delalloc), followed by
+ * a crash. What we are effectively doing here is
+ * significantly reducing the time window where we'd otherwise
+ * be exposed to that problem.
+ */
+ if (VUNTRUNCATE(vp) && VN_DIRTY(vp) && ip->i_delayed_blks > 0)
+ bhv_vop_flush_pages(vp, 0, -1, XFS_B_ASYNC, FI_NONE);
+ }
+
#ifdef HAVE_REFCACHE
/* If we are in the NFS reference cache then don't do this now */
if (ip->i_refcache)
error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, 0);
if (error) {
+ xfs_trans_cancel(tp, 0);
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return VN_INACTIVE_CACHE;
}
xfs_lock_inumorder(int lock_mode, int subclass)
{
if (lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL))
- lock_mode |= (subclass + XFS_IOLOCK_INUMORDER) << XFS_IOLOCK_SHIFT;
+ lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_IOLOCK_SHIFT;
if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))
- lock_mode |= (subclass + XFS_ILOCK_INUMORDER) << XFS_ILOCK_SHIFT;
+ lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_ILOCK_SHIFT;
return lock_mode;
}
*/
xfs_refcache_purge_ip(ip);
+ /*
+ * If we are using filestreams, kill the stream association.
+ * If the file is still open it may get a new one but that
+ * will get killed on last close in xfs_close() so we don't
+ * have to worry about that.
+ */
+ if (link_zero && xfs_inode_is_filestream(ip))
+ xfs_filestream_deassociate(ip);
+
vn_trace_exit(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address);
/*
while (!error && !done) {
/*
- * allocate and setup the transaction
+ * allocate and setup the transaction. Allow this
+ * transaction to dip into the reserve blocks to ensure
+ * the freeing of the space succeeds at ENOSPC.
*/
tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
+ tp->t_flags |= XFS_TRANS_RESERVE;
error = xfs_trans_reserve(tp,
resblks,
XFS_WRITE_LOG_RES(mp),
bhv_vnodeops_t xfs_vnodeops = {
BHV_IDENTITY_INIT(VN_BHV_XFS,VNODE_POSITION_XFS),
.vop_open = xfs_open,
- .vop_close = xfs_close,
.vop_read = xfs_read,
#ifdef HAVE_SPLICE
.vop_splice_read = xfs_splice_read,