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