xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 5772:237ac22142fe)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*5772Sas200622  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
275331Samw 
285331Samw #include <smbsrv/nterror.h>
295331Samw #include <sys/synch.h>
305331Samw #include <smbsrv/smb_incl.h>
315331Samw #include <smbsrv/smb_fsops.h>
32*5772Sas200622 #include <sys/nbmlock.h>
335331Samw 
345331Samw static int smb_do_rename(struct smb_request *sr,
355331Samw 				struct smb_fqi *src_fqi,
365331Samw 				struct smb_fqi *dst_fqi);
375331Samw 
385331Samw /*
395331Samw  * smb_com_rename
405331Samw  *
415331Samw  * Rename a file. Files OldFileName must exist and NewFileName must not.
425331Samw  * Both pathnames must be relative to the Tid specified in the request.
435331Samw  * Open files may be renamed.
445331Samw  *
455331Samw  * Multiple files may be renamed in response to a single request as Rename
465331Samw  * File supports wildcards in the file name (last component of the path).
475331Samw  * NOTE: we don't support rename with wildcards.
485331Samw  *
495331Samw  * SearchAttributes indicates the attributes that the target file(s) must
505331Samw  * have. If SearchAttributes is zero then only normal files are renamed.
515331Samw  * If the system file or hidden attributes are specified then the rename
525331Samw  * is inclusive - both the specified type(s) of files and normal files are
535331Samw  * renamed. The encoding of SearchAttributes is described in section 3.10
545331Samw  * - File Attribute Encoding.
555331Samw  */
565331Samw int
575331Samw smb_com_rename(struct smb_request *sr)
585331Samw {
595331Samw 	static kmutex_t mutex;
605331Samw 	struct smb_fqi *src_fqi;
615331Samw 	struct smb_fqi *dst_fqi;
625331Samw 	struct smb_node *dst_node;
635331Samw 	int rc;
645331Samw 
655331Samw 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
66*5772Sas200622 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
675331Samw 		    ERRDOS, ERROR_ACCESS_DENIED);
685331Samw 		/* NOTREACHED */
695331Samw 	}
705331Samw 
715331Samw 	src_fqi = &sr->arg.dirop.fqi;
725331Samw 	dst_fqi = &sr->arg.dirop.dst_fqi;
735331Samw 
745331Samw 	if (smbsr_decode_vwv(sr, "w", &src_fqi->srch_attr) != 0) {
755331Samw 		smbsr_decode_error(sr);
765331Samw 		/* NOTREACHED */
775331Samw 	}
785331Samw 
795331Samw 	rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path, &dst_fqi->path);
805331Samw 	if (rc != 0) {
815331Samw 		smbsr_decode_error(sr);
825331Samw 		/* NOTREACHED */
835331Samw 	}
845331Samw 
855331Samw 	dst_fqi->srch_attr = 0;
865331Samw 
875331Samw 	mutex_enter(&mutex);
885331Samw 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
895331Samw 	mutex_exit(&mutex);
905331Samw 
915331Samw 	if (rc != 0) {
925331Samw 		/*
935331Samw 		 * ERROR_FILE_EXISTS doesn't work for Windows98 clients.
945331Samw 		 *
955331Samw 		 * Windows95 clients don't see this problem because the target
965331Samw 		 * is deleted before the rename request.
975331Samw 		 *
985331Samw 		 * The following values are based on observed WFWG, Win9x,
995331Samw 		 * NT and W2K client behaviour.
1005331Samw 		 */
1015331Samw 		if (rc == EEXIST) {
102*5772Sas200622 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION,
1035331Samw 			    ERRDOS, ERROR_ALREADY_EXISTS);
1045331Samw 			/* NOTREACHED */
1055331Samw 		}
1065331Samw 
1075331Samw 		if (rc == EPIPE) {
108*5772Sas200622 			smbsr_error(sr, NT_STATUS_SHARING_VIOLATION,
1095331Samw 			    ERRDOS, ERROR_SHARING_VIOLATION);
1105331Samw 			/* NOTREACHED */
1115331Samw 		}
1125331Samw 
113*5772Sas200622 		smbsr_errno(sr, rc);
1145331Samw 		/* NOTREACHED */
1155331Samw 	}
1165331Samw 
1175331Samw 	if (src_fqi->dir_snode)
1185331Samw 		smb_node_release(src_fqi->dir_snode);
1195331Samw 
1205331Samw 	dst_node = dst_fqi->dir_snode;
1215331Samw 	if (dst_node) {
1225331Samw 		if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
1235331Samw 			dst_node->flags |= NODE_FLAGS_CHANGED;
1245331Samw 			smb_process_node_notify_change_queue(dst_node);
1255331Samw 		}
1265331Samw 		smb_node_release(dst_node);
1275331Samw 	}
1285331Samw 
1295331Samw 	SMB_NULL_FQI_NODES(*src_fqi);
1305331Samw 	SMB_NULL_FQI_NODES(*dst_fqi);
1315331Samw 
1325331Samw 	smbsr_encode_empty_result(sr);
1335331Samw 
1345331Samw 	return (SDRC_NORMAL_REPLY);
1355331Samw }
1365331Samw 
1375331Samw /*
1385331Samw  * smb_do_rename
1395331Samw  *
1405331Samw  * Backend to smb_com_rename to ensure that the rename operation is atomic.
1415331Samw  * This function should be called within a mutual exclusion region. If the
1425331Samw  * source and destination are identical, we don't actually do a rename, we
1435331Samw  * just check that the conditions are right. If the source and destination
1445331Samw  * files differ only in case, we a case-sensitive rename. Otherwise, we do
1455331Samw  * a full case-insensitive rename.
1465331Samw  *
1475331Samw  * This function should always return errno values.
1485331Samw  *
1495331Samw  * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi
1505331Samw  * are not released in this routine but in smb_com_rename().
1515331Samw  */
1525331Samw static int
1535331Samw smb_do_rename(
1545331Samw     struct smb_request *sr,
1555331Samw     struct smb_fqi *src_fqi,
1565331Samw     struct smb_fqi *dst_fqi)
1575331Samw {
1585331Samw 	struct smb_node *src_node;
1595331Samw 	char *dstname;
1605331Samw 	DWORD status;
1615331Samw 	int rc;
1625331Samw 	int count;
1635331Samw 
1645331Samw 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) {
1655331Samw 		return (rc);
1665331Samw 	}
1675331Samw 
1685331Samw 	src_node = src_fqi->last_snode;
1695331Samw 
1705331Samw 	/*
1715331Samw 	 * Break the oplock before access checks. If a client
1725331Samw 	 * has a file open, this will force a flush or close,
1735331Samw 	 * which may affect the outcome of any share checking.
1745331Samw 	 */
1755331Samw 	if (OPLOCKS_IN_FORCE(src_node)) {
1765331Samw 		status = smb_break_oplock(sr, src_node);
1775331Samw 
1785331Samw 		if (status != NT_STATUS_SUCCESS) {
1795331Samw 			smb_node_release(src_node);
1805331Samw 			smb_node_release(src_fqi->dir_snode);
1815331Samw 
1825331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
1835331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
1845331Samw 			return (EACCES);
1855331Samw 		}
1865331Samw 	}
1875331Samw 
188*5772Sas200622 	for (count = 0; count <= 3; count++) {
189*5772Sas200622 		if (count) {
190*5772Sas200622 			smb_node_end_crit(src_node);
191*5772Sas200622 			delay(MSEC_TO_TICK(400));
192*5772Sas200622 		}
193*5772Sas200622 
194*5772Sas200622 		smb_node_start_crit(src_node, RW_READER);
195*5772Sas200622 
196*5772Sas200622 		status = smb_node_rename_check(src_node);
197*5772Sas200622 
198*5772Sas200622 		if (status != NT_STATUS_SHARING_VIOLATION)
199*5772Sas200622 			break;
200*5772Sas200622 	}
201*5772Sas200622 
202*5772Sas200622 	if (status == NT_STATUS_SHARING_VIOLATION) {
203*5772Sas200622 		smb_node_end_crit(src_node);
204*5772Sas200622 
205*5772Sas200622 		smb_node_release(src_node);
206*5772Sas200622 		smb_node_release(src_fqi->dir_snode);
207*5772Sas200622 
208*5772Sas200622 		SMB_NULL_FQI_NODES(*src_fqi);
209*5772Sas200622 		SMB_NULL_FQI_NODES(*dst_fqi);
210*5772Sas200622 		return (EPIPE); /* = ERRbadshare */
211*5772Sas200622 	}
212*5772Sas200622 
213*5772Sas200622 	status = smb_range_check(sr, sr->user_cr, src_node, 0, UINT64_MAX,
214*5772Sas200622 	    B_TRUE);
215*5772Sas200622 
2165331Samw 	if (status != NT_STATUS_SUCCESS) {
217*5772Sas200622 		smb_node_end_crit(src_node);
218*5772Sas200622 
2195331Samw 		smb_node_release(src_node);
2205331Samw 		smb_node_release(src_fqi->dir_snode);
2215331Samw 
2225331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
2235331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
2245331Samw 		return (EACCES);
2255331Samw 	}
2265331Samw 
2275331Samw 	if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) {
2285331Samw 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
229*5772Sas200622 			smb_node_end_crit(src_node);
230*5772Sas200622 
231*5772Sas200622 			smb_node_release(src_node);
2325331Samw 			smb_node_release(src_fqi->dir_snode);
2335331Samw 
2345331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
2355331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
2365331Samw 			return (rc);
2375331Samw 		}
2385331Samw 
2395331Samw 		/*
2405331Samw 		 * Because the fqm parameter to smbd_fs_query() was 0,
2415331Samw 		 * a successful return value means that dst_fqi->last_snode
2425331Samw 		 * may be NULL.
2435331Samw 		 */
2445331Samw 		if (dst_fqi->last_snode)
2455331Samw 			smb_node_release(dst_fqi->last_snode);
2465331Samw 
2475331Samw 		rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp);
2485331Samw 		if (rc == 0) {
249*5772Sas200622 			smb_node_end_crit(src_node);
250*5772Sas200622 
251*5772Sas200622 			smb_node_release(src_node);
2525331Samw 			smb_node_release(src_fqi->dir_snode);
2535331Samw 			smb_node_release(dst_fqi->dir_snode);
2545331Samw 
2555331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
2565331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
2575331Samw 			return (0);
2585331Samw 		}
2595331Samw 
2605331Samw 		rc = smb_fsop_rename(sr, sr->user_cr,
2615331Samw 		    src_fqi->dir_snode,
2625331Samw 		    src_fqi->last_comp_od,
2635331Samw 		    dst_fqi->dir_snode,
2645331Samw 		    dst_fqi->last_comp);
2655331Samw 
2665331Samw 		if (rc != 0) {
2675331Samw 			smb_node_release(src_fqi->dir_snode);
2685331Samw 			smb_node_release(dst_fqi->dir_snode);
2695331Samw 
2705331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
2715331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
2725331Samw 		}
273*5772Sas200622 
274*5772Sas200622 		smb_node_end_crit(src_node);
275*5772Sas200622 
276*5772Sas200622 		smb_node_release(src_node);
2775331Samw 		return (rc);
2785331Samw 	}
2795331Samw 
2805331Samw 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
2815331Samw 	if (rc != 0) {
282*5772Sas200622 		smb_node_end_crit(src_node);
283*5772Sas200622 
284*5772Sas200622 		smb_node_release(src_node);
2855331Samw 		smb_node_release(src_fqi->dir_snode);
2865331Samw 
2875331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
2885331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
2895331Samw 		return (rc);
2905331Samw 	}
2915331Samw 
2925331Samw 	/*
2935331Samw 	 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return
2945331Samw 	 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode
2955331Samw 	 * is NULL).
2965331Samw 	 */
2975331Samw 
2985331Samw 	/*
2995331Samw 	 * Use the unmangled form of the destination name if the
3005331Samw 	 * source and destination names are the same and the source
3015331Samw 	 * name is mangled.  (We are taking a chance here, assuming
3025331Samw 	 * that this is what the user wants.)
3035331Samw 	 */
3045331Samw 
3055331Samw 	if ((smb_maybe_mangled_name(src_fqi->last_comp)) &&
3065331Samw 	    (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) {
3075331Samw 		dstname = src_fqi->last_comp_od;
3085331Samw 	} else {
3095331Samw 		dstname = dst_fqi->last_comp;
3105331Samw 	}
3115331Samw 
3125331Samw 	rc = smb_fsop_rename(sr, sr->user_cr,
3135331Samw 	    src_fqi->dir_snode,
3145331Samw 	    src_fqi->last_comp_od,
3155331Samw 	    dst_fqi->dir_snode,
3165331Samw 	    dstname);
3175331Samw 
3185331Samw 	if (rc != 0) {
3195331Samw 		smb_node_release(src_fqi->dir_snode);
3205331Samw 		smb_node_release(dst_fqi->dir_snode);
3215331Samw 
3225331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
3235331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
3245331Samw 	}
3255331Samw 
326*5772Sas200622 	smb_node_end_crit(src_node);
327*5772Sas200622 
328*5772Sas200622 	smb_node_release(src_node);
329*5772Sas200622 
3305331Samw 	return (rc);
3315331Samw }
332