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