xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision 5bbb4db2c3f208d12bf0fd11769728f9e5ba66a2)
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, *tnode;
134 	char *dstname;
135 	DWORD status;
136 	int rc;
137 	int count;
138 	char *path;
139 
140 	tnode = sr->tid_tree->t_snode;
141 
142 	/* Lookup the source node. It MUST exist. */
143 	path = src_fqi->fq_path.pn_path;
144 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
145 	    &src_fqi->fq_dnode, src_fqi->fq_last_comp);
146 	if (rc != 0)
147 		return (rc);
148 
149 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode,
150 	    src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
151 	if (rc != 0) {
152 		smb_node_release(src_fqi->fq_dnode);
153 		return (rc);
154 	}
155 
156 	src_node = src_fqi->fq_fnode;
157 	rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
158 	if (rc != 0) {
159 		smb_node_release(src_fqi->fq_fnode);
160 		smb_node_release(src_fqi->fq_dnode);
161 		return (rc);
162 	}
163 
164 	/*
165 	 * Break the oplock before access checks. If a client
166 	 * has a file open, this will force a flush or close,
167 	 * which may affect the outcome of any share checking.
168 	 */
169 	(void) smb_oplock_break(src_node, sr->session, B_FALSE);
170 
171 	for (count = 0; count <= 3; count++) {
172 		if (count) {
173 			smb_node_end_crit(src_node);
174 			delay(MSEC_TO_TICK(400));
175 		}
176 
177 		smb_node_start_crit(src_node, RW_READER);
178 
179 		status = smb_node_rename_check(src_node);
180 
181 		if (status != NT_STATUS_SHARING_VIOLATION)
182 			break;
183 	}
184 
185 	if (status == NT_STATUS_SHARING_VIOLATION) {
186 		smb_node_end_crit(src_node);
187 		smb_node_release(src_fqi->fq_fnode);
188 		smb_node_release(src_fqi->fq_dnode);
189 		return (EPIPE); /* = ERRbadshare */
190 	}
191 
192 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
193 
194 	if (status != NT_STATUS_SUCCESS) {
195 		smb_node_end_crit(src_node);
196 		smb_node_release(src_fqi->fq_fnode);
197 		smb_node_release(src_fqi->fq_dnode);
198 		return (EACCES);
199 	}
200 
201 	/* Lookup destination node. */
202 	path = dst_fqi->fq_path.pn_path;
203 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
204 	    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
205 	if (rc != 0) {
206 		smb_node_end_crit(src_node);
207 		smb_node_release(src_fqi->fq_fnode);
208 		smb_node_release(src_fqi->fq_dnode);
209 		return (rc);
210 	}
211 
212 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode,
213 	    dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
214 	if ((rc != 0) && (rc != ENOENT)) {
215 		smb_node_end_crit(src_node);
216 		smb_node_release(src_fqi->fq_fnode);
217 		smb_node_release(src_fqi->fq_dnode);
218 		smb_node_release(dst_fqi->fq_dnode);
219 		return (rc);
220 	}
221 
222 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
223 	    dst_fqi->fq_path.pn_path) == 0) {
224 
225 		if (dst_fqi->fq_fnode)
226 			smb_node_release(dst_fqi->fq_fnode);
227 
228 		rc = strcmp(src_fqi->fq_fnode->od_name, dst_fqi->fq_last_comp);
229 		if (rc == 0) {
230 			smb_node_end_crit(src_node);
231 			smb_node_release(src_fqi->fq_fnode);
232 			smb_node_release(src_fqi->fq_dnode);
233 			smb_node_release(dst_fqi->fq_dnode);
234 			return (0);
235 		}
236 
237 		rc = smb_fsop_rename(sr, sr->user_cr,
238 		    src_fqi->fq_dnode, src_fqi->fq_fnode->od_name,
239 		    dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
240 
241 		smb_node_end_crit(src_node);
242 		if (rc == 0)
243 			smb_node_notify_change(dst_fqi->fq_dnode);
244 		smb_node_release(src_fqi->fq_fnode);
245 		smb_node_release(src_fqi->fq_dnode);
246 		smb_node_release(dst_fqi->fq_dnode);
247 		return (rc);
248 	}
249 
250 	/* dst node must not exist */
251 	if (dst_fqi->fq_fnode) {
252 		smb_node_end_crit(src_node);
253 		smb_node_release(src_fqi->fq_fnode);
254 		smb_node_release(src_fqi->fq_dnode);
255 		smb_node_release(dst_fqi->fq_fnode);
256 		smb_node_release(dst_fqi->fq_dnode);
257 		return (EEXIST);
258 	}
259 
260 	/*
261 	 * If the source name is mangled but the source and destination
262 	 * on-disk names are identical, we'll use the on-disk name.
263 	 */
264 	if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) &&
265 	    (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) {
266 		dstname = src_fqi->fq_fnode->od_name;
267 	} else {
268 		dstname = dst_fqi->fq_last_comp;
269 	}
270 
271 	rc = smb_fsop_rename(sr, sr->user_cr,
272 	    src_fqi->fq_dnode, src_fqi->fq_fnode->od_name,
273 	    dst_fqi->fq_dnode, dstname);
274 
275 	smb_node_end_crit(src_node);
276 	if (rc == 0)
277 		smb_node_notify_change(dst_fqi->fq_dnode);
278 	smb_node_release(src_fqi->fq_fnode);
279 	smb_node_release(src_fqi->fq_dnode);
280 	smb_node_release(dst_fqi->fq_dnode);
281 	return (rc);
282 }
283 
284 /*
285  * smb_com_nt_rename
286  *
287  * Rename a file. Files OldFileName must exist and NewFileName must not.
288  * Both pathnames must be relative to the Tid specified in the request.
289  * Open files may be renamed.
290  *
291  * Multiple files may be renamed in response to a single request as Rename
292  * File supports wildcards in the file name (last component of the path).
293  * NOTE: we don't support rename with wildcards.
294  *
295  * SearchAttributes indicates the attributes that the target file(s) must
296  * have. If SearchAttributes is zero then only normal files are renamed.
297  * If the system file or hidden attributes are specified then the rename
298  * is inclusive - both the specified type(s) of files and normal files are
299  * renamed. The encoding of SearchAttributes is described in section 3.10
300  * - File Attribute Encoding.
301  *
302  *  Client Request                     Description
303  *  =================================  ==================================
304  *  UCHAR WordCount;                   Count of parameter words = 4
305  *  USHORT SearchAttributes;
306  *  USHORT InformationLevel;           0x0103 Create a hard link
307  *                                     0x0104 In-place rename
308  *                                     0x0105 Move (rename) a file
309  *  ULONG ClusterCount                 Servers should ignore this value
310  *  USHORT ByteCount;                  Count of data bytes; min = 4
311  *  UCHAR Buffer[];                    Buffer containing:
312  *                                     UCHAR BufferFormat1 0x04
313  *                                     UCHAR OldFileName[] OldFileName
314  *                                     UCHAR BufferFormat1 0x04
315  *                                     UCHAR OldFileName[] NewFileName
316  *
317  *  Server Response                    Description
318  *  =================================  ==================================
319  *  UCHAR WordCount;                   Count of parameter words = 0
320  *  UCHAR ByteCount;                   Count of data bytes = 0
321  */
322 smb_sdrc_t
323 smb_pre_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 	uint32_t clusters;
328 	int rc;
329 
330 	rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr,
331 	    &sr->arg.dirop.info_level, &clusters);
332 	if (rc == 0) {
333 		rc = smbsr_decode_data(sr, "%SS", sr,
334 		    &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path);
335 
336 		dst_fqi->fq_sattr = 0;
337 	}
338 
339 	DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr,
340 	    struct dirop *, &sr->arg.dirop);
341 
342 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
343 }
344 
345 void
346 smb_post_nt_rename(smb_request_t *sr)
347 {
348 	DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr);
349 }
350 
351 smb_sdrc_t
352 smb_com_nt_rename(smb_request_t *sr)
353 {
354 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
355 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
356 	int rc;
357 
358 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
359 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
360 		    ERRDOS, ERROR_ACCESS_DENIED);
361 		return (SDRC_ERROR);
362 	}
363 
364 	if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) {
365 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
366 		    ERRDOS, ERROR_BAD_PATHNAME);
367 		return (SDRC_ERROR);
368 	}
369 
370 	switch (sr->arg.dirop.info_level) {
371 	case SMB_NT_RENAME_SET_LINK_INFO:
372 		rc = smb_make_link(sr, src_fqi, dst_fqi);
373 		break;
374 	case SMB_NT_RENAME_RENAME_FILE:
375 	case SMB_NT_RENAME_MOVE_FILE:
376 		rc = smb_do_rename(sr, src_fqi, dst_fqi);
377 		break;
378 	case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
379 		rc = EINVAL;
380 		break;
381 	default:
382 		rc = EACCES;
383 		break;
384 	}
385 
386 	if (rc != 0) {
387 		smb_rename_set_error(sr, rc);
388 		return (SDRC_ERROR);
389 	}
390 
391 	rc = smbsr_encode_empty_result(sr);
392 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
393 }
394 
395 /*
396  * smb_make_link
397  *
398  * Common code for creating a hard link (adding an additional name
399  * for a file.
400  *
401  * If the source and destination are identical, we go through all
402  * the checks but we don't create a link.
403  *
404  * Returns errno values.
405  */
406 static int
407 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
408 {
409 	smb_node_t *src_fnode, *tnode;
410 	DWORD status;
411 	int rc;
412 	int count;
413 	char *path;
414 
415 	tnode = sr->tid_tree->t_snode;
416 
417 	/* Lookup the source node. It MUST exist. */
418 	path = src_fqi->fq_path.pn_path;
419 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
420 	    &src_fqi->fq_dnode, src_fqi->fq_last_comp);
421 	if (rc != 0)
422 		return (rc);
423 
424 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode,
425 	    src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
426 	if (rc != 0) {
427 		smb_node_release(src_fqi->fq_dnode);
428 		return (rc);
429 	}
430 
431 	src_fnode = src_fqi->fq_fnode;
432 	rc = smb_rename_check_attr(sr, src_fnode, src_fqi->fq_sattr);
433 	if (rc != 0) {
434 		smb_node_release(src_fqi->fq_fnode);
435 		smb_node_release(src_fqi->fq_dnode);
436 		return (rc);
437 	}
438 
439 	/*
440 	 * Break the oplock before access checks. If a client
441 	 * has a file open, this will force a flush or close,
442 	 * which may affect the outcome of any share checking.
443 	 */
444 	(void) smb_oplock_break(src_fnode, sr->session, B_FALSE);
445 
446 	for (count = 0; count <= 3; count++) {
447 		if (count) {
448 			smb_node_end_crit(src_fnode);
449 			delay(MSEC_TO_TICK(400));
450 		}
451 
452 		smb_node_start_crit(src_fnode, RW_READER);
453 		status = smb_node_rename_check(src_fnode);
454 
455 		if (status != NT_STATUS_SHARING_VIOLATION)
456 			break;
457 	}
458 
459 	if (status == NT_STATUS_SHARING_VIOLATION) {
460 		smb_node_end_crit(src_fnode);
461 		smb_node_release(src_fqi->fq_fnode);
462 		smb_node_release(src_fqi->fq_dnode);
463 		return (EPIPE); /* = ERRbadshare */
464 	}
465 
466 	status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE);
467 	if (status != NT_STATUS_SUCCESS) {
468 		smb_node_end_crit(src_fnode);
469 		smb_node_release(src_fqi->fq_fnode);
470 		smb_node_release(src_fqi->fq_dnode);
471 		return (EACCES);
472 	}
473 
474 	if (utf8_strcasecmp(src_fqi->fq_path.pn_path,
475 	    dst_fqi->fq_path.pn_path) == 0) {
476 		smb_node_end_crit(src_fnode);
477 		smb_node_release(src_fqi->fq_fnode);
478 		smb_node_release(src_fqi->fq_dnode);
479 		return (0);
480 	}
481 
482 	/* Lookup the destination node. It MUST NOT exist. */
483 	path = dst_fqi->fq_path.pn_path;
484 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
485 	    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
486 	if (rc != 0) {
487 		smb_node_end_crit(src_fnode);
488 		smb_node_release(src_fqi->fq_fnode);
489 		smb_node_release(src_fqi->fq_dnode);
490 		return (rc);
491 	}
492 
493 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode,
494 	    dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
495 	if (rc == 0) {
496 		smb_node_release(dst_fqi->fq_fnode);
497 		rc = EEXIST;
498 	}
499 	if (rc != ENOENT) {
500 		smb_node_end_crit(src_fnode);
501 		smb_node_release(src_fqi->fq_fnode);
502 		smb_node_release(src_fqi->fq_dnode);
503 		smb_node_release(dst_fqi->fq_dnode);
504 		return (rc);
505 	}
506 
507 	rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode,
508 	    dst_fqi->fq_last_comp);
509 
510 	smb_node_end_crit(src_fnode);
511 	if (rc == 0)
512 		smb_node_notify_change(dst_fqi->fq_dnode);
513 	smb_node_release(src_fqi->fq_fnode);
514 	smb_node_release(src_fqi->fq_dnode);
515 	smb_node_release(dst_fqi->fq_dnode);
516 	return (rc);
517 }
518 
519 static int
520 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
521 {
522 	smb_attr_t attr;
523 
524 	if (smb_node_getattr(sr, node, &attr) != 0)
525 		return (EIO);
526 
527 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
528 	    !(SMB_SEARCH_HIDDEN(sattr)))
529 		return (ESRCH);
530 
531 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
532 	    !(SMB_SEARCH_SYSTEM(sattr)))
533 		return (ESRCH);
534 
535 	return (0);
536 }
537 
538 /*
539  * The following values are based on observed WFWG, Windows 9x, Windows NT
540  * and Windows 2000 behaviour.
541  *
542  * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
543  *
544  * Windows 95 clients don't see the problem because the target is deleted
545  * before the rename request.
546  */
547 static void
548 smb_rename_set_error(smb_request_t *sr, int errnum)
549 {
550 	static struct {
551 		int errnum;
552 		uint16_t errcode;
553 		uint32_t status32;
554 	} rc_map[] = {
555 	{ EEXIST, ERROR_ALREADY_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
556 	{ EPIPE,  ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
557 	{ ENOENT, ERROR_FILE_NOT_FOUND,	NT_STATUS_OBJECT_NAME_NOT_FOUND },
558 	{ ESRCH,  ERROR_FILE_NOT_FOUND,	NT_STATUS_NO_SUCH_FILE },
559 	{ EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
560 	{ EACCES, ERROR_ACCESS_DENIED,	NT_STATUS_ACCESS_DENIED },
561 	{ EIO,    ERROR_INTERNAL_ERROR,	NT_STATUS_INTERNAL_ERROR }
562 	};
563 
564 	int i;
565 
566 	if (errnum == 0)
567 		return;
568 
569 	for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
570 		if (rc_map[i].errnum == errnum) {
571 			smbsr_error(sr, rc_map[i].status32,
572 			    ERRDOS, rc_map[i].errcode);
573 			return;
574 		}
575 	}
576 
577 	smbsr_errno(sr, errnum);
578 }
579