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 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_1(op__SetInformation__start, 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_1(op__SetInformation__done, 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_1(op__SetInformation2__start, 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_1(op__SetInformation2__done, 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 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 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 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 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 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 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