static int
smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct)
{
	smb_node_t	*node;
	int		rc;

	node = (smb_node_t *)((arg)->fa_fnode->fn_available);
	SMB_NODE_VALID(node);

	if (ct == NULL) {
		(void) smb_oplock_break(node, NULL, B_FALSE);
		return (0);
	}

	if (ct->cc_caller_id == smb_ct.cc_caller_id)
		return (0);

	if (ct->cc_flags & CC_DONTBLOCK) {
		if (smb_oplock_break(node, NULL, B_TRUE))
			return (0);
		ct->cc_flags |= CC_WOULDBLOCK;
		rc = EAGAIN;
	} else {
		(void) smb_oplock_break(node, NULL, B_FALSE);
		rc = 0;
	}
	return (rc);
}
Beispiel #2
0
/*
 * smb_open_oplock_break
 *
 * If the node has an ofile opened with share access none,
 * (smb_node_share_check = FALSE) only break BATCH oplock.
 * Otherwise:
 * If overwriting, break to SMB_OPLOCK_NONE, else
 * If opening for anything other than attribute access,
 * break oplock to LEVEL_II.
 */
static void
smb_open_oplock_break(smb_request_t *sr, smb_node_t *node)
{
	smb_arg_open_t	*op = &sr->sr_open;
	uint32_t	flags = 0;

	if (!smb_node_share_check(node))
		flags |= SMB_OPLOCK_BREAK_BATCH;

	if (smb_open_overwrite(op)) {
		flags |= SMB_OPLOCK_BREAK_TO_NONE;
		(void) smb_oplock_break(sr, node, flags);
	} else if (!smb_open_attr_only(op)) {
		flags |= SMB_OPLOCK_BREAK_TO_LEVEL_II;
		(void) smb_oplock_break(sr, node, flags);
	}
}
Beispiel #3
0
static int
smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct, uint32_t flags)
{
	smb_node_t	*node;
	int		rc;

	node = (smb_node_t *)((arg)->fa_fnode->fn_available);
	SMB_NODE_VALID(node);

	ASSERT(ct != &smb_ct);

	if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
		flags |= SMB_OPLOCK_BREAK_NOWAIT;
		rc = smb_oplock_break(NULL, node, flags);
		if (rc == EAGAIN)
			ct->cc_flags |= CC_WOULDBLOCK;
	} else {
		rc = smb_oplock_break(NULL, node, flags);
	}

	return (rc);
}
Beispiel #4
0
/*
 * smb_common_rename
 *
 * Common code for renaming a file.
 *
 * If the source and destination are identical, we go through all
 * the checks but we don't actually do the rename.  If the source
 * and destination files differ only in case, we do a case-sensitive
 * rename.  Otherwise, we do a full case-insensitive rename.
 *
 * Returns NT status values.
 *
 * Similar to smb_make_link(), below.
 */
