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