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