uint32_t
smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
{
	smb_node_t *src_fnode, *src_dnode, *dst_dnode;
	smb_node_t *dst_fnode = 0;
	smb_node_t *tnode;
	char *new_name, *path;
	DWORD status;
	int rc, count;

	tnode = sr->tid_tree->t_snode;
	path = dst_fqi->fq_path.pn_path;

	/* Check if attempting to rename a stream - not yet supported */
	rc = smb_rename_check_stream(src_fqi, dst_fqi);
	if (rc != 0)
		return (smb_rename_errno2status(rc));

	/*
	 * The source node may already have been provided,
	 * i.e. when called by SMB1/SMB2 smb_setinfo_rename.
	 * Not provided by smb_com_rename, smb_com_nt_rename.
	 */
	if (src_fqi->fq_fnode) {
		smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
		smb_node_ref(src_fqi->fq_fnode);
		smb_node_ref(src_fqi->fq_dnode);
	} else {
		/* lookup and validate src node */
		rc = smb_rename_lookup_src(sr);
		if (rc != 0)
			return (smb_rename_errno2status(rc));
	}

	src_fnode = src_fqi->fq_fnode;
	src_dnode = src_fqi->fq_dnode;

	/*
	 * Find the destination dnode and last component.
	 * May already be provided, i.e. when called via
	 * SMB1 trans2 setinfo.
	 */
	if (dst_fqi->fq_dnode) {
		/* called via smb_set_rename_info */
		smb_node_ref(dst_fqi->fq_dnode);
	} else {
		/* called via smb2_setf_rename, smb_com_rename, etc. */
		rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
		    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
		if (rc != 0) {
			smb_rename_release_src(sr);
			return (smb_rename_errno2status(rc));
		}
	}

	dst_dnode = dst_fqi->fq_dnode;
	new_name = dst_fqi->fq_last_comp;

	/* If exact name match in same directory, we're done */
	if ((src_dnode == dst_dnode) &&
	    (strcmp(src_fnode->od_name, new_name) == 0)) {
		smb_rename_release_src(sr);
		smb_node_release(dst_dnode);
		return (0);
	}

	/* Lookup destination node */
	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
	    dst_dnode, new_name, &dst_fqi->fq_fnode);

	/* If the destination node doesn't already exist, validate new_name. */
	if (rc == ENOENT) {
		if (smb_is_invalid_filename(new_name)) {
			smb_rename_release_src(sr);
			smb_node_release(dst_dnode);
			return (NT_STATUS_OBJECT_NAME_INVALID);
		}
	}

	/*
	 * Handle case where changing case of the same directory entry.
	 *
	 * If we found the dst node in the same directory as the src node,
	 * and their names differ only in case:
	 *
	 * If the tree is case sensitive (or mixed):
	 *  Do case sensitive lookup to see if exact match exists.
	 *  If the exact match is the same node as src_node we're done.
	 *
	 * If the tree is case insensitive:
	 *  There is currently no way to tell if the case is different
	 *  or not, so do the rename (unless the specified new name was
	 *  mangled).
	 */
	if ((rc == 0) &&
	    (src_dnode == dst_dnode) &&
	    (smb_strcasecmp(src_fnode->od_name,
	    dst_fqi->fq_fnode->od_name, 0) == 0)) {
		smb_node_release(dst_fqi->fq_fnode);
		dst_fqi->fq_fnode = NULL;

		if (smb_tree_has_feature(sr->tid_tree,
		    SMB_TREE_NO_CASESENSITIVE)) {
			if (smb_strcasecmp(src_fnode->od_name,
			    dst_fqi->fq_last_comp, 0) != 0) {
				smb_rename_release_src(sr);
				smb_node_release(dst_dnode);
				return (0);
			}
		} else {
			rc = smb_fsop_lookup(sr, sr->user_cr,
			    SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
			    &dst_fqi->fq_fnode);

			if ((rc == 0) &&
			    (dst_fqi->fq_fnode == src_fnode)) {
				smb_rename_release_src(sr);
				smb_node_release(dst_fqi->fq_fnode);
				smb_node_release(dst_dnode);
				return (0);
			}
		}
	}

	if ((rc != 0) && (rc != ENOENT)) {
		smb_rename_release_src(sr);
		smb_node_release(dst_fqi->fq_dnode);
		return (smb_rename_errno2status(rc));
	}

	if (dst_fqi->fq_fnode) {
		/*
		 * Destination already exists.  Do delete checks.
		 */
		dst_fnode = dst_fqi->fq_fnode;

		if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
			smb_rename_release_src(sr);
			smb_node_release(dst_fnode);
			smb_node_release(dst_dnode);
			return (NT_STATUS_OBJECT_NAME_COLLISION);
		}

		(void) smb_oplock_break(sr, dst_fnode,
		    SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH);

		/*
		 * Wait (a little) for the oplock break to be
		 * responded to by clients closing handles.
		 * Hold node->n_lock as reader to keep new
		 * ofiles from showing up after we check.
		 */
		smb_node_rdlock(dst_fnode);
		for (count = 0; count <= 12; count++) {
			status = smb_node_delete_check(dst_fnode);
			if (status != NT_STATUS_SHARING_VIOLATION)
				break;
			smb_node_unlock(dst_fnode);
			delay(MSEC_TO_TICK(100));
			smb_node_rdlock(dst_fnode);
		}
		if (status != NT_STATUS_SUCCESS) {
			smb_node_unlock(dst_fnode);
			smb_rename_release_src(sr);
			smb_node_release(dst_fnode);
			smb_node_release(dst_dnode);
			return (NT_STATUS_ACCESS_DENIED);
		}

		/*
		 * Note, the combination of these two:
		 *	smb_node_rdlock(node);
		 *	nbl_start_crit(node->vp, RW_READER);
		 * is equivalent to this call:
		 *	smb_node_start_crit(node, RW_READER)
		 *
		 * Cleanup after this point should use:
		 *	smb_node_end_crit(dst_fnode)
		 */
		nbl_start_crit(dst_fnode->vp, RW_READER);

		/*
		 * This checks nbl_share_conflict, nbl_lock_conflict
		 */
		status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE);
		if (status != NT_STATUS_SUCCESS) {
			smb_node_end_crit(dst_fnode);
			smb_rename_release_src(sr);
			smb_node_release(dst_fnode);
			smb_node_release(dst_dnode);
			return (NT_STATUS_ACCESS_DENIED);
		}

		new_name = dst_fnode->od_name;
	}

	rc = smb_fsop_rename(sr, sr->user_cr,
	    src_dnode, src_fnode->od_name,
	    dst_dnode, new_name);

	if (rc == 0) {
		/*
		 * Note that renames in the same directory are normally
		 * delivered in {old,new} pairs, and clients expect them
		 * in that order, if both events are delivered.
		 */
		int a_src, a_dst; /* action codes */
		if (src_dnode == dst_dnode) {
			a_src = FILE_ACTION_RENAMED_OLD_NAME;
			a_dst = FILE_ACTION_RENAMED_NEW_NAME;
		} else {
			a_src = FILE_ACTION_REMOVED;
			a_dst = FILE_ACTION_ADDED;
		}
		smb_node_notify_change(src_dnode, a_src, src_fnode->od_name);
		smb_node_notify_change(dst_dnode, a_dst, new_name);
	}

	smb_rename_release_src(sr);

	if (dst_fqi->fq_fnode) {
		smb_node_end_crit(dst_fnode);
		smb_node_release(dst_fnode);
	}
	smb_node_release(dst_dnode);

	return (smb_rename_errno2status(rc));
}
Beispiel #5
0
/*
 * smb_rename_lookup_src
 *
 * Lookup the src node, checking for sharing violations and
 * breaking any existing BATCH oplock.
 * Populate sr->arg.dirop.fqi
 *
 * Upon success, the dnode and fnode will have holds and the
 * fnode will be in a critical section. These should be
 * released using smb_rename_release_src().
 *
 * Returns errno values.
 */
