xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 8934:8ff6afa44187)
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*8934SJose.Borrego@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #include <smbsrv/nterror.h>
275331Samw #include <sys/synch.h>
285331Samw #include <smbsrv/smb_incl.h>
295331Samw #include <smbsrv/smb_fsops.h>
305772Sas200622 #include <sys/nbmlock.h>
315331Samw 
327961SNatalie.Li@Sun.COM static int smb_do_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
335331Samw 
345331Samw /*
355331Samw  * smb_com_rename
365331Samw  *
375331Samw  * Rename a file. Files OldFileName must exist and NewFileName must not.
385331Samw  * Both pathnames must be relative to the Tid specified in the request.
395331Samw  * Open files may be renamed.
405331Samw  *
415331Samw  * Multiple files may be renamed in response to a single request as Rename
425331Samw  * File supports wildcards in the file name (last component of the path).
435331Samw  * NOTE: we don't support rename with wildcards.
445331Samw  *
455331Samw  * SearchAttributes indicates the attributes that the target file(s) must
465331Samw  * have. If SearchAttributes is zero then only normal files are renamed.
475331Samw  * If the system file or hidden attributes are specified then the rename
485331Samw  * is inclusive - both the specified type(s) of files and normal files are
495331Samw  * renamed. The encoding of SearchAttributes is described in section 3.10
505331Samw  * - File Attribute Encoding.
515331Samw  */
526030Sjb150015 smb_sdrc_t
536139Sjb150015 smb_pre_rename(smb_request_t *sr)
546139Sjb150015 {
557961SNatalie.Li@Sun.COM 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
567961SNatalie.Li@Sun.COM 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
576139Sjb150015 	int rc;
586139Sjb150015 
596139Sjb150015 	if ((rc = smbsr_decode_vwv(sr, "w", &src_fqi->srch_attr)) == 0) {
606139Sjb150015 		rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path,
616139Sjb150015 		    &dst_fqi->path);
626139Sjb150015 
636139Sjb150015 		dst_fqi->srch_attr = 0;
646139Sjb150015 	}
656139Sjb150015 
666139Sjb150015 	DTRACE_SMB_2(op__Rename__start, smb_request_t *, sr,
676139Sjb150015 	    struct dirop *, &sr->arg.dirop);
686139Sjb150015 
696139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
706139Sjb150015 }
716139Sjb150015 
726139Sjb150015 void
736139Sjb150015 smb_post_rename(smb_request_t *sr)
746139Sjb150015 {
756139Sjb150015 	DTRACE_SMB_1(op__Rename__done, smb_request_t *, sr);
766139Sjb150015 }
776139Sjb150015 
786139Sjb150015 smb_sdrc_t
796139Sjb150015 smb_com_rename(smb_request_t *sr)
805331Samw {
815331Samw 	static kmutex_t mutex;
827961SNatalie.Li@Sun.COM 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
837961SNatalie.Li@Sun.COM 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
845331Samw 	struct smb_node *dst_node;
855331Samw 	int rc;
865331Samw 
875331Samw 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
885772Sas200622 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
895331Samw 		    ERRDOS, ERROR_ACCESS_DENIED);
906139Sjb150015 		return (SDRC_ERROR);
915331Samw 	}
925331Samw 
935331Samw 	mutex_enter(&mutex);
945331Samw 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
955331Samw 	mutex_exit(&mutex);
965331Samw 
975331Samw 	if (rc != 0) {
985331Samw 		/*
996139Sjb150015 		 * The following values are based on observed WFWG,
1006139Sjb150015 		 * Windows 9x, NT and Windows 2000 behaviour.
1016139Sjb150015 		 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
1026139Sjb150015 		 * Windows 95 clients don't see the problem because the
1036139Sjb150015 		 * target is deleted before the rename request.
1045331Samw 		 */
1056139Sjb150015 		switch (rc) {
1066139Sjb150015 		case EEXIST:
1075772Sas200622 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION,
1085331Samw 			    ERRDOS, ERROR_ALREADY_EXISTS);
1096139Sjb150015 			break;
1106139Sjb150015 		case EPIPE:
1115772Sas200622 			smbsr_error(sr, NT_STATUS_SHARING_VIOLATION,
1125331Samw 			    ERRDOS, ERROR_SHARING_VIOLATION);
1136139Sjb150015 			break;
1146139Sjb150015 		case ENOENT:
1156139Sjb150015 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
1166139Sjb150015 			    ERRDOS, ERROR_FILE_NOT_FOUND);
1176139Sjb150015 			break;
1186139Sjb150015 		default:
1196139Sjb150015 			smbsr_errno(sr, rc);
1206139Sjb150015 			break;
1215331Samw 		}
1225331Samw 
1236139Sjb150015 		return (SDRC_ERROR);
1245331Samw 	}
1255331Samw 
1265331Samw 	if (src_fqi->dir_snode)
1275331Samw 		smb_node_release(src_fqi->dir_snode);
1285331Samw 
1295331Samw 	dst_node = dst_fqi->dir_snode;
1305331Samw 	if (dst_node) {
1315331Samw 		if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
1325331Samw 			dst_node->flags |= NODE_FLAGS_CHANGED;
1335331Samw 			smb_process_node_notify_change_queue(dst_node);
1345331Samw 		}
1355331Samw 		smb_node_release(dst_node);
1365331Samw 	}
1375331Samw 
1385331Samw 	SMB_NULL_FQI_NODES(*src_fqi);
1395331Samw 	SMB_NULL_FQI_NODES(*dst_fqi);
1405331Samw 
1416030Sjb150015 	rc = smbsr_encode_empty_result(sr);
1426139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1435331Samw }
1445331Samw 
1455331Samw /*
1465331Samw  * smb_do_rename
1475331Samw  *
1485331Samw  * Backend to smb_com_rename to ensure that the rename operation is atomic.
1495331Samw  * This function should be called within a mutual exclusion region. If the
1505331Samw  * source and destination are identical, we don't actually do a rename, we
1515331Samw  * just check that the conditions are right. If the source and destination
1525331Samw  * files differ only in case, we a case-sensitive rename. Otherwise, we do
1535331Samw  * a full case-insensitive rename.
1545331Samw  *
1555331Samw  * This function should always return errno values.
1565331Samw  *
1575331Samw  * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi
1585331Samw  * are not released in this routine but in smb_com_rename().
1595331Samw  */
1605331Samw static int
1615331Samw smb_do_rename(
1626139Sjb150015     smb_request_t *sr,
1637961SNatalie.Li@Sun.COM     smb_fqi_t *src_fqi,
1647961SNatalie.Li@Sun.COM     smb_fqi_t *dst_fqi)
1655331Samw {
1667961SNatalie.Li@Sun.COM 	smb_node_t *src_node;
1675331Samw 	char *dstname;
1685331Samw 	DWORD status;
1695331Samw 	int rc;
1705331Samw 	int count;
1715331Samw 
1725331Samw 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) {
1735331Samw 		return (rc);
1745331Samw 	}
1755331Samw 
1765331Samw 	src_node = src_fqi->last_snode;
1775331Samw 
1785331Samw 	/*
1795331Samw 	 * Break the oplock before access checks. If a client
1805331Samw 	 * has a file open, this will force a flush or close,
1815331Samw 	 * which may affect the outcome of any share checking.
1825331Samw 	 */
183*8934SJose.Borrego@Sun.COM 	(void) smb_oplock_break(src_node, SMB_SESSION_GET_ID(sr->session),
184*8934SJose.Borrego@Sun.COM 	    B_FALSE);
1855331Samw 
1865772Sas200622 	for (count = 0; count <= 3; count++) {
1875772Sas200622 		if (count) {
1885772Sas200622 			smb_node_end_crit(src_node);
1895772Sas200622 			delay(MSEC_TO_TICK(400));
1905772Sas200622 		}
1915772Sas200622 
1925772Sas200622 		smb_node_start_crit(src_node, RW_READER);
1935772Sas200622 
1945772Sas200622 		status = smb_node_rename_check(src_node);
1955772Sas200622 
1965772Sas200622 		if (status != NT_STATUS_SHARING_VIOLATION)
1975772Sas200622 			break;
1985772Sas200622 	}
1995772Sas200622 
2005772Sas200622 	if (status == NT_STATUS_SHARING_VIOLATION) {
2015772Sas200622 		smb_node_end_crit(src_node);
2025772Sas200622 
2035772Sas200622 		smb_node_release(src_node);
2045772Sas200622 		smb_node_release(src_fqi->dir_snode);
2055772Sas200622 
2065772Sas200622 		SMB_NULL_FQI_NODES(*src_fqi);
2075772Sas200622 		SMB_NULL_FQI_NODES(*dst_fqi);
2085772Sas200622 		return (EPIPE); /* = ERRbadshare */
2095772Sas200622 	}
2105772Sas200622 
2117348SJose.Borrego@Sun.COM 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
2125772Sas200622 
2135331Samw 	if (status != NT_STATUS_SUCCESS) {
2145772Sas200622 		smb_node_end_crit(src_node);
2155772Sas200622 
2165331Samw 		smb_node_release(src_node);
2175331Samw 		smb_node_release(src_fqi->dir_snode);
2185331Samw 
2195331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
2205331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
2215331Samw 		return (EACCES);
2225331Samw 	}
2235331Samw 
2245331Samw 	if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) {
2255331Samw 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
2265772Sas200622 			smb_node_end_crit(src_node);
2275772Sas200622 
2285772Sas200622 			smb_node_release(src_node);
2295331Samw 			smb_node_release(src_fqi->dir_snode);
2305331Samw 
2315331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
2325331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
2335331Samw 			return (rc);
2345331Samw 		}
2355331Samw 
2365331Samw 		/*
2375331Samw 		 * Because the fqm parameter to smbd_fs_query() was 0,
2385331Samw 		 * a successful return value means that dst_fqi->last_snode
2395331Samw 		 * may be NULL.
2405331Samw 		 */
2415331Samw 		if (dst_fqi->last_snode)
2425331Samw 			smb_node_release(dst_fqi->last_snode);
2435331Samw 
2445331Samw 		rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp);
2455331Samw 		if (rc == 0) {
2465772Sas200622 			smb_node_end_crit(src_node);
2475772Sas200622 
2485772Sas200622 			smb_node_release(src_node);
2495331Samw 			smb_node_release(src_fqi->dir_snode);
2505331Samw 			smb_node_release(dst_fqi->dir_snode);
2515331Samw 
2525331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
2535331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
2545331Samw 			return (0);
2555331Samw 		}
2565331Samw 
2575331Samw 		rc = smb_fsop_rename(sr, sr->user_cr,
2585331Samw 		    src_fqi->dir_snode,
2595331Samw 		    src_fqi->last_comp_od,
2605331Samw 		    dst_fqi->dir_snode,
2615331Samw 		    dst_fqi->last_comp);
2625331Samw 
2635331Samw 		if (rc != 0) {
2645331Samw 			smb_node_release(src_fqi->dir_snode);
2655331Samw 			smb_node_release(dst_fqi->dir_snode);
2665331Samw 
2675331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
2685331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
2695331Samw 		}
2705772Sas200622 
2715772Sas200622 		smb_node_end_crit(src_node);
2725772Sas200622 
2735772Sas200622 		smb_node_release(src_node);
2745331Samw 		return (rc);
2755331Samw 	}
2765331Samw 
2775331Samw 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
2785331Samw 	if (rc != 0) {
2795772Sas200622 		smb_node_end_crit(src_node);
2805772Sas200622 
2815772Sas200622 		smb_node_release(src_node);
2825331Samw 		smb_node_release(src_fqi->dir_snode);
2835331Samw 
2845331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
2855331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
2865331Samw 		return (rc);
2875331Samw 	}
2885331Samw 
2895331Samw 	/*
2905331Samw 	 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return
2915331Samw 	 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode
2925331Samw 	 * is NULL).
2935331Samw 	 */
2945331Samw 
2955331Samw 	/*
2965331Samw 	 * Use the unmangled form of the destination name if the
2975331Samw 	 * source and destination names are the same and the source
2985331Samw 	 * name is mangled.  (We are taking a chance here, assuming
2995331Samw 	 * that this is what the user wants.)
3005331Samw 	 */
3015331Samw 
3025331Samw 	if ((smb_maybe_mangled_name(src_fqi->last_comp)) &&
3035331Samw 	    (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) {
3045331Samw 		dstname = src_fqi->last_comp_od;
3055331Samw 	} else {
3065331Samw 		dstname = dst_fqi->last_comp;
3075331Samw 	}
3085331Samw 
3095331Samw 	rc = smb_fsop_rename(sr, sr->user_cr,
3105331Samw 	    src_fqi->dir_snode,
3115331Samw 	    src_fqi->last_comp_od,
3125331Samw 	    dst_fqi->dir_snode,
3135331Samw 	    dstname);
3145331Samw 
3155331Samw 	if (rc != 0) {
3165331Samw 		smb_node_release(src_fqi->dir_snode);
3175331Samw 		smb_node_release(dst_fqi->dir_snode);
3185331Samw 
3195331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
3205331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
3215331Samw 	}
3225331Samw 
3235772Sas200622 	smb_node_end_crit(src_node);
3245772Sas200622 
3255772Sas200622 	smb_node_release(src_node);
3265772Sas200622 
3275331Samw 	return (rc);
3285331Samw }
329