Commit 517b5e8c authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Christoph Hellwig
Browse files

xfs: merge xfs_mkdir into xfs_create



xfs_create and xfs_mkdir only have minor differences, so merge both of them
into a sigle function.  While we're at it also make the error handling code
more straight-forward.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Dave Chinner <david@fromorbit.com>
parent a5687787
Loading
Loading
Loading
Loading
+9 −21
Original line number Diff line number Diff line
@@ -211,8 +211,13 @@ xfs_vn_mknod(
	 * Irix uses Missed'em'V split, but doesn't want to see
	 * the upper 5 bits of (14bit) major.
	 */
	if (S_ISCHR(mode) || S_ISBLK(mode)) {
		if (unlikely(!sysv_valid_dev(rdev) || MAJOR(rdev) & ~0x1ff))
			return -EINVAL;
		rdev = sysv_encode_dev(rdev);
	} else {
		rdev = 0;
	}

	if (test_default_acl && test_default_acl(dir)) {
		if (!_ACL_ALLOC(default_acl)) {
@@ -224,28 +229,11 @@ xfs_vn_mknod(
		}
	}

	xfs_dentry_to_name(&name, dentry);

	if (IS_POSIXACL(dir) && !default_acl)
		mode &= ~current->fs->umask;

	switch (mode & S_IFMT) {
	case S_IFCHR:
	case S_IFBLK:
	case S_IFIFO:
	case S_IFSOCK:
		rdev = sysv_encode_dev(rdev);
	case S_IFREG:
	xfs_dentry_to_name(&name, dentry);
	error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL);
		break;
	case S_IFDIR:
		error = xfs_mkdir(XFS_I(dir), &name, mode, &ip, NULL);
		break;
	default:
		error = EINVAL;
		break;
	}

	if (unlikely(error))
		goto out_free_acl;

+83 −268
Original line number Diff line number Diff line
@@ -1387,23 +1387,28 @@ xfs_create(
	xfs_inode_t		**ipp,
	cred_t			*credp)
{
	xfs_mount_t		*mp = dp->i_mount;
	xfs_inode_t		*ip;
	xfs_trans_t		*tp;
	int			is_dir = S_ISDIR(mode);
	struct xfs_mount	*mp = dp->i_mount;
	struct xfs_inode	*ip = NULL;
	struct xfs_trans	*tp = NULL;
	int			error;
	xfs_bmap_free_t		free_list;
	xfs_fsblock_t		first_block;
	boolean_t		unlock_dp_on_error = B_FALSE;
	int			dm_event_sent = 0;
	uint			cancel_flags;
	int			committed;
	xfs_prid_t		prid;
	struct xfs_dquot	*udqp, *gdqp;
	struct xfs_dquot	*udqp = NULL;
	struct xfs_dquot	*gdqp = NULL;
	uint			resblks;
	uint			log_res;
	uint			log_count;

	ASSERT(!*ipp);
	xfs_itrace_entry(dp);

	if (XFS_FORCED_SHUTDOWN(mp))
		return XFS_ERROR(EIO);

	if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) {
		error = XFS_SEND_NAMESP(mp, DM_EVENT_CREATE,
				dp, DM_RIGHT_NULL, NULL,
@@ -1412,19 +1417,12 @@ xfs_create(

		if (error)
			return error;
		dm_event_sent = 1;
	}

	if (XFS_FORCED_SHUTDOWN(mp))
		return XFS_ERROR(EIO);

	/* Return through std_return after this point. */

	udqp = gdqp = NULL;
	if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
		prid = dp->i_d.di_projid;
	else
		prid = (xfs_prid_t)dfltprid;
		prid = dfltprid;

	/*
	 * Make sure that we have allocated dquot(s) on disk.
@@ -1435,61 +1433,81 @@ xfs_create(
	if (error)
		goto std_return;

	ip = NULL;

	if (is_dir) {
		rdev = 0;
		resblks = XFS_MKDIR_SPACE_RES(mp, name->len);
		log_res = XFS_MKDIR_LOG_RES(mp);
		log_count = XFS_MKDIR_LOG_COUNT;
		tp = xfs_trans_alloc(mp, XFS_TRANS_MKDIR);
	} else {
		resblks = XFS_CREATE_SPACE_RES(mp, name->len);
		log_res = XFS_CREATE_LOG_RES(mp);
		log_count = XFS_CREATE_LOG_COUNT;
		tp = xfs_trans_alloc(mp, XFS_TRANS_CREATE);
	}

	cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
	resblks = XFS_CREATE_SPACE_RES(mp, name->len);

	/*
	 * Initially assume that the file does not exist and
	 * reserve the resources for that case.  If that is not
	 * the case we'll drop the one we have and get a more
	 * appropriate transaction later.
	 */
	error = xfs_trans_reserve(tp, resblks, XFS_CREATE_LOG_RES(mp), 0,
			XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
	error = xfs_trans_reserve(tp, resblks, log_res, 0,
			XFS_TRANS_PERM_LOG_RES, log_count);
	if (error == ENOSPC) {
		resblks = 0;
		error = xfs_trans_reserve(tp, 0, XFS_CREATE_LOG_RES(mp), 0,
				XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
		error = xfs_trans_reserve(tp, 0, log_res, 0,
				XFS_TRANS_PERM_LOG_RES, log_count);
	}
	if (error) {
		cancel_flags = 0;
		goto error_return;
		goto out_trans_cancel;
	}

	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
	unlock_dp_on_error = B_TRUE;

	xfs_bmap_init(&free_list, &first_block);
	/*
	 * Check for directory link count overflow.
	 */
	if (is_dir && dp->i_d.di_nlink >= XFS_MAXLINK) {
		error = XFS_ERROR(EMLINK);
		goto out_trans_cancel;
	}

	ASSERT(ip == NULL);
	xfs_bmap_init(&free_list, &first_block);

	/*
	 * Reserve disk quota and the inode.
	 */
	error = XFS_TRANS_RESERVE_QUOTA(mp, tp, udqp, gdqp, resblks, 1, 0);
	if (error)
		goto error_return;
		goto out_trans_cancel;

	error = xfs_dir_canenter(tp, dp, name, resblks);
	if (error)
		goto error_return;
	error = xfs_dir_ialloc(&tp, dp, mode, 1,
			rdev, credp, prid, resblks > 0,
			&ip, &committed);
		goto out_trans_cancel;

	/*
	 * A newly created regular or special file just has one directory
	 * entry pointing to them, but a directory also the "." entry
	 * pointing to itself.
	 */
	error = xfs_dir_ialloc(&tp, dp, mode, is_dir ? 2 : 1, rdev, credp,
			       prid, resblks > 0, &ip, &committed);
	if (error) {
		if (error == ENOSPC)
			goto error_return;
		goto abort_return;
			goto out_trans_cancel;
		goto out_trans_abort;
	}
	xfs_itrace_ref(ip);

	/*
	 * At this point, we've gotten a newly allocated inode.
	 * It is locked (and joined to the transaction).
	 */

	xfs_itrace_ref(ip);
	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));

	/*
@@ -1508,19 +1526,28 @@ xfs_create(
					resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
	if (error) {
		ASSERT(error != ENOSPC);
		goto abort_return;
		goto out_trans_abort;
	}
	xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
	xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);

	if (is_dir) {
		error = xfs_dir_init(tp, ip, dp);
		if (error)
			goto out_bmap_cancel;

		error = xfs_bumplink(tp, dp);
		if (error)
			goto out_bmap_cancel;
	}

	/*
	 * If this is a synchronous mount, make sure that the
	 * create transaction goes to disk before returning to
	 * the user.
	 */
	if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) {
	if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC))
		xfs_trans_set_sync(tp);
	}

	/*
	 * Attach the dquot(s) to the inodes and modify them incore.
@@ -1537,16 +1564,13 @@ xfs_create(
	IHOLD(ip);

	error = xfs_bmap_finish(&tp, &free_list, &committed);
	if (error) {
		xfs_bmap_cancel(&free_list);
		goto abort_rele;
	}
	if (error)
		goto out_abort_rele;

	error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
	if (error) {
		IRELE(ip);
		tp = NULL;
		goto error_return;
		goto out_dqrele;
	}

	XFS_QM_DQRELE(mp, udqp);
@@ -1555,26 +1579,22 @@ xfs_create(
	*ipp = ip;

	/* Fallthrough to std_return with error = 0  */

 std_return:
	if ((*ipp || (error != 0 && dm_event_sent != 0)) &&
	    DM_EVENT_ENABLED(dp, DM_EVENT_POSTCREATE)) {
		(void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTCREATE,
			dp, DM_RIGHT_NULL,
			*ipp ? ip : NULL,
			DM_RIGHT_NULL, name->name, NULL,
			mode, error, 0);
	if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTCREATE)) {
		XFS_SEND_NAMESP(mp, DM_EVENT_POSTCREATE, dp, DM_RIGHT_NULL,
				ip, DM_RIGHT_NULL, name->name, NULL, mode,
				error, 0);
	}

	return error;

 abort_return:
 out_bmap_cancel:
	xfs_bmap_cancel(&free_list);
 out_trans_abort:
	cancel_flags |= XFS_TRANS_ABORT;
	/* FALLTHROUGH */

 error_return:
	if (tp != NULL)
 out_trans_cancel:
	xfs_trans_cancel(tp, cancel_flags);

 out_dqrele:
	XFS_QM_DQRELE(mp, udqp);
	XFS_QM_DQRELE(mp, gdqp);

