xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 /*
33  * NT_RENAME InformationLevels:
34  *
35  * SMB_NT_RENAME_MOVE_CLUSTER_INFO	Server returns invalid parameter.
36  * SMB_NT_RENAME_SET_LINK_INFO		Create a hard link to a file.
37  * SMB_NT_RENAME_RENAME_FILE		In-place rename of a file.
38  * SMB_NT_RENAME_MOVE_FILE		Move (rename) a file.
39  */
40 #define	SMB_NT_RENAME_MOVE_CLUSTER_INFO	0x0102
41 #define	SMB_NT_RENAME_SET_LINK_INFO	0x0103
42 #define	SMB_NT_RENAME_RENAME_FILE	0x0104
43 #define	SMB_NT_RENAME_MOVE_FILE		0x0105
44 
45 static int smb_do_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
46 static int smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
47 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
48 static void smb_rename_set_error(smb_request_t *, int);
49 
50 /*
51  * smb_com_rename
52  *
53  * Rename a file. Files OldFileName must exist and NewFileName must not.
54  * Both pathnames must be relative to the Tid specified in the request.
55  * Open files may be renamed.
56  *
57  * Multiple files may be renamed in response to a single request as Rename
58  * File supports wildcards in the file name (last component of the path).
59  * NOTE: we don't support rename with wildcards.
60  *
61  * SearchAttributes indicates the attributes that the target file(s) must
62  * have. If SearchAttributes is zero then only normal files are renamed.
63  * If the system file or hidden attributes are specified then the rename
64  * is inclusive - both the specified type(s) of files and normal files are
65  * renamed. The encoding of SearchAttributes is described in section 3.10
66  * - File Attribute Encoding.
67  */
68 smb_sdrc_t
69 smb_pre_rename(smb_request_t *sr)
70 {
71 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
72 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
73 	int rc;
74 
75 	if ((rc = smbsr_decode_vwv(sr, "w", &src_fqi->fq_sattr)) == 0) {
76 		rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->fq_path.pn_path,
77 		    &dst_fqi->fq_path.pn_path);
78 
79 		dst_fqi->fq_sattr = 0;
80 	}
81 
82 	DTRACE_SMB_2(op__Rename__start, smb_request_t *, sr,
83 	    struct dirop *, &sr->arg.dirop);
84 
85 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
86 }
87 
88 void
89 smb_post_rename(smb_request_t *sr)
90 {
91 	DTRACE_SMB_1(op__Rename__done, smb_request_t *, sr);
92 }
93 
94 smb_sdrc_t
95 smb_com_rename(smb_request_t *sr)
96 {
97 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
98 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
99 	int rc;
100 
101 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
102 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
103 		    ERRDOS, ERROR_ACCESS_DENIED);
104 		return (SDRC_ERROR);
105 	}
106 
107 	rc = smb_do_rename(sr, src_fqi, dst_fqi);
108 
109 	if (rc != 0) {
110 		smb_rename_set_error(sr, rc);
111 		return (SDRC_ERROR);
112 	}
113 
114 	rc = smbsr_encode_empty_result(sr);
115 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
116 }
117 
118 /*
119  * smb_do_rename
120  *
121  * Common code for renaming a file.
122  *
123  * If the source and destination are identical, we go through all
124  * the checks but we don't actually do the rename.  If the source
125  * and destination files differ only in case, we do a case-sensitive
126  * rename.  Otherwise, we do a full case-insensitive rename.
127  *
128  * Returns errno values.
129  */
130 static int
131 smb_do_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
132 {
133 	smb_node_t *src_node;
134 	char *dstname;
135 	DWORD status;
136 	int rc;
137 	int count;
138 
139 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0)
140 		return (rc);
141 
142 	src_node = src_fqi->fq_fnode;
143 
144 	if ((rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr)) != 0)
145 		goto rename_cleanup_nodes;
146 
147 	/*
148 	 * Break the oplock before access checks. If a client
149 	 * has a file open, this will force a flush or close,
150 	 * which may affect the outcome of any share checking.
151 	 */
152 	(void) smb_oplock_break(src_node, sr->session, B_FALSE);
153 
154 	for (count = 0; count <= 3; count++) {
155 		if (count) {
156 			smb_node_end_crit(src_node);
157 			delay(MSEC_TO_TICK(400));
158 		}
159 
160 		smb_node_start_crit(src_node, RW_READER);
161 
162 		status = smb_node_rename_check(src_node);
163 
164 		if (status != NT_STATUS_SHARING_VIOLATION)
165 			break;
166 	}
167 
168 	if (status == NT_STATUS_SHARING_VIOLATION) {
169 		smb_node_end_crit(src_node);
170 		rc = EPIPE;	/* = ERRbadshare */
171 		goto rename_cleanup_nodes;
172 	}
173 
174 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
175 
176 	if (status != NT_STATUS_SUCCESS) {
177 		smb_node_end_crit(src_node);
178 		rc = EACCES;
179 		goto rename_cleanup_nodes;
180 	}
181 
182 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
183 	    dst_fqi->fq_path.pn_path) == 0) {
184 		if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) {
185 			smb_node_end_crit(src_node);
186 			goto rename_cleanup_nodes;
187 		}
188 
189 		/*
190 		 * Because the fqm parameter to smbd_fs_query() was 0,
191 		 * dst_fqi->fq_fnode may be NULL.
192 		 */
193 		if (dst_fqi->fq_fnode)
194 			smb_node_release(dst_fqi->fq_fnode);
195 
196 		rc = strcmp(src_fqi->fq_od_name, dst_fqi->fq_last_comp);
197 		if (rc == 0) {
198 			smb_node_end_crit(src_node);
199 			goto rename_cleanup_nodes;
200 		}
201 
202 		rc = smb_fsop_rename(sr, sr->user_cr,
203 		    src_fqi->fq_dnode, src_fqi->fq_od_name,
204 		    dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
205 
206 		smb_node_end_crit(src_node);
207 		if (rc == 0)
208 			smb_node_notify_change(dst_fqi->fq_dnode);
209 		goto rename_cleanup_nodes;
210 	}
211 
212 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
213 	if (rc != 0) {
214 		smb_node_end_crit(src_node);
215 		goto rename_cleanup_nodes;
216 	}
217 
218 	/*
219 	 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode
220 	 * is valid (dst_fqi->fq_fnode is NULL).
221 	 */
222 
223 	/*
224 	 * If the source name is mangled but the source and destination
225 	 * on-disk names are identical, we'll use the on-disk name.
226 	 */
227 	if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) &&
228 	    (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) {
229 		dstname = src_fqi->fq_od_name;
230 	} else {
231 		dstname = dst_fqi->fq_last_comp;
232 	}
233 
234 	rc = smb_fsop_rename(sr, sr->user_cr,
235 	    src_fqi->fq_dnode, src_fqi->fq_od_name,
236 	    dst_fqi->fq_dnode, dstname);
237 
238 	smb_node_end_crit(src_node);
239 
240 	if (rc == 0)
241 		smb_node_notify_change(dst_fqi->fq_dnode);
242 
243 rename_cleanup_nodes:
244 	smb_node_release(src_node);
245 	smb_node_release(src_fqi->fq_dnode);
246 
247 	if (dst_fqi->fq_dnode)
248 		smb_node_release(dst_fqi->fq_dnode);
249 
250 	SMB_NULL_FQI_NODES(*src_fqi);
251 	SMB_NULL_FQI_NODES(*dst_fqi);
252 	return (rc);
253 }
254 
255 /*
256  * smb_com_nt_rename
257  *
258  * Rename a file. Files OldFileName must exist and NewFileName must not.
259  * Both pathnames must be relative to the Tid specified in the request.
260  * Open files may be renamed.
261  *
262  * Multiple files may be renamed in response to a single request as Rename
263  * File supports wildcards in the file name (last component of the path).
264  * NOTE: we don't support rename with wildcards.
265  *
266  * SearchAttributes indicates the attributes that the target file(s) must
267  * have. If SearchAttributes is zero then only normal files are renamed.
268  * If the system file or hidden attributes are specified then the rename
269  * is inclusive - both the specified type(s) of files and normal files are
270  * renamed. The encoding of SearchAttributes is described in section 3.10
271  * - File Attribute Encoding.
272  *
273  *  Client Request                     Description
274  *  =================================  ==================================
275  *  UCHAR WordCount;                   Count of parameter words = 4
276  *  USHORT SearchAttributes;
277  *  USHORT InformationLevel;           0x0103 Create a hard link
278  *                                     0x0104 In-place rename
279  *                                     0x0105 Move (rename) a file
280  *  ULONG ClusterCount                 Servers should ignore this value
281  *  USHORT ByteCount;                  Count of data bytes; min = 4
282  *  UCHAR Buffer[];                    Buffer containing:
283  *                                     UCHAR BufferFormat1 0x04
284  *                                     UCHAR OldFileName[] OldFileName
285  *                                     UCHAR BufferFormat1 0x04
286  *                                     UCHAR OldFileName[] NewFileName
287  *
288  *  Server Response                    Description
289  *  =================================  ==================================
290  *  UCHAR WordCount;                   Count of parameter words = 0
291  *  UCHAR ByteCount;                   Count of data bytes = 0
292  */
293 smb_sdrc_t
294 smb_pre_nt_rename(smb_request_t *sr)
295 {
296 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
297 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
298 	uint32_t clusters;
299 	int rc;
300 
301 	rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr,
302 	    &sr->arg.dirop.info_level, &clusters);
303 	if (rc == 0) {
304 		rc = smbsr_decode_data(sr, "%SS", sr,
305 		    &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path);
306 
307 		dst_fqi->fq_sattr = 0;
308 	}
309 
310 	DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr,
311 	    struct dirop *, &sr->arg.dirop);
312 
313 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
314 }
315 
316 void
317 smb_post_nt_rename(smb_request_t *sr)
318 {
319 	DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr);
320 }
321 
322 smb_sdrc_t
323 smb_com_nt_rename(smb_request_t *sr)
324 {
325 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
326 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
327 	int rc;
328 
329 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
330 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
331 		    ERRDOS, ERROR_ACCESS_DENIED);
332 		return (SDRC_ERROR);
333 	}
334 
335 	if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) {
336 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
337 		    ERRDOS, ERROR_BAD_PATHNAME);
338 		return (SDRC_ERROR);
339 	}
340 
341 	switch (sr->arg.dirop.info_level) {
342 	case SMB_NT_RENAME_SET_LINK_INFO:
343 		rc = smb_make_link(sr, src_fqi, dst_fqi);
344 		break;
345 	case SMB_NT_RENAME_RENAME_FILE:
346 	case SMB_NT_RENAME_MOVE_FILE:
347 		rc = smb_do_rename(sr, src_fqi, dst_fqi);
348 		break;
349 	case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
350 		rc = EINVAL;
351 		break;
352 	default:
353 		rc = EACCES;
354 		break;
355 	}
356 
357 	if (rc != 0) {
358 		smb_rename_set_error(sr, rc);
359 		return (SDRC_ERROR);
360 	}
361 
362 	rc = smbsr_encode_empty_result(sr);
363 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
364 }
365 
366 /*
367  * smb_make_link
368  *
369  * Common code for creating a hard link (adding an additional name
370  * for a file.
371  *
372  * If the source and destination are identical, we go through all
373  * the checks but we don't create a link.
374  *
375  * Returns errno values.
376  */
377 static int
378 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
379 {
380 	smb_node_t *src_fnode;
381 	DWORD status;
382 	int rc;
383 	int count;
384 
385 	if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0)
386 		return (rc);
387 
388 	src_fnode = src_fqi->fq_fnode;
389 
390 	if ((rc = smb_rename_check_attr(sr, src_fnode, src_fqi->fq_sattr)) != 0)
391 		goto link_cleanup_nodes;
392 
393 	/*
394 	 * Break the oplock before access checks. If a client
395 	 * has a file open, this will force a flush or close,
396 	 * which may affect the outcome of any share checking.
397 	 */
398 	(void) smb_oplock_break(src_fnode, sr->session, B_FALSE);
399 
400 	for (count = 0; count <= 3; count++) {
401 		if (count) {
402 			smb_node_end_crit(src_fnode);
403 			delay(MSEC_TO_TICK(400));
404 		}
405 
406 		smb_node_start_crit(src_fnode, RW_READER);
407 		status = smb_node_rename_check(src_fnode);
408 
409 		if (status != NT_STATUS_SHARING_VIOLATION)
410 			break;
411 	}
412 
413 	if (status == NT_STATUS_SHARING_VIOLATION) {
414 		smb_node_end_crit(src_fnode);
415 		rc = EPIPE;	/* = ERRbadshare */
416 		goto link_cleanup_nodes;
417 	}
418 
419 	status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE);
420 
421 	if (status != NT_STATUS_SUCCESS) {
422 		smb_node_end_crit(src_fnode);
423 		rc = EACCES;
424 		goto link_cleanup_nodes;
425 	}
426 
427 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
428 	    dst_fqi->fq_path.pn_path) == 0) {
429 		smb_node_end_crit(src_fnode);
430 		rc = 0;
431 		goto link_cleanup_nodes;
432 	}
433 
434 	rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST);
435 	if (rc != 0) {
436 		smb_node_end_crit(src_fnode);
437 		goto link_cleanup_nodes;
438 	}
439 
440 	/*
441 	 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode
442 	 * is valid (dst_fqi->fq_fnode is NULL).
443 	 */
444 	rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode,
445 	    dst_fqi->fq_last_comp);
446 
447 	smb_node_end_crit(src_fnode);
448 
449 	if (rc == 0)
450 		smb_node_notify_change(dst_fqi->fq_dnode);
451 
452 link_cleanup_nodes:
453 	smb_node_release(src_fnode);
454 	smb_node_release(src_fqi->fq_dnode);
455 
456 	if (dst_fqi->fq_dnode)
457 		smb_node_release(dst_fqi->fq_dnode);
458 
459 	SMB_NULL_FQI_NODES(*src_fqi);
460 	SMB_NULL_FQI_NODES(*dst_fqi);
461 	return (rc);
462 }
463 
464 static int
465 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
466 {
467 	smb_attr_t attr;
468 
469 	if (smb_node_getattr(sr, node, &attr) != 0)
470 		return (EIO);
471 
472 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
473 	    !(SMB_SEARCH_HIDDEN(sattr)))
474 		return (ESRCH);
475 
476 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
477 	    !(SMB_SEARCH_SYSTEM(sattr)))
478 		return (ESRCH);
479 
480 	return (0);
481 }
482 
483 /*
484  * The following values are based on observed WFWG, Windows 9x, Windows NT
485  * and Windows 2000 behaviour.
486  *
487  * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
488  *
489  * Windows 95 clients don't see the problem because the target is deleted
490  * before the rename request.
491  */
492 static void
493 smb_rename_set_error(smb_request_t *sr, int errnum)
494 {
495 	static struct {
496 		int errnum;
497 		uint16_t errcode;
498 		uint32_t status32;
499 	} rc_map[] = {
500 	{ EEXIST, ERROR_ALREADY_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
501 	{ EPIPE,  ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
502 	{ ENOENT, ERROR_FILE_NOT_FOUND,	NT_STATUS_OBJECT_NAME_NOT_FOUND },
503 	{ ESRCH,  ERROR_FILE_NOT_FOUND,	NT_STATUS_NO_SUCH_FILE },
504 	{ EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
505 	{ EACCES, ERROR_ACCESS_DENIED,	NT_STATUS_ACCESS_DENIED },
506 	{ EIO,    ERROR_INTERNAL_ERROR,	NT_STATUS_INTERNAL_ERROR }
507 	};
508 
509 	int i;
510 
511 	if (errnum == 0)
512 		return;
513 
514 	for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
515 		if (rc_map[i].errnum == errnum) {
516 			smbsr_error(sr, rc_map[i].status32,
517 			    ERRDOS, rc_map[i].errcode);
518 			return;
519 		}
520 	}
521 
522 	smbsr_errno(sr, errnum);
523 }
524