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 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 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 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 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 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 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 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