xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 5331:3047ad28a67b)
1*5331Samw /*
2*5331Samw  * CDDL HEADER START
3*5331Samw  *
4*5331Samw  * The contents of this file are subject to the terms of the
5*5331Samw  * Common Development and Distribution License (the "License").
6*5331Samw  * You may not use this file except in compliance with the License.
7*5331Samw  *
8*5331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5331Samw  * or http://www.opensolaris.org/os/licensing.
10*5331Samw  * See the License for the specific language governing permissions
11*5331Samw  * and limitations under the License.
12*5331Samw  *
13*5331Samw  * When distributing Covered Code, include this CDDL HEADER in each
14*5331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5331Samw  * If applicable, add the following below this CDDL HEADER, with the
16*5331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
17*5331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5331Samw  *
19*5331Samw  * CDDL HEADER END
20*5331Samw  */
21*5331Samw /*
22*5331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5331Samw  * Use is subject to license terms.
24*5331Samw  */
25*5331Samw 
26*5331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5331Samw 
28*5331Samw #include <smbsrv/nterror.h>
29*5331Samw #include <sys/synch.h>
30*5331Samw #include <smbsrv/smb_incl.h>
31*5331Samw #include <smbsrv/smb_fsops.h>
32*5331Samw 
33*5331Samw static int smb_do_rename(struct smb_request *sr,
34*5331Samw 				struct smb_fqi *src_fqi,
35*5331Samw 				struct smb_fqi *dst_fqi);
36*5331Samw 
37*5331Samw 
38*5331Samw /*
39*5331Samw  * smb_com_rename
40*5331Samw  *
41*5331Samw  * Rename a file. Files OldFileName must exist and NewFileName must not.
42*5331Samw  * Both pathnames must be relative to the Tid specified in the request.
43*5331Samw  * Open files may be renamed.
44*5331Samw  *
45*5331Samw  * Multiple files may be renamed in response to a single request as Rename
46*5331Samw  * File supports wildcards in the file name (last component of the path).
47*5331Samw  * NOTE: we don't support rename with wildcards.
48*5331Samw  *
49*5331Samw  * SearchAttributes indicates the attributes that the target file(s) must
50*5331Samw  * have. If SearchAttributes is zero then only normal files are renamed.
51*5331Samw  * If the system file or hidden attributes are specified then the rename
52*5331Samw  * is inclusive - both the specified type(s) of files and normal files are
53*5331Samw  * renamed. The encoding of SearchAttributes is described in section 3.10
54*5331Samw  * - File Attribute Encoding.
55*5331Samw  */
56*5331Samw int
57*5331Samw smb_com_rename(struct smb_request *sr)
58*5331Samw {
59*5331Samw 	static kmutex_t mutex;
60*5331Samw 	struct smb_fqi *src_fqi;
61*5331Samw 	struct smb_fqi *dst_fqi;
62*5331Samw 	struct smb_node *dst_node;
63*5331Samw 	int rc;
64*5331Samw 
65*5331Samw 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
66*5331Samw 		smbsr_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED,
67*5331Samw 		    ERRDOS, ERROR_ACCESS_DENIED);
68*5331Samw 		/* NOTREACHED */
69*5331Samw 	}
70*5331Samw 
71*5331Samw 	src_fqi = &sr->arg.dirop.fqi;
72*5331Samw 	dst_fqi = &sr->arg.dirop.dst_fqi;
73*5331Samw 
74*5331Samw 	if (smbsr_decode_vwv(sr, "w", &src_fqi->srch_attr) != 0) {
75*5331Samw 		smbsr_decode_error(sr);
76*5331Samw 		/* NOTREACHED */
77*5331Samw 	}
78*5331Samw 
79*5331Samw 	rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path, &dst_fqi->path);
80*5331Samw 	if (rc != 0) {
81*5331Samw 		smbsr_decode_error(sr);
82*5331Samw 		/* NOTREACHED */
83*5331Samw 	}
84*5331Samw 
85*5331Samw 	dst_fqi->srch_attr = 0;
86*5331Samw 
87*5331Samw 	mutex_enter(&mutex);
88*5331Samw 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
89*5331Samw 	mutex_exit(&mutex);
90*5331Samw 
91*5331Samw 	if (rc != 0) {
92*5331Samw 		/*
93*5331Samw 		 * ERROR_FILE_EXISTS doesn't work for Windows98 clients.
94*5331Samw 		 *
95*5331Samw 		 * Windows95 clients don't see this problem because the target
96*5331Samw 		 * is deleted before the rename request.
97*5331Samw 		 *
98*5331Samw 		 * The following values are based on observed WFWG, Win9x,
99*5331Samw 		 * NT and W2K client behaviour.
100*5331Samw 		 */
101*5331Samw 		if (rc == EEXIST) {
102*5331Samw 			smbsr_raise_cifs_error(sr,
103*5331Samw 			    NT_STATUS_OBJECT_NAME_COLLISION,
104*5331Samw 			    ERRDOS, ERROR_ALREADY_EXISTS);
105*5331Samw 			/* NOTREACHED */
106*5331Samw 		}
107*5331Samw 
108*5331Samw 		if (rc == EPIPE) {
109*5331Samw 			smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION,
110*5331Samw 			    ERRDOS, ERROR_SHARING_VIOLATION);
111*5331Samw 			/* NOTREACHED */
112*5331Samw 		}
113*5331Samw 
114*5331Samw 		smbsr_raise_errno(sr, rc);
115*5331Samw 		/* NOTREACHED */
116*5331Samw 	}
117*5331Samw 
118*5331Samw 	if (src_fqi->dir_snode)
119*5331Samw 		smb_node_release(src_fqi->dir_snode);
120*5331Samw 
121*5331Samw 	dst_node = dst_fqi->dir_snode;
122*5331Samw 	if (dst_node) {
123*5331Samw 		if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
124*5331Samw 			dst_node->flags |= NODE_FLAGS_CHANGED;
125*5331Samw 			smb_process_node_notify_change_queue(dst_node);
126*5331Samw 		}
127*5331Samw 		smb_node_release(dst_node);
128*5331Samw 	}
129*5331Samw 
130*5331Samw 	SMB_NULL_FQI_NODES(*src_fqi);
131*5331Samw 	SMB_NULL_FQI_NODES(*dst_fqi);
132*5331Samw 
133*5331Samw 	smbsr_encode_empty_result(sr);
134*5331Samw 
135*5331Samw 	return (SDRC_NORMAL_REPLY);
136*5331Samw }
137*5331Samw 
138*5331Samw /*
139*5331Samw  * smb_rename_share_check
140*5331Samw  *
141*5331Samw  * An open file can be renamed if
142*5331Samw  *
143*5331Samw  *      1. isn't opened for data writing or deleting
144*5331Samw  *
145*5331Samw  *  2. Opened with "Deny Delete" share mode
146*5331Samw  *         But not opened for data reading or executing
147*5331Samw  *         (opened for accessing meta data)
148*5331Samw  */
149*5331Samw DWORD
150*5331Samw smb_rename_share_check(struct smb_node *node)
151*5331Samw {
152*5331Samw 	struct smb_ofile *open;
153*5331Samw 
154*5331Samw 	if (node == 0 || node->n_refcnt <= 1)
155*5331Samw 		return (NT_STATUS_SUCCESS);
156*5331Samw 
157*5331Samw 	smb_llist_enter(&node->n_ofile_list, RW_READER);
158*5331Samw 	open = smb_llist_head(&node->n_ofile_list);
159*5331Samw 	while (open) {
160*5331Samw 		if (open->f_granted_access &
161*5331Samw 		    (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) {
162*5331Samw 			smb_llist_exit(&node->n_ofile_list);
163*5331Samw 			return (NT_STATUS_SHARING_VIOLATION);
164*5331Samw 		}
165*5331Samw 
166*5331Samw 		if ((open->f_share_access & FILE_SHARE_DELETE) == 0) {
167*5331Samw 			if (open->f_granted_access &
168*5331Samw 			    (FILE_READ_DATA | FILE_EXECUTE)) {
169*5331Samw 				smb_llist_exit(&node->n_ofile_list);
170*5331Samw 				return (NT_STATUS_SHARING_VIOLATION);
171*5331Samw 			}
172*5331Samw 		}
173*5331Samw 		open = smb_llist_next(&node->n_ofile_list, open);
174*5331Samw 	}
175*5331Samw 	smb_llist_exit(&node->n_ofile_list);
176*5331Samw 	return (NT_STATUS_SUCCESS);
177*5331Samw }
178*5331Samw 
179*5331Samw 
180*5331Samw /*
181*5331Samw  * smb_do_rename
182*5331Samw  *
183*5331Samw  * Backend to smb_com_rename to ensure that the rename operation is atomic.
184*5331Samw  * This function should be called within a mutual exclusion region. If the
185*5331Samw  * source and destination are identical, we don't actually do a rename, we
186*5331Samw  * just check that the conditions are right. If the source and destination
187*5331Samw  * files differ only in case, we a case-sensitive rename. Otherwise, we do
188*5331Samw  * a full case-insensitive rename.
189*5331Samw  *
190*5331Samw  * This function should always return errno values.
191*5331Samw  *
192*5331Samw  * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi
193*5331Samw  * are not released in this routine but in smb_com_rename().
194*5331Samw  */
195*5331Samw static int
196*5331Samw smb_do_rename(
197*5331Samw     struct smb_request *sr,
198*5331Samw     struct smb_fqi *src_fqi,
199*5331Samw     struct smb_fqi *dst_fqi)
200*5331Samw {
201*5331Samw 	struct smb_node *src_node;
202*5331Samw 	char *dstname;
203*5331Samw 	DWORD status;
204*5331Samw 	int rc;
205*5331Samw 	int count;
206*5331Samw 
207*5331Samw 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) {
208*5331Samw 		return (rc);
209*5331Samw 	}
210*5331Samw 
211*5331Samw 	src_node = src_fqi->last_snode;
212*5331Samw 
213*5331Samw 	/*
214*5331Samw 	 * Break the oplock before access checks. If a client
215*5331Samw 	 * has a file open, this will force a flush or close,
216*5331Samw 	 * which may affect the outcome of any share checking.
217*5331Samw 	 */
218*5331Samw 	if (OPLOCKS_IN_FORCE(src_node)) {
219*5331Samw 		status = smb_break_oplock(sr, src_node);
220*5331Samw 
221*5331Samw 		if (status != NT_STATUS_SUCCESS) {
222*5331Samw 			smb_node_release(src_node);
223*5331Samw 			smb_node_release(src_fqi->dir_snode);
224*5331Samw 
225*5331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
226*5331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
227*5331Samw 			return (EACCES);
228*5331Samw 		}
229*5331Samw 	}
230*5331Samw 
231*5331Samw 	status = smb_lock_range_access(sr, src_node, 0, 0, FILE_WRITE_DATA);
232*5331Samw 	if (status != NT_STATUS_SUCCESS) {
233*5331Samw 		smb_node_release(src_node);
234*5331Samw 		smb_node_release(src_fqi->dir_snode);
235*5331Samw 
236*5331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
237*5331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
238*5331Samw 		return (EACCES);
239*5331Samw 	}
240*5331Samw 
241*5331Samw 
242*5331Samw 	for (count = 0; count <= 3; count++) {
243*5331Samw 		if (count)
244*5331Samw 			delay(MSEC_TO_TICK(400));
245*5331Samw 		status = smb_rename_share_check(src_node);
246*5331Samw 		if (status != NT_STATUS_SHARING_VIOLATION)
247*5331Samw 			break;
248*5331Samw 	}
249*5331Samw 
250*5331Samw 	smb_node_release(src_node);
251*5331Samw 
252*5331Samw 	if (status == NT_STATUS_SHARING_VIOLATION) {
253*5331Samw 		smb_node_release(src_fqi->dir_snode);
254*5331Samw 
255*5331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
256*5331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
257*5331Samw 		return (EPIPE); /* = ERRbadshare */
258*5331Samw 	}
259*5331Samw 
260*5331Samw 	if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) {
261*5331Samw 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
262*5331Samw 			smb_node_release(src_fqi->dir_snode);
263*5331Samw 
264*5331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
265*5331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
266*5331Samw 			return (rc);
267*5331Samw 		}
268*5331Samw 
269*5331Samw 		/*
270*5331Samw 		 * Because the fqm parameter to smbd_fs_query() was 0,
271*5331Samw 		 * a successful return value means that dst_fqi->last_snode
272*5331Samw 		 * may be NULL.
273*5331Samw 		 */
274*5331Samw 		if (dst_fqi->last_snode)
275*5331Samw 			smb_node_release(dst_fqi->last_snode);
276*5331Samw 
277*5331Samw 		rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp);
278*5331Samw 		if (rc == 0) {
279*5331Samw 			smb_node_release(src_fqi->dir_snode);
280*5331Samw 			smb_node_release(dst_fqi->dir_snode);
281*5331Samw 
282*5331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
283*5331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
284*5331Samw 			return (0);
285*5331Samw 		}
286*5331Samw 
287*5331Samw 		rc = smb_fsop_rename(sr, sr->user_cr,
288*5331Samw 		    src_fqi->dir_snode,
289*5331Samw 		    src_fqi->last_comp_od,
290*5331Samw 		    dst_fqi->dir_snode,
291*5331Samw 		    dst_fqi->last_comp);
292*5331Samw 
293*5331Samw 		if (rc != 0) {
294*5331Samw 			smb_node_release(src_fqi->dir_snode);
295*5331Samw 			smb_node_release(dst_fqi->dir_snode);
296*5331Samw 
297*5331Samw 			SMB_NULL_FQI_NODES(*src_fqi);
298*5331Samw 			SMB_NULL_FQI_NODES(*dst_fqi);
299*5331Samw 		}
300*5331Samw 		return (rc);
301*5331Samw 	}
302*5331Samw 
303*5331Samw 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
304*5331Samw 	if (rc != 0) {
305*5331Samw 		smb_node_release(src_fqi->dir_snode);
306*5331Samw 
307*5331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
308*5331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
309*5331Samw 		return (rc);
310*5331Samw 	}
311*5331Samw 
312*5331Samw 	/*
313*5331Samw 	 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return
314*5331Samw 	 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode
315*5331Samw 	 * is NULL).
316*5331Samw 	 */
317*5331Samw 
318*5331Samw 	/*
319*5331Samw 	 * Use the unmangled form of the destination name if the
320*5331Samw 	 * source and destination names are the same and the source
321*5331Samw 	 * name is mangled.  (We are taking a chance here, assuming
322*5331Samw 	 * that this is what the user wants.)
323*5331Samw 	 */
324*5331Samw 
325*5331Samw 	if ((smb_maybe_mangled_name(src_fqi->last_comp)) &&
326*5331Samw 	    (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) {
327*5331Samw 		dstname = src_fqi->last_comp_od;
328*5331Samw 	} else {
329*5331Samw 		dstname = dst_fqi->last_comp;
330*5331Samw 	}
331*5331Samw 
332*5331Samw 	rc = smb_fsop_rename(sr, sr->user_cr,
333*5331Samw 	    src_fqi->dir_snode,
334*5331Samw 	    src_fqi->last_comp_od,
335*5331Samw 	    dst_fqi->dir_snode,
336*5331Samw 	    dstname);
337*5331Samw 
338*5331Samw 	if (rc != 0) {
339*5331Samw 		smb_node_release(src_fqi->dir_snode);
340*5331Samw 		smb_node_release(dst_fqi->dir_snode);
341*5331Samw 
342*5331Samw 		SMB_NULL_FQI_NODES(*src_fqi);
343*5331Samw 		SMB_NULL_FQI_NODES(*dst_fqi);
344*5331Samw 	}
345*5331Samw 
346*5331Samw 	return (rc);
347*5331Samw }
348