@@ -1583,20 +1603,18 @@ std_return:

	goto std_return;

 abort_rele:
 out_abort_rele:
	/*
	 * Wait until after the current transaction is aborted to
	 * release the inode.  This prevents recursive transactions
	 * and deadlocks from xfs_inactive.
	 */
	xfs_bmap_cancel(&free_list);
	cancel_flags |= XFS_TRANS_ABORT;
	xfs_trans_cancel(tp, cancel_flags);
	IRELE(ip);

	XFS_QM_DQRELE(mp, udqp);
	XFS_QM_DQRELE(mp, gdqp);

	goto std_return;
	unlock_dp_on_error = B_FALSE;
	goto out_dqrele;
}

#ifdef DEBUG
@@ -2112,209 +2130,6 @@ std_return:
	goto std_return;
}


int
xfs_mkdir(
	xfs_inode_t             *dp,
	struct xfs_name		*dir_name,
	mode_t			mode,
	xfs_inode_t		**ipp,
	cred_t			*credp)
{
	xfs_mount_t		*mp = dp->i_mount;
	xfs_inode_t		*cdp;	/* inode of created dir */
	xfs_trans_t		*tp;
	int			cancel_flags;
	int			error;
	int			committed;
	xfs_bmap_free_t         free_list;
	xfs_fsblock_t           first_block;
	boolean_t		unlock_dp_on_error = B_FALSE;
	boolean_t		created = B_FALSE;
	int			dm_event_sent = 0;
	xfs_prid_t		prid;
	struct xfs_dquot	*udqp, *gdqp;
	uint			resblks;

	if (XFS_FORCED_SHUTDOWN(mp))
		return XFS_ERROR(EIO);

	tp = NULL;

	if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) {
		error = XFS_SEND_NAMESP(mp, DM_EVENT_CREATE,
					dp, DM_RIGHT_NULL, NULL,
					DM_RIGHT_NULL, dir_name->name, NULL,
					mode, 0, 0);
		if (error)
			return error;
		dm_event_sent = 1;
	}

	/* Return through std_return after this point. */

	xfs_itrace_entry(dp);

	mp = dp->i_mount;
	udqp = gdqp = NULL;
	if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
		prid = dp->i_d.di_projid;
	else
		prid = (xfs_prid_t)dfltprid;

	/*
	 * Make sure that we have allocated dquot(s) on disk.
	 */
	error = XFS_QM_DQVOPALLOC(mp, dp,
			current_fsuid(), current_fsgid(), prid,
			XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
	if (error)
		goto std_return;

	tp = xfs_trans_alloc(mp, XFS_TRANS_MKDIR);
	cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
	resblks = XFS_MKDIR_SPACE_RES(mp, dir_name->len);
	error = xfs_trans_reserve(tp, resblks, XFS_MKDIR_LOG_RES(mp), 0,
				  XFS_TRANS_PERM_LOG_RES, XFS_MKDIR_LOG_COUNT);
	if (error == ENOSPC) {
		resblks = 0;
		error = xfs_trans_reserve(tp, 0, XFS_MKDIR_LOG_RES(mp), 0,
					  XFS_TRANS_PERM_LOG_RES,
					  XFS_MKDIR_LOG_COUNT);
	}
	if (error) {
		cancel_flags = 0;
		goto error_return;
	}

	xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
	unlock_dp_on_error = B_TRUE;

	/*
	 * Check for directory link count overflow.
	 */
	if (dp->i_d.di_nlink >= XFS_MAXLINK) {
		error = XFS_ERROR(EMLINK);
		goto error_return;
	}

	/*
	 * Reserve disk quota and the inode.
	 */
	error = XFS_TRANS_RESERVE_QUOTA(mp, tp, udqp, gdqp, resblks, 1, 0);
	if (error)
		goto error_return;

	error = xfs_dir_canenter(tp, dp, dir_name, resblks);
	if (error)
		goto error_return;
	/*
	 * create the directory inode.
	 */
	error = xfs_dir_ialloc(&tp, dp, mode, 2,
			0, credp, prid, resblks > 0,
		&cdp, NULL);
	if (error) {
		if (error == ENOSPC)
			goto error_return;
		goto abort_return;
	}
	xfs_itrace_ref(cdp);

	/*
	 * Now we add the directory inode to the transaction.
	 * We waited until now since xfs_dir_ialloc might start
	 * a new transaction.  Had we joined the transaction
	 * earlier, the locks might have gotten released. An error
	 * from here on will result in the transaction cancel
	 * unlocking dp so don't do it explicitly in the error path.
	 */
	IHOLD(dp);
	xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
	unlock_dp_on_error = B_FALSE;

	xfs_bmap_init(&free_list, &first_block);

	error = xfs_dir_createname(tp, dp, dir_name, cdp->i_ino,
					&first_block, &free_list, resblks ?
					resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
	if (error) {
		ASSERT(error != ENOSPC);
		goto error1;
	}
	xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);

	error = xfs_dir_init(tp, cdp, dp);
	if (error)
		goto error2;

	error = xfs_bumplink(tp, dp);
	if (error)
		goto error2;

	created = B_TRUE;

	*ipp = cdp;
	IHOLD(cdp);

	/*
	 * Attach the dquots to the new inode and modify the icount incore.
	 */
	XFS_QM_DQVOPCREATE(mp, tp, cdp, udqp, gdqp);

	/*
	 * If this is a synchronous mount, make sure that the
	 * mkdir transaction goes to disk before returning to
	 * the user.
	 */
	if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) {
		xfs_trans_set_sync(tp);
	}

	error = xfs_bmap_finish(&tp, &free_list, &committed);
	if (error) {
		IRELE(cdp);
		goto error2;
	}

	error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
	XFS_QM_DQRELE(mp, udqp);
	XFS_QM_DQRELE(mp, gdqp);
	if (error) {
		IRELE(cdp);
	}

	/* Fall through to std_return with error = 0 or errno from
	 * xfs_trans_commit. */

