Skip to content

Commit

Permalink
xfs: allow linkat() on O_TMPFILE files
Browse files Browse the repository at this point in the history
The VFS allows an anonymous temporary file to be named at a later
time via a linkat() syscall. The inodes for O_TMPFILE files are
are marked with a special flag I_LINKABLE and have a zero link count.

To support this in XFS, xfs_link() detects if this flag I_LINKABLE
is set and behaves appropriately when detected. So in this case,
its transaciton reservation takes into account the additional
overhead of removing the inode from the unlinked list. Then the
inode is removed from the unlinked list and the directory entry
is added. Finally its link count is bumped accordingly.

Signed-off-by: Zhi Yong Wu <wuzhy@linux.vnet.ibm.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de> 
Signed-off-by: Ben Myers <bpm@sgi.com>
  • Loading branch information
Zhi Yong Wu authored and Ben Myers committed Jan 6, 2014
1 parent 99b6436 commit ab29743
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
10 changes: 9 additions & 1 deletion fs/xfs/xfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ kmem_zone_t *xfs_inode_zone;

STATIC int xfs_iflush_int(xfs_inode_t *, xfs_buf_t *);

STATIC int xfs_iunlink_remove(xfs_trans_t *, xfs_inode_t *);

/*
* helper function to extract extent size hint from inode
*/
Expand Down Expand Up @@ -1118,7 +1120,7 @@ xfs_bumplink(
{
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);

ASSERT(ip->i_d.di_nlink > 0);
ASSERT(ip->i_d.di_nlink > 0 || (VFS_I(ip)->i_state & I_LINKABLE));
ip->i_d.di_nlink++;
inc_nlink(VFS_I(ip));
if ((ip->i_d.di_version == 1) &&
Expand Down Expand Up @@ -1504,6 +1506,12 @@ xfs_link(

xfs_bmap_init(&free_list, &first_block);

if (sip->i_d.di_nlink == 0) {
error = xfs_iunlink_remove(tp, sip);
if (error)
goto abort_return;
}

error = xfs_dir_createname(tp, tdp, target_name, sip->i_ino,
&first_block, &free_list, resblks);
if (error)
Expand Down
19 changes: 17 additions & 2 deletions fs/xfs/xfs_trans_resv.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,20 @@ xfs_calc_rename_reservation(
XFS_FSB_TO_B(mp, 1))));
}

/*
* For removing an inode from unlinked list at first, we can modify:
* the agi hash list and counters: sector size
* the on disk inode before ours in the agi hash list: inode cluster size
*/
STATIC uint
xfs_calc_iunlink_remove_reservation(
struct xfs_mount *mp)
{
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
MAX((__uint16_t)XFS_FSB_TO_B(mp, 1),
(__uint16_t)XFS_INODE_CLUSTER_SIZE(mp));
}

/*
* For creating a link to an inode:
* the parent directory inode: inode size
Expand All @@ -220,6 +234,7 @@ xfs_calc_link_reservation(
struct xfs_mount *mp)
{
return XFS_DQUOT_LOGRES(mp) +
xfs_calc_iunlink_remove_reservation(mp) +
MAX((xfs_calc_inode_res(mp, 2) +
xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
XFS_FSB_TO_B(mp, 1))),
Expand Down Expand Up @@ -410,9 +425,9 @@ xfs_calc_ifree_reservation(
{
return XFS_DQUOT_LOGRES(mp) +
xfs_calc_inode_res(mp, 1) +
xfs_calc_buf_res(2, mp->m_sb.sb_sectsize) +
xfs_calc_buf_res(1, mp->m_sb.sb_sectsize) +
xfs_calc_buf_res(1, XFS_FSB_TO_B(mp, 1)) +
max_t(uint, XFS_FSB_TO_B(mp, 1), XFS_INODE_CLUSTER_SIZE(mp)) +
xfs_calc_iunlink_remove_reservation(mp) +
xfs_calc_buf_res(1, 0) +
xfs_calc_buf_res(2 + XFS_IALLOC_BLOCKS(mp) +
mp->m_in_maxlevels, 0) +
Expand Down

0 comments on commit ab29743

Please sign in to comment.