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 2017 Nexenta Systems, Inc.  All rights reserved.
24 */
25
26/*
27 * Common functions supporting both:
28 * SMB1 Trans2 Set File/Path Info,
29 * SMB2 Set File Info
30 */
31
32#include <smbsrv/smb2_kproto.h>
33#include <smbsrv/smb_fsops.h>
34
35/*
36 * smb_set_basic_info
37 * [MS-FSCC] 2.4.7
38 *	FileBasicInformation
39 *	SMB_SET_FILE_BASIC_INFO
40 *	SMB_FILE_BASIC_INFORMATION
41 *
42 * Sets basic file/path information.
43 *
44 * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
45 * target is not a directory.
46 *
47 * For compatibility with windows servers:
48 * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
49 *   clear (0) the file's attributes.
50 * - if the specified attributes are 0 do NOT change the file's attributes.
51 */
52uint32_t
53smb_set_basic_info(smb_request_t *sr, smb_setinfo_t *si)
54{
55	smb_attr_t *attr = &si->si_attr;
56	smb_node_t *node = si->si_node;
57	uint64_t crtime, atime, mtime, ctime;
58	uint32_t attributes;
59	int rc;
60
61	if (smb_mbc_decodef(&si->si_data, "qqqql",
62	    &crtime, &atime, &mtime, &ctime, &attributes) != 0)
63		return (NT_STATUS_INFO_LENGTH_MISMATCH);
64
65	if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
66	    (!smb_node_is_dir(node)))
67		return (NT_STATUS_INVALID_PARAMETER);
68
69	bzero(attr, sizeof (*attr));
70	if (atime != 0 && atime != (uint64_t)-1) {
71		smb_time_nt_to_unix(atime, &attr->sa_vattr.va_atime);
72		attr->sa_mask |= SMB_AT_ATIME;
73	}
74	if (mtime != 0 && mtime != (uint64_t)-1) {
75		smb_time_nt_to_unix(mtime, &attr->sa_vattr.va_mtime);
76		attr->sa_mask |= SMB_AT_MTIME;
77	}
78	if (ctime != 0 && ctime != (uint64_t)-1) {
79		smb_time_nt_to_unix(ctime, &attr->sa_vattr.va_ctime);
80		attr->sa_mask |= SMB_AT_CTIME;
81	}
82	if (crtime != 0 && crtime != (uint64_t)-1) {
83		smb_time_nt_to_unix(crtime, &attr->sa_crtime);
84		attr->sa_mask |= SMB_AT_CRTIME;
85	}
86
87	if (attributes != 0) {
88		attr->sa_dosattr = attributes;
89		attr->sa_mask |= SMB_AT_DOSATTR;
90	}
91
92	rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, attr);
93	if (rc != 0)
94		return (smb_errno2status(rc));
95
96	return (0);
97}
98
99/*
100 * smb_set_eof_info
101 *	FileEndOfFileInformation
102 *	SMB_SET_FILE_END_OF_FILE_INFO
103 *	SMB_FILE_END_OF_FILE_INFORMATION
104 */
105uint32_t
106smb_set_eof_info(smb_request_t *sr, smb_setinfo_t *si)
107{
108	smb_attr_t *attr = &si->si_attr;
109	smb_node_t *node = si->si_node;
110	uint64_t eof;
111	uint32_t status;
112	int rc;
113
114	if (smb_mbc_decodef(&si->si_data, "q", &eof) != 0)
115		return (NT_STATUS_INFO_LENGTH_MISMATCH);
116
117	if (smb_node_is_dir(node))
118		return (NT_STATUS_INVALID_PARAMETER);
119
120	status = smb_oplock_break_SETINFO(node, sr->fid_ofile,
121	    FileEndOfFileInformation);
122	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
123		if (sr->session->dialect >= SMB_VERS_2_BASE)
124			(void) smb2sr_go_async(sr);
125		(void) smb_oplock_wait_break(node, 0);
126		status = 0;
127	}
128	if (status != 0)
129		return (status);
130
131	bzero(attr, sizeof (*attr));
132	attr->sa_mask = SMB_AT_SIZE;
133	attr->sa_vattr.va_size = (u_offset_t)eof;
134	rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, attr);
135	if (rc != 0)
136		return (smb_errno2status(rc));
137
138	return (0);
139}
140
141/*
142 * smb_set_alloc_info
143 *	FileAllocationInformation
144 *	SMB_SET_FILE_ALLOCATION_INFO
145 *	SMB_FILE_ALLOCATION_INFORMATION
146 */
147uint32_t
148smb_set_alloc_info(smb_request_t *sr, smb_setinfo_t *si)
149{
150	smb_attr_t *attr = &si->si_attr;
151	smb_node_t *node = si->si_node;
152	uint64_t allocsz;
153	uint32_t status;
154	int rc;
155
156	if (smb_mbc_decodef(&si->si_data, "q", &allocsz) != 0)
157		return (NT_STATUS_INFO_LENGTH_MISMATCH);
158
159	if (smb_node_is_dir(node))
160		return (NT_STATUS_INVALID_PARAMETER);
161
162	status = smb_oplock_break_SETINFO(node, sr->fid_ofile,
163	    FileAllocationInformation);
164	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
165		if (sr->session->dialect >= SMB_VERS_2_BASE)
166			(void) smb2sr_go_async(sr);
167		(void) smb_oplock_wait_break(node, 0);
168		status = 0;
169	}
170	if (status != 0)
171		return (status);
172
173	bzero(attr, sizeof (*attr));
174	attr->sa_mask = SMB_AT_ALLOCSZ;
175	attr->sa_allocsz = (u_offset_t)allocsz;
176	rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, attr);
177	if (rc != 0)
178		return (smb_errno2status(rc));
179
180	return (0);
181}
182
183/*
184 * smb_set_disposition_info
185 * See:
186 *	FileDispositionInformation
187 *	SMB_SET_FILE_DISPOSITION_INFO
188 *	SMB_FILE_DISPOSITION_INFORMATION
189 *
190 * Set/Clear DELETE_ON_CLOSE flag for an open file.
191 * File should have been opened with DELETE access otherwise
192 * the operation is not permitted.
193 *
194 * NOTE: The node should be marked delete-on-close upon the receipt
195 * of the Trans2SetFileInfo(SetDispositionInfo) if mark_delete is set.
196 * It is different than both SmbNtCreateAndX and SmbNtTransact, which
197 * set delete-on-close on the ofile and defer setting the flag on the
198 * node until the file is closed.
199 *
200 * Observation of Windows 2000 indicates the following:
201 *
202 * 1) If a file is not opened with delete-on-close create options and
203 * the delete-on-close is set via Trans2SetFileInfo(SetDispositionInfo)
204 * using that open file handle, any subsequent open requests will fail
205 * with DELETE_PENDING.
206 *
207 * 2) If a file is opened with delete-on-close create options and the
208 * client attempts to unset delete-on-close via Trans2SetFileInfo
209 * (SetDispositionInfo) prior to the file close, any subsequent open
210 * requests will still fail with DELETE_PENDING after the file is closed.
211 *
212 * 3) If a file is opened with delete-on-close create options and that
213 * file handle (not the last open handle and the only file handle
214 * with delete-on-close set) is closed. Any subsequent open requests
215 * will fail with DELETE_PENDING. Unsetting delete-on-close via
216 * Trans2SetFileInfo(SetDispositionInfo) at this time will unset the
217 * node delete-on-close flag, which will result in the file not being
218 * removed even after the last file handle is closed.
219 */
220uint32_t
221smb_set_disposition_info(smb_request_t *sr, smb_setinfo_t *si)
222{
223	smb_node_t *node = si->si_node;
224	smb_ofile_t *of = sr->fid_ofile;
225	uint8_t		mark_delete;
226	uint32_t	status;
227	uint32_t	flags = 0;
228
229	if (smb_mbc_decodef(&si->si_data, "b", &mark_delete) != 0)
230		return (NT_STATUS_INFO_LENGTH_MISMATCH);
231
232	if ((of == NULL) || !(smb_ofile_granted_access(of) & DELETE))
233		return (NT_STATUS_ACCESS_DENIED);
234
235	if (mark_delete == 0) {
236		smb_node_reset_delete_on_close(node);
237		return (NT_STATUS_SUCCESS);
238	}
239
240	/*
241	 * Break any oplock handle caching.
242	 */
243	status = smb_oplock_break_SETINFO(node, of,
244	    FileDispositionInformation);
245	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
246		if (sr->session->dialect >= SMB_VERS_2_BASE)
247			(void) smb2sr_go_async(sr);
248		(void) smb_oplock_wait_break(node, 0);
249		status = 0;
250	}
251	if (status != 0)
252		return (status);
253
254	if (SMB_TREE_SUPPORTS_CATIA(sr))
255		flags |= SMB_CATIA;
256
257	return (smb_node_set_delete_on_close(node, of->f_cr, flags));
258}
259