xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 9914:15092dda0737)
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 /*
228934SJose.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 
32*9914Samw@Sun.COM /*
33*9914Samw@Sun.COM  * NT_RENAME InformationLevels:
34*9914Samw@Sun.COM  *
35*9914Samw@Sun.COM  * SMB_NT_RENAME_MOVE_CLUSTER_INFO	Server returns invalid parameter.
36*9914Samw@Sun.COM  * SMB_NT_RENAME_SET_LINK_INFO		Create a hard link to a file.
37*9914Samw@Sun.COM  * SMB_NT_RENAME_RENAME_FILE		In-place rename of a file.
38*9914Samw@Sun.COM  * SMB_NT_RENAME_MOVE_FILE		Move (rename) a file.
39*9914Samw@Sun.COM  */
40*9914Samw@Sun.COM #define	SMB_NT_RENAME_MOVE_CLUSTER_INFO	0x0102
41*9914Samw@Sun.COM #define	SMB_NT_RENAME_SET_LINK_INFO	0x0103
42*9914Samw@Sun.COM #define	SMB_NT_RENAME_RENAME_FILE	0x0104
43*9914Samw@Sun.COM #define	SMB_NT_RENAME_MOVE_FILE		0x0105
44*9914Samw@Sun.COM 
457961SNatalie.Li@Sun.COM static int smb_do_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
46*9914Samw@Sun.COM static int smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
47*9914Samw@Sun.COM static int smb_rename_check_attr(smb_node_t *, uint16_t);
48*9914Samw@Sun.COM static void smb_rename_set_error(smb_request_t *, int);
495331Samw 
505331Samw /*
515331Samw  * smb_com_rename
525331Samw  *
535331Samw  * Rename a file. Files OldFileName must exist and NewFileName must not.
545331Samw  * Both pathnames must be relative to the Tid specified in the request.
555331Samw  * Open files may be renamed.
565331Samw  *
575331Samw  * Multiple files may be renamed in response to a single request as Rename
585331Samw  * File supports wildcards in the file name (last component of the path).
595331Samw  * NOTE: we don't support rename with wildcards.
605331Samw  *
615331Samw  * SearchAttributes indicates the attributes that the target file(s) must
625331Samw  * have. If SearchAttributes is zero then only normal files are renamed.
635331Samw  * If the system file or hidden attributes are specified then the rename
645331Samw  * is inclusive - both the specified type(s) of files and normal files are
655331Samw  * renamed. The encoding of SearchAttributes is described in section 3.10
665331Samw  * - File Attribute Encoding.
675331Samw  */
686030Sjb150015 smb_sdrc_t
696139Sjb150015 smb_pre_rename(smb_request_t *sr)
706139Sjb150015 {
717961SNatalie.Li@Sun.COM 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
727961SNatalie.Li@Sun.COM 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
736139Sjb150015 	int rc;
746139Sjb150015 
759343SAfshin.Ardakani@Sun.COM 	if ((rc = smbsr_decode_vwv(sr, "w", &src_fqi->fq_sattr)) == 0) {
769343SAfshin.Ardakani@Sun.COM 		rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->fq_path.pn_path,
779343SAfshin.Ardakani@Sun.COM 		    &dst_fqi->fq_path.pn_path);
786139Sjb150015 
799343SAfshin.Ardakani@Sun.COM 		dst_fqi->fq_sattr = 0;
806139Sjb150015 	}
816139Sjb150015 
826139Sjb150015 	DTRACE_SMB_2(op__Rename__start, smb_request_t *, sr,
836139Sjb150015 	    struct dirop *, &sr->arg.dirop);
846139Sjb150015 
856139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
866139Sjb150015 }
876139Sjb150015 
886139Sjb150015 void
896139Sjb150015 smb_post_rename(smb_request_t *sr)
906139Sjb150015 {
916139Sjb150015 	DTRACE_SMB_1(op__Rename__done, smb_request_t *, sr);
926139Sjb150015 }
936139Sjb150015 
946139Sjb150015 smb_sdrc_t
956139Sjb150015 smb_com_rename(smb_request_t *sr)
965331Samw {
977961SNatalie.Li@Sun.COM 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
987961SNatalie.Li@Sun.COM 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
995331Samw 	int rc;
1005331Samw 
1015331Samw 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
1025772Sas200622 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
1035331Samw 		    ERRDOS, ERROR_ACCESS_DENIED);
1046139Sjb150015 		return (SDRC_ERROR);
1055331Samw 	}
1065331Samw 
1075331Samw 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
1085331Samw 
1095331Samw 	if (rc != 0) {
110*9914Samw@Sun.COM 		smb_rename_set_error(sr, rc);
1116139Sjb150015 		return (SDRC_ERROR);
1125331Samw 	}
1135331Samw 
1146030Sjb150015 	rc = smbsr_encode_empty_result(sr);
1156139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1165331Samw }
1175331Samw 
1185331Samw /*
1195331Samw  * smb_do_rename
1205331Samw  *
121*9914Samw@Sun.COM  * Common code for renaming a file.
1225331Samw  *
123*9914Samw@Sun.COM  * If the source and destination are identical, we go through all
124*9914Samw@Sun.COM  * the checks but we don't actually do the rename.  If the source
125*9914Samw@Sun.COM  * and destination files differ only in case, we do a case-sensitive
126*9914Samw@Sun.COM  * rename.  Otherwise, we do a full case-insensitive rename.
1275331Samw  *
128*9914Samw@Sun.COM  * Returns errno values.
1295331Samw  */
1305331Samw static int
131*9914Samw@Sun.COM smb_do_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
1325331Samw {
1337961SNatalie.Li@Sun.COM 	smb_node_t *src_node;
1345331Samw 	char *dstname;
1355331Samw 	DWORD status;
1365331Samw 	int rc;
1375331Samw 	int count;
1385331Samw 
139*9914Samw@Sun.COM 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0)
1405331Samw 		return (rc);
1415331Samw 
1429343SAfshin.Ardakani@Sun.COM 	src_node = src_fqi->fq_fnode;
1435331Samw 
144*9914Samw@Sun.COM 	if ((rc = smb_rename_check_attr(src_node, src_fqi->fq_sattr)) != 0)
145*9914Samw@Sun.COM 		goto rename_cleanup_nodes;
146*9914Samw@Sun.COM 
1475331Samw 	/*
1485331Samw 	 * Break the oplock before access checks. If a client
1495331Samw 	 * has a file open, this will force a flush or close,
1505331Samw 	 * which may affect the outcome of any share checking.
1515331Samw 	 */
1529021Samw@Sun.COM 	(void) smb_oplock_break(src_node, sr->session, B_FALSE);
1535331Samw 
1545772Sas200622 	for (count = 0; count <= 3; count++) {
1555772Sas200622 		if (count) {
1565772Sas200622 			smb_node_end_crit(src_node);
1575772Sas200622 			delay(MSEC_TO_TICK(400));
1585772Sas200622 		}
1595772Sas200622 
1605772Sas200622 		smb_node_start_crit(src_node, RW_READER);
1615772Sas200622 
1625772Sas200622 		status = smb_node_rename_check(src_node);
1635772Sas200622 
1645772Sas200622 		if (status != NT_STATUS_SHARING_VIOLATION)
1655772Sas200622 			break;
1665772Sas200622 	}
1675772Sas200622 
1685772Sas200622 	if (status == NT_STATUS_SHARING_VIOLATION) {
1695772Sas200622 		smb_node_end_crit(src_node);
170*9914Samw@Sun.COM 		rc = EPIPE;	/* = ERRbadshare */
171*9914Samw@Sun.COM 		goto rename_cleanup_nodes;
1725772Sas200622 	}
1735772Sas200622 
1747348SJose.Borrego@Sun.COM 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
1755772Sas200622 
1765331Samw 	if (status != NT_STATUS_SUCCESS) {
1775772Sas200622 		smb_node_end_crit(src_node);
178*9914Samw@Sun.COM 		rc = EACCES;
179*9914Samw@Sun.COM 		goto rename_cleanup_nodes;
1805331Samw 	}
1815331Samw 
1829343SAfshin.Ardakani@Sun.COM 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
1839343SAfshin.Ardakani@Sun.COM 	    dst_fqi->fq_path.pn_path) == 0) {
1845331Samw 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
1855772Sas200622 			smb_node_end_crit(src_node);
186*9914Samw@Sun.COM 			goto rename_cleanup_nodes;
1875331Samw 		}
1885331Samw 
1895331Samw 		/*
1905331Samw 		 * Because the fqm parameter to smbd_fs_query() was 0,
191*9914Samw@Sun.COM 		 * dst_fqi->fq_fnode may be NULL.
1925331Samw 		 */
1939343SAfshin.Ardakani@Sun.COM 		if (dst_fqi->fq_fnode)
1949343SAfshin.Ardakani@Sun.COM 			smb_node_release(dst_fqi->fq_fnode);
1955331Samw 
1969343SAfshin.Ardakani@Sun.COM 		rc = strcmp(src_fqi->fq_od_name, dst_fqi->fq_last_comp);
1975331Samw 		if (rc == 0) {
1985772Sas200622 			smb_node_end_crit(src_node);
199*9914Samw@Sun.COM 			goto rename_cleanup_nodes;
2005331Samw 		}
2015331Samw 
2025331Samw 		rc = smb_fsop_rename(sr, sr->user_cr,
203*9914Samw@Sun.COM 		    src_fqi->fq_dnode, src_fqi->fq_od_name,
204*9914Samw@Sun.COM 		    dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
2055772Sas200622 
2065772Sas200622 		smb_node_end_crit(src_node);
207*9914Samw@Sun.COM 		if (rc == 0)
208*9914Samw@Sun.COM 			smb_node_notify_change(dst_fqi->fq_dnode);
209*9914Samw@Sun.COM 		goto rename_cleanup_nodes;
2105331Samw 	}
2115331Samw 
2125331Samw 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
2135331Samw 	if (rc != 0) {
2145772Sas200622 		smb_node_end_crit(src_node);
215*9914Samw@Sun.COM 		goto rename_cleanup_nodes;
2165331Samw 	}
2175331Samw 
2185331Samw 	/*
219*9914Samw@Sun.COM 	 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode
220*9914Samw@Sun.COM 	 * is valid (dst_fqi->fq_fnode is NULL).
2215331Samw 	 */
2225331Samw 
2235331Samw 	/*
224*9914Samw@Sun.COM 	 * If the source name is mangled but the source and destination
225*9914Samw@Sun.COM 	 * on-disk names are identical, we'll use the on-disk name.
2265331Samw 	 */
2279343SAfshin.Ardakani@Sun.COM 	if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) &&
2289343SAfshin.Ardakani@Sun.COM 	    (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) {
2299343SAfshin.Ardakani@Sun.COM 		dstname = src_fqi->fq_od_name;
2305331Samw 	} else {
2319343SAfshin.Ardakani@Sun.COM 		dstname = dst_fqi->fq_last_comp;
2325331Samw 	}
2335331Samw 
2345331Samw 	rc = smb_fsop_rename(sr, sr->user_cr,
235*9914Samw@Sun.COM 	    src_fqi->fq_dnode, src_fqi->fq_od_name,
236*9914Samw@Sun.COM 	    dst_fqi->fq_dnode, dstname);
2375331Samw 
2385772Sas200622 	smb_node_end_crit(src_node);
2395772Sas200622 
240*9914Samw@Sun.COM 	if (rc == 0)
241*9914Samw@Sun.COM 		smb_node_notify_change(dst_fqi->fq_dnode);
242*9914Samw@Sun.COM 
243*9914Samw@Sun.COM rename_cleanup_nodes:
2445772Sas200622 	smb_node_release(src_node);
245*9914Samw@Sun.COM 	smb_node_release(src_fqi->fq_dnode);
2465772Sas200622 
247*9914Samw@Sun.COM 	if (dst_fqi->fq_dnode)
248*9914Samw@Sun.COM 		smb_node_release(dst_fqi->fq_dnode);
249*9914Samw@Sun.COM 
250*9914Samw@Sun.COM 	SMB_NULL_FQI_NODES(*src_fqi);
251*9914Samw@Sun.COM 	SMB_NULL_FQI_NODES(*dst_fqi);
2525331Samw 	return (rc);
2535331Samw }
254*9914Samw@Sun.COM 
255*9914Samw@Sun.COM /*
256*9914Samw@Sun.COM  * smb_com_nt_rename
257*9914Samw@Sun.COM  *
258*9914Samw@Sun.COM  * Rename a file. Files OldFileName must exist and NewFileName must not.
259*9914Samw@Sun.COM  * Both pathnames must be relative to the Tid specified in the request.
260*9914Samw@Sun.COM  * Open files may be renamed.
261*9914Samw@Sun.COM  *
262*9914Samw@Sun.COM  * Multiple files may be renamed in response to a single request as Rename
263*9914Samw@Sun.COM  * File supports wildcards in the file name (last component of the path).
264*9914Samw@Sun.COM  * NOTE: we don't support rename with wildcards.
265*9914Samw@Sun.COM  *
266*9914Samw@Sun.COM  * SearchAttributes indicates the attributes that the target file(s) must
267*9914Samw@Sun.COM  * have. If SearchAttributes is zero then only normal files are renamed.
268*9914Samw@Sun.COM  * If the system file or hidden attributes are specified then the rename
269*9914Samw@Sun.COM  * is inclusive - both the specified type(s) of files and normal files are
270*9914Samw@Sun.COM  * renamed. The encoding of SearchAttributes is described in section 3.10
271*9914Samw@Sun.COM  * - File Attribute Encoding.
272*9914Samw@Sun.COM  *
273*9914Samw@Sun.COM  *  Client Request                     Description
274*9914Samw@Sun.COM  *  =================================  ==================================
275*9914Samw@Sun.COM  *  UCHAR WordCount;                   Count of parameter words = 4
276*9914Samw@Sun.COM  *  USHORT SearchAttributes;
277*9914Samw@Sun.COM  *  USHORT InformationLevel;           0x0103 Create a hard link
278*9914Samw@Sun.COM  *                                     0x0104 In-place rename
279*9914Samw@Sun.COM  *                                     0x0105 Move (rename) a file
280*9914Samw@Sun.COM  *  ULONG ClusterCount                 Servers should ignore this value
281*9914Samw@Sun.COM  *  USHORT ByteCount;                  Count of data bytes; min = 4
282*9914Samw@Sun.COM  *  UCHAR Buffer[];                    Buffer containing:
283*9914Samw@Sun.COM  *                                     UCHAR BufferFormat1 0x04
284*9914Samw@Sun.COM  *                                     UCHAR OldFileName[] OldFileName
285*9914Samw@Sun.COM  *                                     UCHAR BufferFormat1 0x04
286*9914Samw@Sun.COM  *                                     UCHAR OldFileName[] NewFileName
287*9914Samw@Sun.COM  *
288*9914Samw@Sun.COM  *  Server Response                    Description
289*9914Samw@Sun.COM  *  =================================  ==================================
290*9914Samw@Sun.COM  *  UCHAR WordCount;                   Count of parameter words = 0
291*9914Samw@Sun.COM  *  UCHAR ByteCount;                   Count of data bytes = 0
292*9914Samw@Sun.COM  */
293*9914Samw@Sun.COM smb_sdrc_t
294*9914Samw@Sun.COM smb_pre_nt_rename(smb_request_t *sr)
295*9914Samw@Sun.COM {
296*9914Samw@Sun.COM 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
297*9914Samw@Sun.COM 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
298*9914Samw@Sun.COM 	uint32_t clusters;
299*9914Samw@Sun.COM 	int rc;
300*9914Samw@Sun.COM 
301*9914Samw@Sun.COM 	rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr,
302*9914Samw@Sun.COM 	    &sr->arg.dirop.info_level, &clusters);
303*9914Samw@Sun.COM 	if (rc == 0) {
304*9914Samw@Sun.COM 		rc = smbsr_decode_data(sr, "%SS", sr,
305*9914Samw@Sun.COM 		    &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path);
306*9914Samw@Sun.COM 
307*9914Samw@Sun.COM 		dst_fqi->fq_sattr = 0;
308*9914Samw@Sun.COM 	}
309*9914Samw@Sun.COM 
310*9914Samw@Sun.COM 	DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr,
311*9914Samw@Sun.COM 	    struct dirop *, &sr->arg.dirop);
312*9914Samw@Sun.COM 
313*9914Samw@Sun.COM 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
314*9914Samw@Sun.COM }
315*9914Samw@Sun.COM 
316*9914Samw@Sun.COM void
317*9914Samw@Sun.COM smb_post_nt_rename(smb_request_t *sr)
318*9914Samw@Sun.COM {
319*9914Samw@Sun.COM 	DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr);
320*9914Samw@Sun.COM }
321*9914Samw@Sun.COM 
322*9914Samw@Sun.COM smb_sdrc_t
323*9914Samw@Sun.COM smb_com_nt_rename(smb_request_t *sr)
324*9914Samw@Sun.COM {
325*9914Samw@Sun.COM 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
326*9914Samw@Sun.COM 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
327*9914Samw@Sun.COM 	int rc;
328*9914Samw@Sun.COM 
329*9914Samw@Sun.COM 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
330*9914Samw@Sun.COM 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
331*9914Samw@Sun.COM 		    ERRDOS, ERROR_ACCESS_DENIED);
332*9914Samw@Sun.COM 		return (SDRC_ERROR);
333*9914Samw@Sun.COM 	}
334*9914Samw@Sun.COM 
335*9914Samw@Sun.COM 	if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) {
336*9914Samw@Sun.COM 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
337*9914Samw@Sun.COM 		    ERRDOS, ERROR_BAD_PATHNAME);
338*9914Samw@Sun.COM 		return (SDRC_ERROR);
339*9914Samw@Sun.COM 	}
340*9914Samw@Sun.COM 
341*9914Samw@Sun.COM 	switch (sr->arg.dirop.info_level) {
342*9914Samw@Sun.COM 	case SMB_NT_RENAME_SET_LINK_INFO:
343*9914Samw@Sun.COM 		rc = smb_make_link(sr, src_fqi, dst_fqi);
344*9914Samw@Sun.COM 		break;
345*9914Samw@Sun.COM 	case SMB_NT_RENAME_RENAME_FILE:
346*9914Samw@Sun.COM 	case SMB_NT_RENAME_MOVE_FILE:
347*9914Samw@Sun.COM 		rc = smb_do_rename(sr, src_fqi, dst_fqi);
348*9914Samw@Sun.COM 		break;
349*9914Samw@Sun.COM 	case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
350*9914Samw@Sun.COM 		rc = EINVAL;
351*9914Samw@Sun.COM 		break;
352*9914Samw@Sun.COM 	default:
353*9914Samw@Sun.COM 		rc = EACCES;
354*9914Samw@Sun.COM 		break;
355*9914Samw@Sun.COM 	}
356*9914Samw@Sun.COM 
357*9914Samw@Sun.COM 	if (rc != 0) {
358*9914Samw@Sun.COM 		smb_rename_set_error(sr, rc);
359*9914Samw@Sun.COM 		return (SDRC_ERROR);
360*9914Samw@Sun.COM 	}
361*9914Samw@Sun.COM 
362*9914Samw@Sun.COM 	rc = smbsr_encode_empty_result(sr);
363*9914Samw@Sun.COM 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
364*9914Samw@Sun.COM }
365*9914Samw@Sun.COM 
366*9914Samw@Sun.COM /*
367*9914Samw@Sun.COM  * smb_make_link
368*9914Samw@Sun.COM  *
369*9914Samw@Sun.COM  * Common code for creating a hard link (adding an additional name
370*9914Samw@Sun.COM  * for a file.
371*9914Samw@Sun.COM  *
372*9914Samw@Sun.COM  * If the source and destination are identical, we go through all
373*9914Samw@Sun.COM  * the checks but we don't create a link.
374*9914Samw@Sun.COM  *
375*9914Samw@Sun.COM  * Returns errno values.
376*9914Samw@Sun.COM  */
377*9914Samw@Sun.COM static int
378*9914Samw@Sun.COM smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
379*9914Samw@Sun.COM {
380*9914Samw@Sun.COM 	smb_node_t *src_fnode;
381*9914Samw@Sun.COM 	DWORD status;
382*9914Samw@Sun.COM 	int rc;
383*9914Samw@Sun.COM 	int count;
384*9914Samw@Sun.COM 
385*9914Samw@Sun.COM 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0)
386*9914Samw@Sun.COM 		return (rc);
387*9914Samw@Sun.COM 
388*9914Samw@Sun.COM 	src_fnode = src_fqi->fq_fnode;
389*9914Samw@Sun.COM 
390*9914Samw@Sun.COM 	if ((rc = smb_rename_check_attr(src_fnode, src_fqi->fq_sattr)) != 0)
391*9914Samw@Sun.COM 		goto link_cleanup_nodes;
392*9914Samw@Sun.COM 
393*9914Samw@Sun.COM 	/*
394*9914Samw@Sun.COM 	 * Break the oplock before access checks. If a client
395*9914Samw@Sun.COM 	 * has a file open, this will force a flush or close,
396*9914Samw@Sun.COM 	 * which may affect the outcome of any share checking.
397*9914Samw@Sun.COM 	 */
398*9914Samw@Sun.COM 	(void) smb_oplock_break(src_fnode, sr->session, B_FALSE);
399*9914Samw@Sun.COM 
400*9914Samw@Sun.COM 	for (count = 0; count <= 3; count++) {
401*9914Samw@Sun.COM 		if (count) {
402*9914Samw@Sun.COM 			smb_node_end_crit(src_fnode);
403*9914Samw@Sun.COM 			delay(MSEC_TO_TICK(400));
404*9914Samw@Sun.COM 		}
405*9914Samw@Sun.COM 
406*9914Samw@Sun.COM 		smb_node_start_crit(src_fnode, RW_READER);
407*9914Samw@Sun.COM 		status = smb_node_rename_check(src_fnode);
408*9914Samw@Sun.COM 
409*9914Samw@Sun.COM 		if (status != NT_STATUS_SHARING_VIOLATION)
410*9914Samw@Sun.COM 			break;
411*9914Samw@Sun.COM 	}
412*9914Samw@Sun.COM 
413*9914Samw@Sun.COM 	if (status == NT_STATUS_SHARING_VIOLATION) {
414*9914Samw@Sun.COM 		smb_node_end_crit(src_fnode);
415*9914Samw@Sun.COM 		rc = EPIPE;	/* = ERRbadshare */
416*9914Samw@Sun.COM 		goto link_cleanup_nodes;
417*9914Samw@Sun.COM 	}
418*9914Samw@Sun.COM 
419*9914Samw@Sun.COM 	status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE);
420*9914Samw@Sun.COM 
421*9914Samw@Sun.COM 	if (status != NT_STATUS_SUCCESS) {
422*9914Samw@Sun.COM 		smb_node_end_crit(src_fnode);
423*9914Samw@Sun.COM 		rc = EACCES;
424*9914Samw@Sun.COM 		goto link_cleanup_nodes;
425*9914Samw@Sun.COM 	}
426*9914Samw@Sun.COM 
427*9914Samw@Sun.COM 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
428*9914Samw@Sun.COM 	    dst_fqi->fq_path.pn_path) == 0) {
429*9914Samw@Sun.COM 		smb_node_end_crit(src_fnode);
430*9914Samw@Sun.COM 		rc = 0;
431*9914Samw@Sun.COM 		goto link_cleanup_nodes;
432*9914Samw@Sun.COM 	}
433*9914Samw@Sun.COM 
434*9914Samw@Sun.COM 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
435*9914Samw@Sun.COM 	if (rc != 0) {
436*9914Samw@Sun.COM 		smb_node_end_crit(src_fnode);
437*9914Samw@Sun.COM 		goto link_cleanup_nodes;
438*9914Samw@Sun.COM 	}
439*9914Samw@Sun.COM 
440*9914Samw@Sun.COM 	/*
441*9914Samw@Sun.COM 	 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode
442*9914Samw@Sun.COM 	 * is valid (dst_fqi->fq_fnode is NULL).
443*9914Samw@Sun.COM 	 */
444*9914Samw@Sun.COM 	rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode,
445*9914Samw@Sun.COM 	    dst_fqi->fq_last_comp);
446*9914Samw@Sun.COM 
447*9914Samw@Sun.COM 	smb_node_end_crit(src_fnode);
448*9914Samw@Sun.COM 
449*9914Samw@Sun.COM 	if (rc == 0)
450*9914Samw@Sun.COM 		smb_node_notify_change(dst_fqi->fq_dnode);
451*9914Samw@Sun.COM 
452*9914Samw@Sun.COM link_cleanup_nodes:
453*9914Samw@Sun.COM 	smb_node_release(src_fnode);
454*9914Samw@Sun.COM 	smb_node_release(src_fqi->fq_dnode);
455*9914Samw@Sun.COM 
456*9914Samw@Sun.COM 	if (dst_fqi->fq_dnode)
457*9914Samw@Sun.COM 		smb_node_release(dst_fqi->fq_dnode);
458*9914Samw@Sun.COM 
459*9914Samw@Sun.COM 	SMB_NULL_FQI_NODES(*src_fqi);
460*9914Samw@Sun.COM 	SMB_NULL_FQI_NODES(*dst_fqi);
461*9914Samw@Sun.COM 	return (rc);
462*9914Samw@Sun.COM }
463*9914Samw@Sun.COM 
464*9914Samw@Sun.COM static int
465*9914Samw@Sun.COM smb_rename_check_attr(smb_node_t *node, uint16_t sattr)
466*9914Samw@Sun.COM {
467*9914Samw@Sun.COM 	uint16_t dosattr = smb_node_get_dosattr(node);
468*9914Samw@Sun.COM 
469*9914Samw@Sun.COM 	if ((dosattr & FILE_ATTRIBUTE_HIDDEN) && !(SMB_SEARCH_HIDDEN(sattr)))
470*9914Samw@Sun.COM 		return (ESRCH);
471*9914Samw@Sun.COM 
472*9914Samw@Sun.COM 	if ((dosattr & FILE_ATTRIBUTE_SYSTEM) && !(SMB_SEARCH_SYSTEM(sattr)))
473*9914Samw@Sun.COM 		return (ESRCH);
474*9914Samw@Sun.COM 
475*9914Samw@Sun.COM 	return (0);
476*9914Samw@Sun.COM }
477*9914Samw@Sun.COM 
478*9914Samw@Sun.COM /*
479*9914Samw@Sun.COM  * The following values are based on observed WFWG, Windows 9x, Windows NT
480*9914Samw@Sun.COM  * and Windows 2000 behaviour.
481*9914Samw@Sun.COM  *
482*9914Samw@Sun.COM  * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
483*9914Samw@Sun.COM  *
484*9914Samw@Sun.COM  * Windows 95 clients don't see the problem because the target is deleted
485*9914Samw@Sun.COM  * before the rename request.
486*9914Samw@Sun.COM  */
487*9914Samw@Sun.COM static void
488*9914Samw@Sun.COM smb_rename_set_error(smb_request_t *sr, int errnum)
489*9914Samw@Sun.COM {
490*9914Samw@Sun.COM 	static struct {
491*9914Samw@Sun.COM 		int errnum;
492*9914Samw@Sun.COM 		uint16_t errcode;
493*9914Samw@Sun.COM 		uint32_t status32;
494*9914Samw@Sun.COM 	} rc_map[] = {
495*9914Samw@Sun.COM 	{ EEXIST, ERROR_ALREADY_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
496*9914Samw@Sun.COM 	{ EPIPE,  ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
497*9914Samw@Sun.COM 	{ ENOENT, ERROR_FILE_NOT_FOUND,	NT_STATUS_OBJECT_NAME_NOT_FOUND },
498*9914Samw@Sun.COM 	{ ESRCH,  ERROR_FILE_NOT_FOUND,	NT_STATUS_NO_SUCH_FILE },
499*9914Samw@Sun.COM 	{ EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
500*9914Samw@Sun.COM 	{ EACCES, ERROR_ACCESS_DENIED,	NT_STATUS_ACCESS_DENIED }
501*9914Samw@Sun.COM 	};
502*9914Samw@Sun.COM 
503*9914Samw@Sun.COM 	int i;
504*9914Samw@Sun.COM 
505*9914Samw@Sun.COM 	if (errnum == 0)
506*9914Samw@Sun.COM 		return;
507*9914Samw@Sun.COM 
508*9914Samw@Sun.COM 	for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
509*9914Samw@Sun.COM 		if (rc_map[i].errnum == errnum) {
510*9914Samw@Sun.COM 			smbsr_error(sr, rc_map[i].status32,
511*9914Samw@Sun.COM 			    ERRDOS, rc_map[i].errcode);
512*9914Samw@Sun.COM 			return;
513*9914Samw@Sun.COM 		}
514*9914Samw@Sun.COM 	}
515*9914Samw@Sun.COM 
516*9914Samw@Sun.COM 	smbsr_errno(sr, errnum);
517*9914Samw@Sun.COM }
518