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
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_START(op__SetInformation, 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_DONE(op__SetInformation, 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_START(op__SetInformation2, 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_DONE(op__SetInformation2, 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 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 */
323 static uint32_t
smb_set_fileinfo(smb_request_t * sr,smb_setinfo_t * sinfo,int infolev)324 smb_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 */
391 static uint32_t
smb_set_information(smb_request_t * sr,smb_setinfo_t * sinfo)392 smb_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 */
431 static uint32_t
smb_set_information2(smb_request_t * sr,smb_setinfo_t * sinfo)432 smb_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 */
473 static uint32_t
smb_set_standard_info(smb_request_t * sr,smb_setinfo_t * sinfo)474 smb_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 */
526 static uint32_t
smb_set_rename_info(smb_request_t * sr,smb_setinfo_t * sinfo)527 smb_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