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 2019 Nexenta by DDN, 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
69static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
70static 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 */
75static uint32_t smb_set_fileinfo(smb_request_t *, smb_setinfo_t *, int);
76static uint32_t smb_set_information(smb_request_t *, smb_setinfo_t *);
77static uint32_t smb_set_information2(smb_request_t *, smb_setinfo_t *);
78static uint32_t smb_set_standard_info(smb_request_t *, smb_setinfo_t *);
79static uint32_t smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *);
80
81/*
82 * smb_com_trans2_set_file_information
83 */
84smb_sdrc_t
85smb_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 */
102smb_sdrc_t
103smb_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 */
127smb_sdrc_t
128smb_pre_set_information(smb_request_t *sr)
129{
130	DTRACE_SMB_START(op__SetInformation, smb_request_t *, sr);
131	return (SDRC_SUCCESS);
132}
133
134void
135smb_post_set_information(smb_request_t *sr)
136{
137	DTRACE_SMB_DONE(op__SetInformation, smb_request_t *, sr);
138}
139
140smb_sdrc_t
141smb_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 */
167smb_sdrc_t
168smb_pre_set_information2(smb_request_t *sr)
169{
170	DTRACE_SMB_START(op__SetInformation2, smb_request_t *, sr);
171	return (SDRC_SUCCESS);
172}
173
174void
175smb_post_set_information2(smb_request_t *sr)
176{
177	DTRACE_SMB_DONE(op__SetInformation2, smb_request_t *, sr);
178}
179
180smb_sdrc_t
181smb_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 */
207static int
208smb_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 */
264static int
265smb_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	if (STYPE_ISIPC(sr->tid_tree->t_res_type))
281		return (0);
282
283	pn = &sr->arg.dirop.fqi.fq_path;
284	smb_pathname_init(sr, pn, pn->pn_path);
285	if (!smb_pathname_validate(sr, pn))
286		return (-1);
287
288	name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
289	rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path,
290	    sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dnode, name);
291	if (rc == 0) {
292		rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
293		    sr->tid_tree->t_snode, dnode, name, &node);
294		smb_node_release(dnode);
295	}
296	kmem_free(name, MAXNAMELEN);
297
298	if (rc != 0) {
299		smbsr_errno(sr, rc);
300		return (-1);
301	}
302
303	bzero(&sinfo, sizeof (sinfo));
304	sinfo.si_node = node;
305	if (xa != NULL)
306		sinfo.si_data = xa->req_data_mb;
307	status = smb_set_fileinfo(sr, &sinfo, infolev);
308	if (status != 0) {
309		smbsr_error(sr, status, 0, 0);
310		rc = -1;
311	}
312
313	smb_node_release(node);
314	return (rc);
315}
316
317/*
318 * smb_set_fileinfo
319 *
320 * For compatibility with windows servers, SMB_FILE_LINK_INFORMATION
321 * is handled by returning NT_STATUS_NOT_SUPPORTED.
322 */
323static uint32_t
324smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo, int infolev)
325{
326	uint32_t status;
327
328	switch (infolev) {
329	case SMB_SET_INFORMATION:
330		status = smb_set_information(sr, sinfo);
331		break;
332
333	case SMB_SET_INFORMATION2:
334		status = smb_set_information2(sr, sinfo);
335		break;
336
337	case SMB_INFO_STANDARD:
338		status = smb_set_standard_info(sr, sinfo);
339		break;
340
341	case SMB_INFO_SET_EAS:
342		status = NT_STATUS_EAS_NOT_SUPPORTED;
343		break;
344
345	case SMB_SET_FILE_BASIC_INFO:
346	case SMB_FILE_BASIC_INFORMATION:
347		status = smb_set_basic_info(sr, sinfo);
348		break;
349
350	case SMB_SET_FILE_DISPOSITION_INFO:
351	case SMB_FILE_DISPOSITION_INFORMATION:
352		status = smb_set_disposition_info(sr, sinfo);
353		break;
354
355	case SMB_SET_FILE_END_OF_FILE_INFO:
356	case SMB_FILE_END_OF_FILE_INFORMATION:
357		status = smb_set_eof_info(sr, sinfo);
358		break;
359
360	case SMB_SET_FILE_ALLOCATION_INFO:
361	case SMB_FILE_ALLOCATION_INFORMATION:
362		status = smb_set_alloc_info(sr, sinfo);
363		break;
364
365	case SMB_FILE_RENAME_INFORMATION:
366		status = smb_set_rename_info(sr, sinfo);
367		break;
368
369	case SMB_FILE_LINK_INFORMATION:
370		status = NT_STATUS_NOT_SUPPORTED;
371		break;
372
373	default:
374		status = NT_STATUS_INVALID_INFO_CLASS;
375		break;
376	}
377
378	return (status);
379}
380
381/*
382 * smb_set_information
383 *
384 * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
385 * target is not a directory.
386 *
387 * For compatibility with Windows Servers, if the specified
388 * attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change
389 * the file's attributes.
390 */
391static uint32_t
392smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
393{
394	smb_attr_t attr;
395	smb_node_t *node = sinfo->si_node;
396	uint32_t status = 0;
397	uint32_t mtime;
398	uint16_t attributes;
399	int rc;
400
401	if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0)
402		return (NT_STATUS_INFO_LENGTH_MISMATCH);
403
404	if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
405	    (!smb_node_is_dir(node))) {
406		return (NT_STATUS_INVALID_PARAMETER);
407	}
408
409	bzero(&attr, sizeof (smb_attr_t));
410	if (attributes != FILE_ATTRIBUTE_NORMAL) {
411		attr.sa_dosattr = attributes;
412		attr.sa_mask |= SMB_AT_DOSATTR;
413	}
414
415	if (mtime != 0 && mtime != UINT_MAX) {
416		attr.sa_vattr.va_mtime.tv_sec =
417		    smb_time_local_to_gmt(sr, mtime);
418		attr.sa_mask |= SMB_AT_MTIME;
419	}
420
421	rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
422	if (rc != 0)
423		status = smb_errno2status(rc);
424
425	return (status);
426}
427
428/*
429 * smb_set_information2
430 */
431static uint32_t
432smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
433{
434	smb_attr_t attr;
435	uint32_t crtime, atime, mtime;
436	uint32_t status = 0;
437	int rc;
438
439	if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0)
440		return (NT_STATUS_INFO_LENGTH_MISMATCH);
441
442	bzero(&attr, sizeof (smb_attr_t));
443	if (mtime != 0 && mtime != UINT_MAX) {
444		attr.sa_vattr.va_mtime.tv_sec =
445		    smb_time_local_to_gmt(sr, mtime);
446		attr.sa_mask |= SMB_AT_MTIME;
447	}
448
449	if (crtime != 0 && crtime != UINT_MAX) {
450		attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
451		attr.sa_mask |= SMB_AT_CRTIME;
452	}
453
454	if (atime != 0 && atime != UINT_MAX) {
455		attr.sa_vattr.va_atime.tv_sec =
456		    smb_time_local_to_gmt(sr, atime);
457		attr.sa_mask |= SMB_AT_ATIME;
458	}
459
460	rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr,
461	    sr->fid_ofile, &attr);
462	if (rc != 0)
463		status = smb_errno2status(rc);
464
465	return (status);
466}
467
468/*
469 * smb_set_standard_info
470 *
471 * Sets standard file/path information.
472 */
473static uint32_t
474smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
475{
476	smb_attr_t attr;
477	smb_node_t *node = sinfo->si_node;
478	uint32_t crtime, atime, mtime;
479	uint32_t status = 0;
480	int rc;
481
482	if (smb_mbc_decodef(&sinfo->si_data, "yyy",
483	    &crtime, &atime, &mtime) != 0)
484		return (NT_STATUS_INFO_LENGTH_MISMATCH);
485
486	bzero(&attr, sizeof (smb_attr_t));
487	if (mtime != 0 && mtime != (uint32_t)-1) {
488		attr.sa_vattr.va_mtime.tv_sec =
489		    smb_time_local_to_gmt(sr, mtime);
490		attr.sa_mask |= SMB_AT_MTIME;
491	}
492
493	if (crtime != 0 && crtime != (uint32_t)-1) {
494		attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
495		attr.sa_mask |= SMB_AT_CRTIME;
496	}
497
498	if (atime != 0 && atime != (uint32_t)-1) {
499		attr.sa_vattr.va_atime.tv_sec =
500		    smb_time_local_to_gmt(sr, atime);
501		attr.sa_mask |= SMB_AT_ATIME;
502	}
503
504	rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
505	if (rc != 0)
506		status = smb_errno2status(rc);
507
508	return (status);
509}
510
511/*
512 * smb_set_rename_info
513 *
514 * This call only allows a rename in the same directory, and the
515 * directory name is not part of the new name provided.
516 *
517 * Explicitly specified parameter validation rules:
518 * - If rootdir is not NULL respond with NT_STATUS_INVALID_PARAMETER.
519 * - If the filename contains a separator character respond with
520 *   NT_STATUS_INVALID_PARAMETER.
521 *
522 * Oplock break:
523 * Some Windows servers break BATCH oplocks prior to the rename.
524 * W2K3 does not. We behave as W2K3; we do not send an oplock break.
525 */
526static uint32_t
527smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *sinfo)
528{
529	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
530	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
531	char *fname;
532	char *path;
533	uint8_t flags;
534	uint32_t rootdir, namelen;
535	uint32_t status = 0;
536	int rc;
537
538	rc = smb_mbc_decodef(&sinfo->si_data, "b...ll",
539	    &flags, &rootdir, &namelen);
540	if (rc == 0) {
541		rc = smb_mbc_decodef(&sinfo->si_data, "%#U",
542		    sr, namelen, &fname);
543	}
544	if (rc != 0)
545		return (NT_STATUS_INFO_LENGTH_MISMATCH);
546
547	if ((rootdir != 0) || (namelen == 0) || (namelen >= MAXNAMELEN)) {
548		return (NT_STATUS_INVALID_PARAMETER);
549	}
550
551	if (strchr(fname, '\\') != NULL) {
552		return (NT_STATUS_NOT_SUPPORTED);
553	}
554
555	/*
556	 * Construct the full dst. path relative to the share root.
557	 * Allocated path is free'd in smb_request_free.
558	 */
559	path = smb_srm_zalloc(sr, SMB_MAXPATHLEN);
560	if (src_fqi->fq_path.pn_pname) {
561		/* Got here via: smb_set_by_path */
562		(void) snprintf(path, SMB_MAXPATHLEN, "%s\\%s",
563		    src_fqi->fq_path.pn_pname, fname);
564	} else {
565		/* Got here via: smb_set_by_fid */
566		rc = smb_node_getshrpath(sinfo->si_node->n_dnode,
567		    sr->tid_tree, path, SMB_MAXPATHLEN);
568		if (rc != 0) {
569			status = smb_errno2status(rc);
570			return (status);
571		}
572		(void) strlcat(path, "\\", SMB_MAXPATHLEN);
573		(void) strlcat(path, fname, SMB_MAXPATHLEN);
574	}
575
576	/*
577	 * The common rename code can slightly optimize a
578	 * rename in the same directory when we set the
579	 * dst_fqi->fq_dnode, dst_fqi->fq_last_comp
580	 */
581	dst_fqi->fq_dnode = sinfo->si_node->n_dnode;
582	(void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
583
584	status = smb_setinfo_rename(sr, sinfo->si_node, path, flags);
585	return (status);
586}
587