xref: /titanic_50/usr/src/uts/common/fs/smbsrv/smb_set_fileinfo.c (revision a90cf9f29973990687fa61de9f1f6ea22e924e40)
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Trans2 Set File/Path Information Levels:
28  *
29  * SMB_INFO_STANDARD
30  * SMB_INFO_SET_EAS
31  * SMB_SET_FILE_BASIC_INFO
32  * SMB_SET_FILE_DISPOSITION_INFO
33  * SMB_SET_FILE_END_OF_FILE_INFO
34  * SMB_SET_FILE_ALLOCATION_INFO
35  *
36  * Handled Passthrough levels:
37  * SMB_FILE_BASIC_INFORMATION
38  * SMB_FILE_RENAME_INFORMATION
39  * SMB_FILE_LINK_INFORMATION
40  * SMB_FILE_DISPOSITION_INFORMATION
41  * SMB_FILE_END_OF_FILE_INFORMATION
42  * SMB_FILE_ALLOCATION_INFORMATION
43  *
44  * Internal levels representing non trans2 requests
45  * SMB_SET_INFORMATION
46  * SMB_SET_INFORMATION2
47  */
48 
49 /*
50  * Setting timestamps:
51  * The behaviour when the time field is set to -1 is not documented
52  * but is generally treated like 0, meaning that that server file
53  * system assigned value need not be changed.
54  *
55  * Setting attributes - FILE_ATTRIBUTE_NORMAL:
56  * SMB_SET_INFORMATION -
57  * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
58  *   do NOT change the file's attributes.
59  * SMB_SET_BASIC_INFO -
60  * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
61  *   clear (0) the file's attributes.
62  * - if the specified attributes are 0 do NOT change the file's
63  *   attributes.
64  */
65 
66 #include <smbsrv/smb_kproto.h>
67 #include <smbsrv/smb_fsops.h>
68 
69 static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
70 static int smb_set_by_path(smb_request_t *, smb_xa_t *, uint16_t);
71 
72 /*
73  * These functions all return and NT status code.
74  */
75 static uint32_t smb_set_fileinfo(smb_request_t *, smb_setinfo_t *, int);
76 static uint32_t smb_set_information(smb_request_t *, smb_setinfo_t *);
77 static uint32_t smb_set_information2(smb_request_t *, smb_setinfo_t *);
78 static uint32_t smb_set_standard_info(smb_request_t *, smb_setinfo_t *);
79 static uint32_t smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *);
80 
81 /*
82  * smb_com_trans2_set_file_information
83  */
84 smb_sdrc_t
smb_com_trans2_set_file_information(smb_request_t * sr,smb_xa_t * xa)85 smb_com_trans2_set_file_information(smb_request_t *sr, smb_xa_t *xa)
86 {
87 	uint16_t infolev;
88 
89 	if (smb_mbc_decodef(&xa->req_param_mb, "ww",
90 	    &sr->smb_fid, &infolev) != 0)
91 		return (SDRC_ERROR);
92 
93 	if (smb_set_by_fid(sr, xa, infolev) != 0)
94 		return (SDRC_ERROR);
95 
96 	return (SDRC_SUCCESS);
97 }
98 
99 /*
100  * smb_com_trans2_set_path_information
101  */
102 smb_sdrc_t
smb_com_trans2_set_path_information(smb_request_t * sr,smb_xa_t * xa)103 smb_com_trans2_set_path_information(smb_request_t *sr, smb_xa_t *xa)
104 {
105 	uint16_t	infolev;
106 	smb_fqi_t	*fqi = &sr->arg.dirop.fqi;
107 
108 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
109 		smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST,
110 		    ERRDOS, ERROR_INVALID_FUNCTION);
111 		return (SDRC_ERROR);
112 	}
113 
114 	if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u",
115 	    sr, &infolev, &fqi->fq_path.pn_path) != 0)
116 		return (SDRC_ERROR);
117 
118 	if (smb_set_by_path(sr, xa, infolev) != 0)
119 		return (SDRC_ERROR);
120 
121 	return (SDRC_SUCCESS);
122 }
123 
124 /*
125  * smb_com_set_information (aka setattr)
126  */
127 smb_sdrc_t
smb_pre_set_information(smb_request_t * sr)128 smb_pre_set_information(smb_request_t *sr)
129 {
130 	DTRACE_SMB_1(op__SetInformation__start, smb_request_t *, sr);
131 	return (SDRC_SUCCESS);
132 }
133 
134 void
smb_post_set_information(smb_request_t * sr)135 smb_post_set_information(smb_request_t *sr)
136 {
137 	DTRACE_SMB_1(op__SetInformation__done, smb_request_t *, sr);
138 }
139 
140 smb_sdrc_t
smb_com_set_information(smb_request_t * sr)141 smb_com_set_information(smb_request_t *sr)
142 {
143 	uint16_t	infolev = SMB_SET_INFORMATION;
144 	smb_fqi_t	*fqi = &sr->arg.dirop.fqi;
145 
146 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
147 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
148 		    ERRDOS, ERROR_ACCESS_DENIED);
149 		return (SDRC_ERROR);
150 	}
151 
152 	if (smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path) != 0)
153 		return (SDRC_ERROR);
154 
155 	if (smb_set_by_path(sr, NULL, infolev) != 0)
156 		return (SDRC_ERROR);
157 
158 	if (smbsr_encode_empty_result(sr) != 0)
159 		return (SDRC_ERROR);
160 
161 	return (SDRC_SUCCESS);
162 }
163 
164 /*
165  * smb_com_set_information2 (aka setattre)
166  */
167 smb_sdrc_t
smb_pre_set_information2(smb_request_t * sr)168 smb_pre_set_information2(smb_request_t *sr)
169 {
170 	DTRACE_SMB_1(op__SetInformation2__start, smb_request_t *, sr);
171 	return (SDRC_SUCCESS);
172 }
173 
174 void
smb_post_set_information2(smb_request_t * sr)175 smb_post_set_information2(smb_request_t *sr)
176 {
177 	DTRACE_SMB_1(op__SetInformation2__done, smb_request_t *, sr);
178 }
179 
180 smb_sdrc_t
smb_com_set_information2(smb_request_t * sr)181 smb_com_set_information2(smb_request_t *sr)
182 {
183 	uint16_t infolev = SMB_SET_INFORMATION2;
184 
185 	if (smbsr_decode_vwv(sr, "w", &sr->smb_fid) != 0)
186 		return (SDRC_ERROR);
187 
188 	if (smb_set_by_fid(sr, NULL, infolev) != 0)
189 		return (SDRC_ERROR);
190 
191 	if (smbsr_encode_empty_result(sr) != 0)
192 		return (SDRC_ERROR);
193 
194 	return (SDRC_SUCCESS);
195 }
196 
197 /*
198  * smb_set_by_fid
199  *
200  * Common code for setting file information by open file id.
201  * Use the id to identify the node object and invoke smb_set_fileinfo
202  * for that node.
203  *
204  * Setting attributes on a named pipe by id is handled by simply
205  * returning success.
206  */
207 static int
smb_set_by_fid(smb_request_t * sr,smb_xa_t * xa,uint16_t infolev)208 smb_set_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
209 {
210 	smb_setinfo_t sinfo;
211 	uint32_t status;
212 	int rc = 0;
213 
214 	if (SMB_TREE_IS_READONLY(sr)) {
215 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
216 		    ERRDOS, ERROR_ACCESS_DENIED);
217 		return (-1);
218 	}
219 
220 	if (STYPE_ISIPC(sr->tid_tree->t_res_type))
221 		return (0);
222 
223 	smbsr_lookup_file(sr);
224 	if (sr->fid_ofile == NULL) {
225 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
226 		return (-1);
227 	}
228 
229 	if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
230 		smbsr_release_file(sr);
231 		return (0);
232 	}
233 
234 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
235 
236 	bzero(&sinfo, sizeof (sinfo));
237 	sinfo.si_node = sr->fid_ofile->f_node;
238 	if (xa != NULL)
239 		sinfo.si_data = xa->req_data_mb;
240 	status = smb_set_fileinfo(sr, &sinfo, infolev);
241 	if (status != 0) {
242 		smbsr_error(sr, status, 0, 0);
243 		rc = -1;
244 	}
245 
246 	smbsr_release_file(sr);
247 	return (rc);
248 }
249 
250 /*
251  * smb_set_by_path
252  *
253  * Common code for setting file information by file name.
254  * Use the file name to identify the node object and invoke
255  * smb_set_fileinfo for that node.
256  *
257  * Path should be set in sr->arg.dirop.fqi.fq_path prior to
258  * calling smb_set_by_path.
259  *
260  * Setting attributes on a named pipe by name is an error and
261  * is handled in the calling functions so that they can return
262  * the appropriate error status code (which differs by caller).
263  */
264 static int
smb_set_by_path(smb_request_t * sr,smb_xa_t * xa,uint16_t infolev)265 smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
266 {
267 	int rc;
268 	uint32_t status;
269 	smb_setinfo_t sinfo;
270 	smb_node_t *node, *dnode;
271 	char *name;
272 	smb_pathname_t	*pn;
273 
274 	if (SMB_TREE_IS_READONLY(sr)) {
275 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
276 		    ERRDOS, ERROR_ACCESS_DENIED);
277 		return (-1);
278 	}
279 
280 	pn = &sr->arg.dirop.fqi.fq_path;
281 	smb_pathname_init(sr, pn, pn->pn_path);
282 	if (!smb_pathname_validate(sr, pn))
283 		return (-1);
284 
285 	name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
286 	rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path,
287 	    sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dnode, name);
288 	if (rc == 0) {
289 		rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
290 		    sr->tid_tree->t_snode, dnode, name, &node);
291 		smb_node_release(dnode);
292 	}
293 	kmem_free(name, MAXNAMELEN);
294 
295 	if (rc != 0) {
296 		if (rc == ENOENT) {
297 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
298 			    ERRDOS, ERROR_FILE_NOT_FOUND);
299 		} else {
300 			smbsr_errno(sr, rc);
301 		}
302 		return (-1);
303 	}
304 
305 	bzero(&sinfo, sizeof (sinfo));
306 	sinfo.si_node = node;
307 	if (xa != NULL)
308 		sinfo.si_data = xa->req_data_mb;
309 	status = smb_set_fileinfo(sr, &sinfo, infolev);
310 	if (status != 0) {
311 		smbsr_error(sr, status, 0, 0);
312 		rc = -1;
313 	}
314 
315 	smb_node_release(node);
316 	return (rc);
317 }
318 
319 /*
320  * smb_set_fileinfo
321  *
322  * For compatibility with windows servers, SMB_FILE_LINK_INFORMATION
323  * is handled by returning NT_STATUS_NOT_SUPPORTED.
324  */
325 static uint32_t
smb_set_fileinfo(smb_request_t * sr,smb_setinfo_t * sinfo,int infolev)326 smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo, int infolev)
327 {
328 	uint32_t status;
329 
330 	switch (infolev) {
331 	case SMB_SET_INFORMATION:
332 		status = smb_set_information(sr, sinfo);
333 		break;
334 
335 	case SMB_SET_INFORMATION2:
336 		status = smb_set_information2(sr, sinfo);
337 		break;
338 
339 	case SMB_INFO_STANDARD:
340 		status = smb_set_standard_info(sr, sinfo);
341 		break;
342 
343 	case SMB_INFO_SET_EAS:
344 		/* EAs not supported */
345 		status = 0;
346 		break;
347 
348 	case SMB_SET_FILE_BASIC_INFO:
349 	case SMB_FILE_BASIC_INFORMATION:
350 		status = smb_set_basic_info(sr, sinfo);
351 		break;
352 
353 	case SMB_SET_FILE_DISPOSITION_INFO:
354 	case SMB_FILE_DISPOSITION_INFORMATION:
355 		status = smb_set_disposition_info(sr, sinfo);
356 		break;
357 
358 	case SMB_SET_FILE_END_OF_FILE_INFO:
359 	case SMB_FILE_END_OF_FILE_INFORMATION:
360 		status = smb_set_eof_info(sr, sinfo);
361 		break;
362 
363 	case SMB_SET_FILE_ALLOCATION_INFO:
364 	case SMB_FILE_ALLOCATION_INFORMATION:
365 		status = smb_set_alloc_info(sr, sinfo);
366 		break;
367 
368 	case SMB_FILE_RENAME_INFORMATION:
369 		status = smb_set_rename_info(sr, sinfo);
370 		break;
371 
372 	case SMB_FILE_LINK_INFORMATION:
373 		status = NT_STATUS_NOT_SUPPORTED;
374 		break;
375 
376 	default:
377 		status = NT_STATUS_INVALID_INFO_CLASS;
378 		break;
379 	}
380 
381 	return (status);
382 }
383 
384 /*
385  * smb_set_information
386  *
387  * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
388  * target is not a directory.
389  *
390  * For compatibility with Windows Servers, if the specified
391  * attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change
392  * the file's attributes.
393  */
394 static uint32_t
smb_set_information(smb_request_t * sr,smb_setinfo_t * sinfo)395 smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
396 {
397 	smb_attr_t attr;
398 	smb_node_t *node = sinfo->si_node;
399 	uint32_t status = 0;
400 	uint32_t mtime;
401 	uint16_t attributes;
402 	int rc;
403 
404 	if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0)
405 		return (NT_STATUS_INFO_LENGTH_MISMATCH);
406 
407 	if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
408 	    (!smb_node_is_dir(node))) {
409 		return (NT_STATUS_INVALID_PARAMETER);
410 	}
411 
412 	bzero(&attr, sizeof (smb_attr_t));
413 	if (attributes != FILE_ATTRIBUTE_NORMAL) {
414 		attr.sa_dosattr = attributes;
415 		attr.sa_mask |= SMB_AT_DOSATTR;
416 	}
417 
418 	if (mtime != 0 && mtime != UINT_MAX) {
419 		attr.sa_vattr.va_mtime.tv_sec =
420 		    smb_time_local_to_gmt(sr, mtime);
421 		attr.sa_mask |= SMB_AT_MTIME;
422 	}
423 
424 	rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
425 	if (rc != 0)
426 		status = smb_errno2status(rc);
427 
428 	return (status);
429 }
430 
431 /*
432  * smb_set_information2
433  */
434 static uint32_t
smb_set_information2(smb_request_t * sr,smb_setinfo_t * sinfo)435 smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
436 {
437 	smb_attr_t attr;
438 	uint32_t crtime, atime, mtime;
439 	uint32_t status = 0;
440 	int rc;
441 
442 	if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0)
443 		return (NT_STATUS_INFO_LENGTH_MISMATCH);
444 
445 	bzero(&attr, sizeof (smb_attr_t));
446 	if (mtime != 0 && mtime != UINT_MAX) {
447 		attr.sa_vattr.va_mtime.tv_sec =
448 		    smb_time_local_to_gmt(sr, mtime);
449 		attr.sa_mask |= SMB_AT_MTIME;
450 	}
451 
452 	if (crtime != 0 && crtime != UINT_MAX) {
453 		attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
454 		attr.sa_mask |= SMB_AT_CRTIME;
455 	}
456 
457 	if (atime != 0 && atime != UINT_MAX) {
458 		attr.sa_vattr.va_atime.tv_sec =
459 		    smb_time_local_to_gmt(sr, atime);
460 		attr.sa_mask |= SMB_AT_ATIME;
461 	}
462 
463 	rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr,
464 	    sr->fid_ofile, &attr);
465 	if (rc != 0)
466 		status = smb_errno2status(rc);
467 
468 	return (status);
469 }
470 
471 /*
472  * smb_set_standard_info
473  *
474  * Sets standard file/path information.
475  */
476 static uint32_t
smb_set_standard_info(smb_request_t * sr,smb_setinfo_t * sinfo)477 smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
478 {
479 	smb_attr_t attr;
480 	smb_node_t *node = sinfo->si_node;
481 	uint32_t crtime, atime, mtime;
482 	uint32_t status = 0;
483 	int rc;
484 
485 	if (smb_mbc_decodef(&sinfo->si_data, "yyy",
486 	    &crtime, &atime, &mtime) != 0)
487 		return (NT_STATUS_INFO_LENGTH_MISMATCH);
488 
489 	bzero(&attr, sizeof (smb_attr_t));
490 	if (mtime != 0 && mtime != (uint32_t)-1) {
491 		attr.sa_vattr.va_mtime.tv_sec =
492 		    smb_time_local_to_gmt(sr, mtime);
493 		attr.sa_mask |= SMB_AT_MTIME;
494 	}
495 
496 	if (crtime != 0 && crtime != (uint32_t)-1) {
497 		attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
498 		attr.sa_mask |= SMB_AT_CRTIME;
499 	}
500 
501 	if (atime != 0 && atime != (uint32_t)-1) {
502 		attr.sa_vattr.va_atime.tv_sec =
503 		    smb_time_local_to_gmt(sr, atime);
504 		attr.sa_mask |= SMB_AT_ATIME;
505 	}
506 
507 	rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
508 	if (rc != 0)
509 		status = smb_errno2status(rc);
510 
511 	return (status);
512 }
513 
514 /*
515  * smb_set_rename_info
516  *
517  * This call only allows a rename in the same directory, and the
518  * directory name is not part of the new name provided.
519  *
520  * Explicitly specified parameter validation rules:
521  * - If rootdir is not NULL respond with NT_STATUS_INVALID_PARAMETER.
522  * - If the filename contains a separator character respond with
523  *   NT_STATUS_INVALID_PARAMETER.
524  *
525  * Oplock break:
526  * Some Windows servers break BATCH oplocks prior to the rename.
527  * W2K3 does not. We behave as W2K3; we do not send an oplock break.
528  */
529 static uint32_t
smb_set_rename_info(smb_request_t * sr,smb_setinfo_t * sinfo)530 smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *sinfo)
531 {
532 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
533 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
534 	char *fname;
535 	char *path;
536 	uint8_t flags;
537 	uint32_t rootdir, namelen;
538 	uint32_t status = 0;
539 	int rc;
540 
541 	rc = smb_mbc_decodef(&sinfo->si_data, "b...ll",
542 	    &flags, &rootdir, &namelen);
543 	if (rc == 0) {
544 		rc = smb_mbc_decodef(&sinfo->si_data, "%#U",
545 		    sr, namelen, &fname);
546 	}
547 	if (rc != 0)
548 		return (NT_STATUS_INFO_LENGTH_MISMATCH);
549 
550 	if ((rootdir != 0) || (namelen == 0) || (namelen >= MAXNAMELEN)) {
551 		return (NT_STATUS_INVALID_PARAMETER);
552 	}
553 
554 	if (strchr(fname, '\\') != NULL) {
555 		return (NT_STATUS_NOT_SUPPORTED);
556 	}
557 
558 	/*
559 	 * Construct the full dst. path relative to the share root.
560 	 * Allocated path is free'd in smb_request_free.
561 	 */
562 	path = smb_srm_zalloc(sr, SMB_MAXPATHLEN);
563 	if (src_fqi->fq_path.pn_pname) {
564 		/* Got here via: smb_set_by_path */
565 		(void) snprintf(path, SMB_MAXPATHLEN, "%s\\%s",
566 		    src_fqi->fq_path.pn_pname, fname);
567 	} else {
568 		/* Got here via: smb_set_by_fid */
569 		rc = smb_node_getshrpath(sinfo->si_node->n_dnode,
570 		    sr->tid_tree, path, SMB_MAXPATHLEN);
571 		if (rc != 0) {
572 			status = smb_errno2status(rc);
573 			return (status);
574 		}
575 		(void) strlcat(path, "\\", SMB_MAXPATHLEN);
576 		(void) strlcat(path, fname, SMB_MAXPATHLEN);
577 	}
578 
579 	/*
580 	 * The common rename code can slightly optimize a
581 	 * rename in the same directory when we set the
582 	 * dst_fqi->fq_dnode, dst_fqi->fq_last_comp
583 	 */
584 	dst_fqi->fq_dnode = sinfo->si_node->n_dnode;
585 	(void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
586 
587 	status = smb_setinfo_rename(sr, sinfo->si_node, path, flags);
588 	return (status);
589 }
590