static int
smb_rename_lookup_src(smb_request_t *sr)
{
	smb_node_t *src_node, *tnode;
	DWORD status;
	int rc;
	int count;
	char *path;

	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;

	if (smb_is_stream_name(src_fqi->fq_path.pn_path))
		return (EINVAL);

	/* Lookup the source node */
	tnode = sr->tid_tree->t_snode;
	path = src_fqi->fq_path.pn_path;
	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
	    &src_fqi->fq_dnode, src_fqi->fq_last_comp);
	if (rc != 0)
		return (rc);

	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
	    src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
	if (rc != 0) {
		smb_node_release(src_fqi->fq_dnode);
		return (rc);
	}
	src_node = src_fqi->fq_fnode;

	rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
	if (rc != 0) {
		smb_node_release(src_fqi->fq_fnode);
		smb_node_release(src_fqi->fq_dnode);
		return (rc);
	}

	/*
	 * Break BATCH oplock before ofile checks. If a client
	 * has a file open, this will force a flush or close,
	 * which may affect the outcome of any share checking.
	 */
	(void) smb_oplock_break(sr, src_node,
	    SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH);

	/*
	 * Wait (a little) for the oplock break to be
	 * responded to by clients closing handles.
	 * Hold node->n_lock as reader to keep new
	 * ofiles from showing up after we check.
	 */
	smb_node_rdlock(src_node);
	for (count = 0; count <= 12; count++) {
		status = smb_node_rename_check(src_node);
		if (status != NT_STATUS_SHARING_VIOLATION)
			break;
		smb_node_unlock(src_node);
		delay(MSEC_TO_TICK(100));
		smb_node_rdlock(src_node);
	}
	if (status != NT_STATUS_SUCCESS) {
		smb_node_unlock(src_node);
		smb_node_release(src_fqi->fq_fnode);
		smb_node_release(src_fqi->fq_dnode);
		return (EPIPE); /* = ERRbadshare */
	}

	/*
	 * Note, the combination of these two:
	 *	smb_node_rdlock(node);
	 *	nbl_start_crit(node->vp, RW_READER);
	 * is equivalent to this call:
	 *	smb_node_start_crit(node, RW_READER)
	 *
	 * Cleanup after this point should use:
	 *	smb_node_end_crit(src_node)
	 */
	nbl_start_crit(src_node->vp, RW_READER);

	/*
	 * This checks nbl_share_conflict, nbl_lock_conflict
	 */
	status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME);
	if (status != NT_STATUS_SUCCESS) {
		smb_node_end_crit(src_node);
		smb_node_release(src_fqi->fq_fnode);
		smb_node_release(src_fqi->fq_dnode);
		if (status == NT_STATUS_SHARING_VIOLATION)
			return (EPIPE); /* = ERRbadshare */
		return (EACCES);
	}

	/* NB: Caller expects holds on src_fqi fnode, dnode */
	return (0);
}