std_return:
	if ((created || (error != 0 && dm_event_sent != 0)) &&
	    DM_EVENT_ENABLED(dp, DM_EVENT_POSTCREATE)) {
		(void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTCREATE,
					dp, DM_RIGHT_NULL,
					created ? cdp : NULL,
					DM_RIGHT_NULL,
					dir_name->name, NULL,
					mode, error, 0);
	}
	return error;

 error2:
 error1:
	xfs_bmap_cancel(&free_list);
 abort_return:
	cancel_flags |= XFS_TRANS_ABORT;
 error_return:
	xfs_trans_cancel(tp, cancel_flags);
	XFS_QM_DQRELE(mp, udqp);
	XFS_QM_DQRELE(mp, gdqp);

	if (unlock_dp_on_error)
		xfs_iunlock(dp, XFS_ILOCK_EXCL);

	goto std_return;
}

int
xfs_symlink(
	xfs_inode_t		*dp,
+0 −2
Original line number Diff line number Diff line
@@ -31,8 +31,6 @@ int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
		struct xfs_inode *ip);
int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
		struct xfs_name *target_name);
int xfs_mkdir(struct xfs_inode *dp, struct xfs_name *dir_name,
		mode_t mode, struct xfs_inode **ipp, cred_t *credp);
int xfs_readdir(struct xfs_inode	*dp, void *dirent, size_t bufsize,
		       xfs_off_t *offset, filldir_t filldir);
int xfs_symlink(struct xfs_inode *dp, struct xfs_name *link_name,