162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * Copyright (c) 2012-2013 Red Hat, Inc.
562306a36Sopenharmony_ci * All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include "xfs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_fs.h"
1062306a36Sopenharmony_ci#include "xfs_format.h"
1162306a36Sopenharmony_ci#include "xfs_log_format.h"
1262306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1362306a36Sopenharmony_ci#include "xfs_bit.h"
1462306a36Sopenharmony_ci#include "xfs_mount.h"
1562306a36Sopenharmony_ci#include "xfs_dir2.h"
1662306a36Sopenharmony_ci#include "xfs_inode.h"
1762306a36Sopenharmony_ci#include "xfs_bmap.h"
1862306a36Sopenharmony_ci#include "xfs_bmap_btree.h"
1962306a36Sopenharmony_ci#include "xfs_quota.h"
2062306a36Sopenharmony_ci#include "xfs_symlink.h"
2162306a36Sopenharmony_ci#include "xfs_trans_space.h"
2262306a36Sopenharmony_ci#include "xfs_trace.h"
2362306a36Sopenharmony_ci#include "xfs_trans.h"
2462306a36Sopenharmony_ci#include "xfs_ialloc.h"
2562306a36Sopenharmony_ci#include "xfs_error.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* ----- Kernel only functions below ----- */
2862306a36Sopenharmony_ciint
2962306a36Sopenharmony_cixfs_readlink_bmap_ilocked(
3062306a36Sopenharmony_ci	struct xfs_inode	*ip,
3162306a36Sopenharmony_ci	char			*link)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
3462306a36Sopenharmony_ci	struct xfs_bmbt_irec	mval[XFS_SYMLINK_MAPS];
3562306a36Sopenharmony_ci	struct xfs_buf		*bp;
3662306a36Sopenharmony_ci	xfs_daddr_t		d;
3762306a36Sopenharmony_ci	char			*cur_chunk;
3862306a36Sopenharmony_ci	int			pathlen = ip->i_disk_size;
3962306a36Sopenharmony_ci	int			nmaps = XFS_SYMLINK_MAPS;
4062306a36Sopenharmony_ci	int			byte_cnt;
4162306a36Sopenharmony_ci	int			n;
4262306a36Sopenharmony_ci	int			error = 0;
4362306a36Sopenharmony_ci	int			fsblocks = 0;
4462306a36Sopenharmony_ci	int			offset;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	fsblocks = xfs_symlink_blocks(mp, pathlen);
4962306a36Sopenharmony_ci	error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0);
5062306a36Sopenharmony_ci	if (error)
5162306a36Sopenharmony_ci		goto out;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	offset = 0;
5462306a36Sopenharmony_ci	for (n = 0; n < nmaps; n++) {
5562306a36Sopenharmony_ci		d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
5662306a36Sopenharmony_ci		byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		error = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0,
5962306a36Sopenharmony_ci				&bp, &xfs_symlink_buf_ops);
6062306a36Sopenharmony_ci		if (error)
6162306a36Sopenharmony_ci			return error;
6262306a36Sopenharmony_ci		byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
6362306a36Sopenharmony_ci		if (pathlen < byte_cnt)
6462306a36Sopenharmony_ci			byte_cnt = pathlen;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		cur_chunk = bp->b_addr;
6762306a36Sopenharmony_ci		if (xfs_has_crc(mp)) {
6862306a36Sopenharmony_ci			if (!xfs_symlink_hdr_ok(ip->i_ino, offset,
6962306a36Sopenharmony_ci							byte_cnt, bp)) {
7062306a36Sopenharmony_ci				error = -EFSCORRUPTED;
7162306a36Sopenharmony_ci				xfs_alert(mp,
7262306a36Sopenharmony_ci"symlink header does not match required off/len/owner (0x%x/Ox%x,0x%llx)",
7362306a36Sopenharmony_ci					offset, byte_cnt, ip->i_ino);
7462306a36Sopenharmony_ci				xfs_buf_relse(bp);
7562306a36Sopenharmony_ci				goto out;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci			}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci			cur_chunk += sizeof(struct xfs_dsymlink_hdr);
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		memcpy(link + offset, cur_chunk, byte_cnt);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		pathlen -= byte_cnt;
8562306a36Sopenharmony_ci		offset += byte_cnt;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		xfs_buf_relse(bp);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	ASSERT(pathlen == 0);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	link[ip->i_disk_size] = '\0';
9262306a36Sopenharmony_ci	error = 0;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci out:
9562306a36Sopenharmony_ci	return error;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ciint
9962306a36Sopenharmony_cixfs_readlink(
10062306a36Sopenharmony_ci	struct xfs_inode	*ip,
10162306a36Sopenharmony_ci	char			*link)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
10462306a36Sopenharmony_ci	xfs_fsize_t		pathlen;
10562306a36Sopenharmony_ci	int			error = -EFSCORRUPTED;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	trace_xfs_readlink(ip);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (xfs_is_shutdown(mp))
11062306a36Sopenharmony_ci		return -EIO;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	xfs_ilock(ip, XFS_ILOCK_SHARED);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	pathlen = ip->i_disk_size;
11562306a36Sopenharmony_ci	if (!pathlen)
11662306a36Sopenharmony_ci		goto out;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (pathlen < 0 || pathlen > XFS_SYMLINK_MAXLEN) {
11962306a36Sopenharmony_ci		xfs_alert(mp, "%s: inode (%llu) bad symlink length (%lld)",
12062306a36Sopenharmony_ci			 __func__, (unsigned long long) ip->i_ino,
12162306a36Sopenharmony_ci			 (long long) pathlen);
12262306a36Sopenharmony_ci		ASSERT(0);
12362306a36Sopenharmony_ci		goto out;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
12762306a36Sopenharmony_ci		/*
12862306a36Sopenharmony_ci		 * The VFS crashes on a NULL pointer, so return -EFSCORRUPTED
12962306a36Sopenharmony_ci		 * if if_data is junk.
13062306a36Sopenharmony_ci		 */
13162306a36Sopenharmony_ci		if (XFS_IS_CORRUPT(ip->i_mount, !ip->i_df.if_u1.if_data))
13262306a36Sopenharmony_ci			goto out;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		memcpy(link, ip->i_df.if_u1.if_data, pathlen + 1);
13562306a36Sopenharmony_ci		error = 0;
13662306a36Sopenharmony_ci	} else {
13762306a36Sopenharmony_ci		error = xfs_readlink_bmap_ilocked(ip, link);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci out:
14162306a36Sopenharmony_ci	xfs_iunlock(ip, XFS_ILOCK_SHARED);
14262306a36Sopenharmony_ci	return error;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciint
14662306a36Sopenharmony_cixfs_symlink(
14762306a36Sopenharmony_ci	struct mnt_idmap	*idmap,
14862306a36Sopenharmony_ci	struct xfs_inode	*dp,
14962306a36Sopenharmony_ci	struct xfs_name		*link_name,
15062306a36Sopenharmony_ci	const char		*target_path,
15162306a36Sopenharmony_ci	umode_t			mode,
15262306a36Sopenharmony_ci	struct xfs_inode	**ipp)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct xfs_mount	*mp = dp->i_mount;
15562306a36Sopenharmony_ci	struct xfs_trans	*tp = NULL;
15662306a36Sopenharmony_ci	struct xfs_inode	*ip = NULL;
15762306a36Sopenharmony_ci	int			error = 0;
15862306a36Sopenharmony_ci	int			pathlen;
15962306a36Sopenharmony_ci	bool                    unlock_dp_on_error = false;
16062306a36Sopenharmony_ci	xfs_fileoff_t		first_fsb;
16162306a36Sopenharmony_ci	xfs_filblks_t		fs_blocks;
16262306a36Sopenharmony_ci	int			nmaps;
16362306a36Sopenharmony_ci	struct xfs_bmbt_irec	mval[XFS_SYMLINK_MAPS];
16462306a36Sopenharmony_ci	xfs_daddr_t		d;
16562306a36Sopenharmony_ci	const char		*cur_chunk;
16662306a36Sopenharmony_ci	int			byte_cnt;
16762306a36Sopenharmony_ci	int			n;
16862306a36Sopenharmony_ci	struct xfs_buf		*bp;
16962306a36Sopenharmony_ci	prid_t			prid;
17062306a36Sopenharmony_ci	struct xfs_dquot	*udqp = NULL;
17162306a36Sopenharmony_ci	struct xfs_dquot	*gdqp = NULL;
17262306a36Sopenharmony_ci	struct xfs_dquot	*pdqp = NULL;
17362306a36Sopenharmony_ci	uint			resblks;
17462306a36Sopenharmony_ci	xfs_ino_t		ino;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	*ipp = NULL;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	trace_xfs_symlink(dp, link_name);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (xfs_is_shutdown(mp))
18162306a36Sopenharmony_ci		return -EIO;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * Check component lengths of the target path name.
18562306a36Sopenharmony_ci	 */
18662306a36Sopenharmony_ci	pathlen = strlen(target_path);
18762306a36Sopenharmony_ci	if (pathlen >= XFS_SYMLINK_MAXLEN)      /* total string too long */
18862306a36Sopenharmony_ci		return -ENAMETOOLONG;
18962306a36Sopenharmony_ci	ASSERT(pathlen > 0);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	prid = xfs_get_initial_prid(dp);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/*
19462306a36Sopenharmony_ci	 * Make sure that we have allocated dquot(s) on disk.
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	error = xfs_qm_vop_dqalloc(dp, mapped_fsuid(idmap, &init_user_ns),
19762306a36Sopenharmony_ci			mapped_fsgid(idmap, &init_user_ns), prid,
19862306a36Sopenharmony_ci			XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
19962306a36Sopenharmony_ci			&udqp, &gdqp, &pdqp);
20062306a36Sopenharmony_ci	if (error)
20162306a36Sopenharmony_ci		return error;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/*
20462306a36Sopenharmony_ci	 * The symlink will fit into the inode data fork?
20562306a36Sopenharmony_ci	 * There can't be any attributes so we get the whole variable part.
20662306a36Sopenharmony_ci	 */
20762306a36Sopenharmony_ci	if (pathlen <= XFS_LITINO(mp))
20862306a36Sopenharmony_ci		fs_blocks = 0;
20962306a36Sopenharmony_ci	else
21062306a36Sopenharmony_ci		fs_blocks = xfs_symlink_blocks(mp, pathlen);
21162306a36Sopenharmony_ci	resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	error = xfs_trans_alloc_icreate(mp, &M_RES(mp)->tr_symlink, udqp, gdqp,
21462306a36Sopenharmony_ci			pdqp, resblks, &tp);
21562306a36Sopenharmony_ci	if (error)
21662306a36Sopenharmony_ci		goto out_release_dquots;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
21962306a36Sopenharmony_ci	unlock_dp_on_error = true;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/*
22262306a36Sopenharmony_ci	 * Check whether the directory allows new symlinks or not.
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	if (dp->i_diflags & XFS_DIFLAG_NOSYMLINKS) {
22562306a36Sopenharmony_ci		error = -EPERM;
22662306a36Sopenharmony_ci		goto out_trans_cancel;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Allocate an inode for the symlink.
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	error = xfs_dialloc(&tp, dp->i_ino, S_IFLNK, &ino);
23362306a36Sopenharmony_ci	if (!error)
23462306a36Sopenharmony_ci		error = xfs_init_new_inode(idmap, tp, dp, ino,
23562306a36Sopenharmony_ci				S_IFLNK | (mode & ~S_IFMT), 1, 0, prid,
23662306a36Sopenharmony_ci				false, &ip);
23762306a36Sopenharmony_ci	if (error)
23862306a36Sopenharmony_ci		goto out_trans_cancel;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/*
24162306a36Sopenharmony_ci	 * Now we join the directory inode to the transaction.  We do not do it
24262306a36Sopenharmony_ci	 * earlier because xfs_dir_ialloc might commit the previous transaction
24362306a36Sopenharmony_ci	 * (and release all the locks).  An error from here on will result in
24462306a36Sopenharmony_ci	 * the transaction cancel unlocking dp so don't do it explicitly in the
24562306a36Sopenharmony_ci	 * error path.
24662306a36Sopenharmony_ci	 */
24762306a36Sopenharmony_ci	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
24862306a36Sopenharmony_ci	unlock_dp_on_error = false;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/*
25162306a36Sopenharmony_ci	 * Also attach the dquot(s) to it, if applicable.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	resblks -= XFS_IALLOC_SPACE_RES(mp);
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * If the symlink will fit into the inode, write it inline.
25862306a36Sopenharmony_ci	 */
25962306a36Sopenharmony_ci	if (pathlen <= xfs_inode_data_fork_size(ip)) {
26062306a36Sopenharmony_ci		xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, pathlen);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		ip->i_disk_size = pathlen;
26362306a36Sopenharmony_ci		ip->i_df.if_format = XFS_DINODE_FMT_LOCAL;
26462306a36Sopenharmony_ci		xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
26562306a36Sopenharmony_ci	} else {
26662306a36Sopenharmony_ci		int	offset;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		first_fsb = 0;
26962306a36Sopenharmony_ci		nmaps = XFS_SYMLINK_MAPS;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		error = xfs_bmapi_write(tp, ip, first_fsb, fs_blocks,
27262306a36Sopenharmony_ci				  XFS_BMAPI_METADATA, resblks, mval, &nmaps);
27362306a36Sopenharmony_ci		if (error)
27462306a36Sopenharmony_ci			goto out_trans_cancel;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		resblks -= fs_blocks;
27762306a36Sopenharmony_ci		ip->i_disk_size = pathlen;
27862306a36Sopenharmony_ci		xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		cur_chunk = target_path;
28162306a36Sopenharmony_ci		offset = 0;
28262306a36Sopenharmony_ci		for (n = 0; n < nmaps; n++) {
28362306a36Sopenharmony_ci			char	*buf;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci			d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
28662306a36Sopenharmony_ci			byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
28762306a36Sopenharmony_ci			error = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
28862306a36Sopenharmony_ci					       BTOBB(byte_cnt), 0, &bp);
28962306a36Sopenharmony_ci			if (error)
29062306a36Sopenharmony_ci				goto out_trans_cancel;
29162306a36Sopenharmony_ci			bp->b_ops = &xfs_symlink_buf_ops;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci			byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
29462306a36Sopenharmony_ci			byte_cnt = min(byte_cnt, pathlen);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci			buf = bp->b_addr;
29762306a36Sopenharmony_ci			buf += xfs_symlink_hdr_set(mp, ip->i_ino, offset,
29862306a36Sopenharmony_ci						   byte_cnt, bp);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci			memcpy(buf, cur_chunk, byte_cnt);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci			cur_chunk += byte_cnt;
30362306a36Sopenharmony_ci			pathlen -= byte_cnt;
30462306a36Sopenharmony_ci			offset += byte_cnt;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci			xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF);
30762306a36Sopenharmony_ci			xfs_trans_log_buf(tp, bp, 0, (buf + byte_cnt - 1) -
30862306a36Sopenharmony_ci							(char *)bp->b_addr);
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci		ASSERT(pathlen == 0);
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci	i_size_write(VFS_I(ip), ip->i_disk_size);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/*
31562306a36Sopenharmony_ci	 * Create the directory entry for the symlink.
31662306a36Sopenharmony_ci	 */
31762306a36Sopenharmony_ci	error = xfs_dir_createname(tp, dp, link_name, ip->i_ino, resblks);
31862306a36Sopenharmony_ci	if (error)
31962306a36Sopenharmony_ci		goto out_trans_cancel;
32062306a36Sopenharmony_ci	xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
32162306a36Sopenharmony_ci	xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	/*
32462306a36Sopenharmony_ci	 * If this is a synchronous mount, make sure that the
32562306a36Sopenharmony_ci	 * symlink transaction goes to disk before returning to
32662306a36Sopenharmony_ci	 * the user.
32762306a36Sopenharmony_ci	 */
32862306a36Sopenharmony_ci	if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))
32962306a36Sopenharmony_ci		xfs_trans_set_sync(tp);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	error = xfs_trans_commit(tp);
33262306a36Sopenharmony_ci	if (error)
33362306a36Sopenharmony_ci		goto out_release_inode;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	xfs_qm_dqrele(udqp);
33662306a36Sopenharmony_ci	xfs_qm_dqrele(gdqp);
33762306a36Sopenharmony_ci	xfs_qm_dqrele(pdqp);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	*ipp = ip;
34062306a36Sopenharmony_ci	return 0;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ciout_trans_cancel:
34362306a36Sopenharmony_ci	xfs_trans_cancel(tp);
34462306a36Sopenharmony_ciout_release_inode:
34562306a36Sopenharmony_ci	/*
34662306a36Sopenharmony_ci	 * Wait until after the current transaction is aborted to finish the
34762306a36Sopenharmony_ci	 * setup of the inode and release the inode.  This prevents recursive
34862306a36Sopenharmony_ci	 * transactions and deadlocks from xfs_inactive.
34962306a36Sopenharmony_ci	 */
35062306a36Sopenharmony_ci	if (ip) {
35162306a36Sopenharmony_ci		xfs_finish_inode_setup(ip);
35262306a36Sopenharmony_ci		xfs_irele(ip);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ciout_release_dquots:
35562306a36Sopenharmony_ci	xfs_qm_dqrele(udqp);
35662306a36Sopenharmony_ci	xfs_qm_dqrele(gdqp);
35762306a36Sopenharmony_ci	xfs_qm_dqrele(pdqp);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (unlock_dp_on_error)
36062306a36Sopenharmony_ci		xfs_iunlock(dp, XFS_ILOCK_EXCL);
36162306a36Sopenharmony_ci	return error;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/*
36562306a36Sopenharmony_ci * Free a symlink that has blocks associated with it.
36662306a36Sopenharmony_ci *
36762306a36Sopenharmony_ci * Note: zero length symlinks are not allowed to exist. When we set the size to
36862306a36Sopenharmony_ci * zero, also change it to a regular file so that it does not get written to
36962306a36Sopenharmony_ci * disk as a zero length symlink. The inode is on the unlinked list already, so
37062306a36Sopenharmony_ci * userspace cannot find this inode anymore, so this change is not user visible
37162306a36Sopenharmony_ci * but allows us to catch corrupt zero-length symlinks in the verifiers.
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_ciSTATIC int
37462306a36Sopenharmony_cixfs_inactive_symlink_rmt(
37562306a36Sopenharmony_ci	struct xfs_inode *ip)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct xfs_buf	*bp;
37862306a36Sopenharmony_ci	int		done;
37962306a36Sopenharmony_ci	int		error;
38062306a36Sopenharmony_ci	int		i;
38162306a36Sopenharmony_ci	xfs_mount_t	*mp;
38262306a36Sopenharmony_ci	xfs_bmbt_irec_t	mval[XFS_SYMLINK_MAPS];
38362306a36Sopenharmony_ci	int		nmaps;
38462306a36Sopenharmony_ci	int		size;
38562306a36Sopenharmony_ci	xfs_trans_t	*tp;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	mp = ip->i_mount;
38862306a36Sopenharmony_ci	ASSERT(!xfs_need_iread_extents(&ip->i_df));
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * We're freeing a symlink that has some
39162306a36Sopenharmony_ci	 * blocks allocated to it.  Free the
39262306a36Sopenharmony_ci	 * blocks here.  We know that we've got
39362306a36Sopenharmony_ci	 * either 1 or 2 extents and that we can
39462306a36Sopenharmony_ci	 * free them all in one bunmapi call.
39562306a36Sopenharmony_ci	 */
39662306a36Sopenharmony_ci	ASSERT(ip->i_df.if_nextents > 0 && ip->i_df.if_nextents <= 2);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
39962306a36Sopenharmony_ci	if (error)
40062306a36Sopenharmony_ci		return error;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	xfs_ilock(ip, XFS_ILOCK_EXCL);
40362306a36Sopenharmony_ci	xfs_trans_ijoin(tp, ip, 0);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/*
40662306a36Sopenharmony_ci	 * Lock the inode, fix the size, turn it into a regular file and join it
40762306a36Sopenharmony_ci	 * to the transaction.  Hold it so in the normal path, we still have it
40862306a36Sopenharmony_ci	 * locked for the second transaction.  In the error paths we need it
40962306a36Sopenharmony_ci	 * held so the cancel won't rele it, see below.
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci	size = (int)ip->i_disk_size;
41262306a36Sopenharmony_ci	ip->i_disk_size = 0;
41362306a36Sopenharmony_ci	VFS_I(ip)->i_mode = (VFS_I(ip)->i_mode & ~S_IFMT) | S_IFREG;
41462306a36Sopenharmony_ci	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
41562306a36Sopenharmony_ci	/*
41662306a36Sopenharmony_ci	 * Find the block(s) so we can inval and unmap them.
41762306a36Sopenharmony_ci	 */
41862306a36Sopenharmony_ci	done = 0;
41962306a36Sopenharmony_ci	nmaps = ARRAY_SIZE(mval);
42062306a36Sopenharmony_ci	error = xfs_bmapi_read(ip, 0, xfs_symlink_blocks(mp, size),
42162306a36Sopenharmony_ci				mval, &nmaps, 0);
42262306a36Sopenharmony_ci	if (error)
42362306a36Sopenharmony_ci		goto error_trans_cancel;
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * Invalidate the block(s). No validation is done.
42662306a36Sopenharmony_ci	 */
42762306a36Sopenharmony_ci	for (i = 0; i < nmaps; i++) {
42862306a36Sopenharmony_ci		error = xfs_trans_get_buf(tp, mp->m_ddev_targp,
42962306a36Sopenharmony_ci				XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
43062306a36Sopenharmony_ci				XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0,
43162306a36Sopenharmony_ci				&bp);
43262306a36Sopenharmony_ci		if (error)
43362306a36Sopenharmony_ci			goto error_trans_cancel;
43462306a36Sopenharmony_ci		xfs_trans_binval(tp, bp);
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci	/*
43762306a36Sopenharmony_ci	 * Unmap the dead block(s) to the dfops.
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci	error = xfs_bunmapi(tp, ip, 0, size, 0, nmaps, &done);
44062306a36Sopenharmony_ci	if (error)
44162306a36Sopenharmony_ci		goto error_trans_cancel;
44262306a36Sopenharmony_ci	ASSERT(done);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/*
44562306a36Sopenharmony_ci	 * Commit the transaction. This first logs the EFI and the inode, then
44662306a36Sopenharmony_ci	 * rolls and commits the transaction that frees the extents.
44762306a36Sopenharmony_ci	 */
44862306a36Sopenharmony_ci	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
44962306a36Sopenharmony_ci	error = xfs_trans_commit(tp);
45062306a36Sopenharmony_ci	if (error) {
45162306a36Sopenharmony_ci		ASSERT(xfs_is_shutdown(mp));
45262306a36Sopenharmony_ci		goto error_unlock;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/*
45662306a36Sopenharmony_ci	 * Remove the memory for extent descriptions (just bookkeeping).
45762306a36Sopenharmony_ci	 */
45862306a36Sopenharmony_ci	if (ip->i_df.if_bytes)
45962306a36Sopenharmony_ci		xfs_idata_realloc(ip, -ip->i_df.if_bytes, XFS_DATA_FORK);
46062306a36Sopenharmony_ci	ASSERT(ip->i_df.if_bytes == 0);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	xfs_iunlock(ip, XFS_ILOCK_EXCL);
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cierror_trans_cancel:
46662306a36Sopenharmony_ci	xfs_trans_cancel(tp);
46762306a36Sopenharmony_cierror_unlock:
46862306a36Sopenharmony_ci	xfs_iunlock(ip, XFS_ILOCK_EXCL);
46962306a36Sopenharmony_ci	return error;
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci/*
47362306a36Sopenharmony_ci * xfs_inactive_symlink - free a symlink
47462306a36Sopenharmony_ci */
47562306a36Sopenharmony_ciint
47662306a36Sopenharmony_cixfs_inactive_symlink(
47762306a36Sopenharmony_ci	struct xfs_inode	*ip)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
48062306a36Sopenharmony_ci	int			pathlen;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	trace_xfs_inactive_symlink(ip);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (xfs_is_shutdown(mp))
48562306a36Sopenharmony_ci		return -EIO;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	xfs_ilock(ip, XFS_ILOCK_EXCL);
48862306a36Sopenharmony_ci	pathlen = (int)ip->i_disk_size;
48962306a36Sopenharmony_ci	ASSERT(pathlen);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (pathlen <= 0 || pathlen > XFS_SYMLINK_MAXLEN) {
49262306a36Sopenharmony_ci		xfs_alert(mp, "%s: inode (0x%llx) bad symlink length (%d)",
49362306a36Sopenharmony_ci			 __func__, (unsigned long long)ip->i_ino, pathlen);
49462306a36Sopenharmony_ci		xfs_iunlock(ip, XFS_ILOCK_EXCL);
49562306a36Sopenharmony_ci		ASSERT(0);
49662306a36Sopenharmony_ci		return -EFSCORRUPTED;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/*
50062306a36Sopenharmony_ci	 * Inline fork state gets removed by xfs_difree() so we have nothing to
50162306a36Sopenharmony_ci	 * do here in that case.
50262306a36Sopenharmony_ci	 */
50362306a36Sopenharmony_ci	if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
50462306a36Sopenharmony_ci		xfs_iunlock(ip, XFS_ILOCK_EXCL);
50562306a36Sopenharmony_ci		return 0;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	xfs_iunlock(ip, XFS_ILOCK_EXCL);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* remove the remote symlink */
51162306a36Sopenharmony_ci	return xfs_inactive_symlink_rmt(ip);
51262306a36Sopenharmony_ci}
513