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