xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 1be2e5dfebda7cac010af97aae7a3a1b45649aed)
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 2007 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 
33 static int smb_do_rename(struct smb_request *sr,
34 				struct smb_fqi *src_fqi,
35 				struct smb_fqi *dst_fqi);
36 
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 int
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_raise_cifs_error(sr, NT_STATUS_ACCESS_DENIED,
67 		    ERRDOS, ERROR_ACCESS_DENIED);
68 		/* NOTREACHED */
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 		smbsr_decode_error(sr);
76 		/* NOTREACHED */
77 	}
78 
79 	rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->path, &dst_fqi->path);
80 	if (rc != 0) {
81 		smbsr_decode_error(sr);
82 		/* NOTREACHED */
83 	}
84 
85 	dst_fqi->srch_attr = 0;
86 
87 	mutex_enter(&mutex);
88 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
89 	mutex_exit(&mutex);
90 
91 	if (rc != 0) {
92 		/*
93 		 * ERROR_FILE_EXISTS doesn't work for Windows98 clients.
94 		 *
95 		 * Windows95 clients don't see this problem because the target
96 		 * is deleted before the rename request.
97 		 *
98 		 * The following values are based on observed WFWG, Win9x,
99 		 * NT and W2K client behaviour.
100 		 */
101 		if (rc == EEXIST) {
102 			smbsr_raise_cifs_error(sr,
103 			    NT_STATUS_OBJECT_NAME_COLLISION,
104 			    ERRDOS, ERROR_ALREADY_EXISTS);
105 			/* NOTREACHED */
106 		}
107 
108 		if (rc == EPIPE) {
109 			smbsr_raise_cifs_error(sr, NT_STATUS_SHARING_VIOLATION,
110 			    ERRDOS, ERROR_SHARING_VIOLATION);
111 			/* NOTREACHED */
112 		}
113 
114 		smbsr_raise_errno(sr, rc);
115 		/* NOTREACHED */
116 	}
117 
118 	if (src_fqi->dir_snode)
119 		smb_node_release(src_fqi->dir_snode);
120 
121 	dst_node = dst_fqi->dir_snode;
122 	if (dst_node) {
123 		if (dst_node->flags & NODE_FLAGS_NOTIFY_CHANGE) {
124 			dst_node->flags |= NODE_FLAGS_CHANGED;
125 			smb_process_node_notify_change_queue(dst_node);
126 		}
127 		smb_node_release(dst_node);
128 	}
129 
130 	SMB_NULL_FQI_NODES(*src_fqi);
131 	SMB_NULL_FQI_NODES(*dst_fqi);
132 
133 	smbsr_encode_empty_result(sr);
134 
135 	return (SDRC_NORMAL_REPLY);
136 }
137 
138 /*
139  * smb_rename_share_check
140  *
141  * An open file can be renamed if
142  *
143  *      1. isn't opened for data writing or deleting
144  *
145  *  2. Opened with "Deny Delete" share mode
146  *         But not opened for data reading or executing
147  *         (opened for accessing meta data)
148  */
149 DWORD
150 smb_rename_share_check(struct smb_node *node)
151 {
152 	struct smb_ofile *open;
153 
154 	if (node == 0 || node->n_refcnt <= 1)
155 		return (NT_STATUS_SUCCESS);
156 
157 	smb_llist_enter(&node->n_ofile_list, RW_READER);
158 	open = smb_llist_head(&node->n_ofile_list);
159 	while (open) {
160 		if (open->f_granted_access &
161 		    (FILE_WRITE_DATA | FILE_APPEND_DATA | DELETE)) {
162 			smb_llist_exit(&node->n_ofile_list);
163 			return (NT_STATUS_SHARING_VIOLATION);
164 		}
165 
166 		if ((open->f_share_access & FILE_SHARE_DELETE) == 0) {
167 			if (open->f_granted_access &
168 			    (FILE_READ_DATA | FILE_EXECUTE)) {
169 				smb_llist_exit(&node->n_ofile_list);
170 				return (NT_STATUS_SHARING_VIOLATION);
171 			}
172 		}
173 		open = smb_llist_next(&node->n_ofile_list, open);
174 	}
175 	smb_llist_exit(&node->n_ofile_list);
176 	return (NT_STATUS_SUCCESS);
177 }
178 
179 
180 /*
181  * smb_do_rename
182  *
183  * Backend to smb_com_rename to ensure that the rename operation is atomic.
184  * This function should be called within a mutual exclusion region. If the
185  * source and destination are identical, we don't actually do a rename, we
186  * just check that the conditions are right. If the source and destination
187  * files differ only in case, we a case-sensitive rename. Otherwise, we do
188  * a full case-insensitive rename.
189  *
190  * This function should always return errno values.
191  *
192  * Upon success, the last_snode's and dir_snode's of both src_fqi and dst_fqi
193  * are not released in this routine but in smb_com_rename().
194  */
195 static int
196 smb_do_rename(
197     struct smb_request *sr,
198     struct smb_fqi *src_fqi,
199     struct smb_fqi *dst_fqi)
200 {
201 	struct smb_node *src_node;
202 	char *dstname;
203 	DWORD status;
204 	int rc;
205 	int count;
206 
207 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) {
208 		return (rc);
209 	}
210 
211 	src_node = src_fqi->last_snode;
212 
213 	/*
214 	 * Break the oplock before access checks. If a client
215 	 * has a file open, this will force a flush or close,
216 	 * which may affect the outcome of any share checking.
217 	 */
218 	if (OPLOCKS_IN_FORCE(src_node)) {
219 		status = smb_break_oplock(sr, src_node);
220 
221 		if (status != NT_STATUS_SUCCESS) {
222 			smb_node_release(src_node);
223 			smb_node_release(src_fqi->dir_snode);
224 
225 			SMB_NULL_FQI_NODES(*src_fqi);
226 			SMB_NULL_FQI_NODES(*dst_fqi);
227 			return (EACCES);
228 		}
229 	}
230 
231 	status = smb_lock_range_access(sr, src_node, 0, 0, FILE_WRITE_DATA);
232 	if (status != NT_STATUS_SUCCESS) {
233 		smb_node_release(src_node);
234 		smb_node_release(src_fqi->dir_snode);
235 
236 		SMB_NULL_FQI_NODES(*src_fqi);
237 		SMB_NULL_FQI_NODES(*dst_fqi);
238 		return (EACCES);
239 	}
240 
241 
242 	for (count = 0; count <= 3; count++) {
243 		if (count)
244 			delay(MSEC_TO_TICK(400));
245 		status = smb_rename_share_check(src_node);
246 		if (status != NT_STATUS_SHARING_VIOLATION)
247 			break;
248 	}
249 
250 	smb_node_release(src_node);
251 
252 	if (status == NT_STATUS_SHARING_VIOLATION) {
253 		smb_node_release(src_fqi->dir_snode);
254 
255 		SMB_NULL_FQI_NODES(*src_fqi);
256 		SMB_NULL_FQI_NODES(*dst_fqi);
257 		return (EPIPE); /* = ERRbadshare */
258 	}
259 
260 	if (utf8_strcasecmp(src_fqi->path, dst_fqi->path) == 0) {
261 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
262 			smb_node_release(src_fqi->dir_snode);
263 
264 			SMB_NULL_FQI_NODES(*src_fqi);
265 			SMB_NULL_FQI_NODES(*dst_fqi);
266 			return (rc);
267 		}
268 
269 		/*
270 		 * Because the fqm parameter to smbd_fs_query() was 0,
271 		 * a successful return value means that dst_fqi->last_snode
272 		 * may be NULL.
273 		 */
274 		if (dst_fqi->last_snode)
275 			smb_node_release(dst_fqi->last_snode);
276 
277 		rc = strcmp(src_fqi->last_comp_od, dst_fqi->last_comp);
278 		if (rc == 0) {
279 			smb_node_release(src_fqi->dir_snode);
280 			smb_node_release(dst_fqi->dir_snode);
281 
282 			SMB_NULL_FQI_NODES(*src_fqi);
283 			SMB_NULL_FQI_NODES(*dst_fqi);
284 			return (0);
285 		}
286 
287 		rc = smb_fsop_rename(sr, sr->user_cr,
288 		    src_fqi->dir_snode,
289 		    src_fqi->last_comp_od,
290 		    dst_fqi->dir_snode,
291 		    dst_fqi->last_comp);
292 
293 		if (rc != 0) {
294 			smb_node_release(src_fqi->dir_snode);
295 			smb_node_release(dst_fqi->dir_snode);
296 
297 			SMB_NULL_FQI_NODES(*src_fqi);
298 			SMB_NULL_FQI_NODES(*dst_fqi);
299 		}
300 		return (rc);
301 	}
302 
303 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
304 	if (rc != 0) {
305 		smb_node_release(src_fqi->dir_snode);
306 
307 		SMB_NULL_FQI_NODES(*src_fqi);
308 		SMB_NULL_FQI_NODES(*dst_fqi);
309 		return (rc);
310 	}
311 
312 	/*
313 	 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return
314 	 * value, only dst_fqi->dir_snode is valid (dst_fqi->last_snode
315 	 * is NULL).
316 	 */
317 
318 	/*
319 	 * Use the unmangled form of the destination name if the
320 	 * source and destination names are the same and the source
321 	 * name is mangled.  (We are taking a chance here, assuming
322 	 * that this is what the user wants.)
323 	 */
324 
325 	if ((smb_maybe_mangled_name(src_fqi->last_comp)) &&
326 	    (strcmp(src_fqi->last_comp, dst_fqi->last_comp) == 0)) {
327 		dstname = src_fqi->last_comp_od;
328 	} else {
329 		dstname = dst_fqi->last_comp;
330 	}
331 
332 	rc = smb_fsop_rename(sr, sr->user_cr,
333 	    src_fqi->dir_snode,
334 	    src_fqi->last_comp_od,
335 	    dst_fqi->dir_snode,
336 	    dstname);
337 
338 	if (rc != 0) {
339 		smb_node_release(src_fqi->dir_snode);
340 		smb_node_release(dst_fqi->dir_snode);
341 
342 		SMB_NULL_FQI_NODES(*src_fqi);
343 		SMB_NULL_FQI_NODES(*dst_fqi);
344 	}
345 
346 	return (rc);
347 }
348