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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <smbsrv/nterror.h> 27 #include <sys/synch.h> 28 #include <smbsrv/smb_incl.h> 29 #include <smbsrv/smb_fsops.h> 30 #include <sys/nbmlock.h> 31 32 /* 33 * NT_RENAME InformationLevels: 34 * 35 * SMB_NT_RENAME_MOVE_CLUSTER_INFO Server returns invalid parameter. 36 * SMB_NT_RENAME_SET_LINK_INFO Create a hard link to a file. 37 * SMB_NT_RENAME_RENAME_FILE In-place rename of a file. 38 * SMB_NT_RENAME_MOVE_FILE Move (rename) a file. 39 */ 40 #define SMB_NT_RENAME_MOVE_CLUSTER_INFO 0x0102 41 #define SMB_NT_RENAME_SET_LINK_INFO 0x0103 42 #define SMB_NT_RENAME_RENAME_FILE 0x0104 43 #define SMB_NT_RENAME_MOVE_FILE 0x0105 44 45 static int smb_do_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *); 46 static int smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *); 47 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t); 48 static void smb_rename_set_error(smb_request_t *, int); 49 50 /* 51 * smb_com_rename 52 * 53 * Rename a file. Files OldFileName must exist and NewFileName must not. 54 * Both pathnames must be relative to the Tid specified in the request. 55 * Open files may be renamed. 56 * 57 * Multiple files may be renamed in response to a single request as Rename 58 * File supports wildcards in the file name (last component of the path). 59 * NOTE: we don't support rename with wildcards. 60 * 61 * SearchAttributes indicates the attributes that the target file(s) must 62 * have. If SearchAttributes is zero then only normal files are renamed. 63 * If the system file or hidden attributes are specified then the rename 64 * is inclusive - both the specified type(s) of files and normal files are 65 * renamed. The encoding of SearchAttributes is described in section 3.10 66 * - File Attribute Encoding. 67 */ 68 smb_sdrc_t 69 smb_pre_rename(smb_request_t *sr) 70 { 71 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 72 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 73 int rc; 74 75 if ((rc = smbsr_decode_vwv(sr, "w", &src_fqi->fq_sattr)) == 0) { 76 rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->fq_path.pn_path, 77 &dst_fqi->fq_path.pn_path); 78 79 dst_fqi->fq_sattr = 0; 80 } 81 82 DTRACE_SMB_2(op__Rename__start, smb_request_t *, sr, 83 struct dirop *, &sr->arg.dirop); 84 85 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 86 } 87 88 void 89 smb_post_rename(smb_request_t *sr) 90 { 91 DTRACE_SMB_1(op__Rename__done, smb_request_t *, sr); 92 } 93 94 smb_sdrc_t 95 smb_com_rename(smb_request_t *sr) 96 { 97 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 98 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 99 int rc; 100 101 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 102 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 103 ERRDOS, ERROR_ACCESS_DENIED); 104 return (SDRC_ERROR); 105 } 106 107 rc = smb_do_rename(sr, src_fqi, dst_fqi); 108 109 if (rc != 0) { 110 smb_rename_set_error(sr, rc); 111 return (SDRC_ERROR); 112 } 113 114 rc = smbsr_encode_empty_result(sr); 115 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 116 } 117 118 /* 119 * smb_do_rename 120 * 121 * Common code for renaming a file. 122 * 123 * If the source and destination are identical, we go through all 124 * the checks but we don't actually do the rename. If the source 125 * and destination files differ only in case, we do a case-sensitive 126 * rename. Otherwise, we do a full case-insensitive rename. 127 * 128 * Returns errno values. 129 */ 130 static int 131 smb_do_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 132 { 133 smb_node_t *src_node, *tnode; 134 char *dstname; 135 DWORD status; 136 int rc; 137 int count; 138 char *path; 139 140 tnode = sr->tid_tree->t_snode; 141 142 /* Lookup the source node. It MUST exist. */ 143 path = src_fqi->fq_path.pn_path; 144 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 145 &src_fqi->fq_dnode, src_fqi->fq_last_comp); 146 if (rc != 0) 147 return (rc); 148 149 rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, 150 src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode); 151 if (rc != 0) { 152 smb_node_release(src_fqi->fq_dnode); 153 return (rc); 154 } 155 156 src_node = src_fqi->fq_fnode; 157 rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr); 158 if (rc != 0) { 159 smb_node_release(src_fqi->fq_fnode); 160 smb_node_release(src_fqi->fq_dnode); 161 return (rc); 162 } 163 164 /* 165 * Break the oplock before access checks. If a client 166 * has a file open, this will force a flush or close, 167 * which may affect the outcome of any share checking. 168 */ 169 (void) smb_oplock_break(src_node, sr->session, B_FALSE); 170 171 for (count = 0; count <= 3; count++) { 172 if (count) { 173 smb_node_end_crit(src_node); 174 delay(MSEC_TO_TICK(400)); 175 } 176 177 smb_node_start_crit(src_node, RW_READER); 178 179 status = smb_node_rename_check(src_node); 180 181 if (status != NT_STATUS_SHARING_VIOLATION) 182 break; 183 } 184 185 if (status == NT_STATUS_SHARING_VIOLATION) { 186 smb_node_end_crit(src_node); 187 smb_node_release(src_fqi->fq_fnode); 188 smb_node_release(src_fqi->fq_dnode); 189 return (EPIPE); /* = ERRbadshare */ 190 } 191 192 status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE); 193 194 if (status != NT_STATUS_SUCCESS) { 195 smb_node_end_crit(src_node); 196 smb_node_release(src_fqi->fq_fnode); 197 smb_node_release(src_fqi->fq_dnode); 198 return (EACCES); 199 } 200 201 /* Lookup destination node. */ 202 path = dst_fqi->fq_path.pn_path; 203 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 204 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 205 if (rc != 0) { 206 smb_node_end_crit(src_node); 207 smb_node_release(src_fqi->fq_fnode); 208 smb_node_release(src_fqi->fq_dnode); 209 return (rc); 210 } 211 212 rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, 213 dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode); 214 if ((rc != 0) && (rc != ENOENT)) { 215 smb_node_end_crit(src_node); 216 smb_node_release(src_fqi->fq_fnode); 217 smb_node_release(src_fqi->fq_dnode); 218 smb_node_release(dst_fqi->fq_dnode); 219 return (rc); 220 } 221 222 if (utf8_strcasecmp(src_fqi->fq_path.pn_path, 223 dst_fqi->fq_path.pn_path) == 0) { 224 225 if (dst_fqi->fq_fnode) 226 smb_node_release(dst_fqi->fq_fnode); 227 228 rc = strcmp(src_fqi->fq_fnode->od_name, dst_fqi->fq_last_comp); 229 if (rc == 0) { 230 smb_node_end_crit(src_node); 231 smb_node_release(src_fqi->fq_fnode); 232 smb_node_release(src_fqi->fq_dnode); 233 smb_node_release(dst_fqi->fq_dnode); 234 return (0); 235 } 236 237 rc = smb_fsop_rename(sr, sr->user_cr, 238 src_fqi->fq_dnode, src_fqi->fq_fnode->od_name, 239 dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 240 241 smb_node_end_crit(src_node); 242 if (rc == 0) 243 smb_node_notify_change(dst_fqi->fq_dnode); 244 smb_node_release(src_fqi->fq_fnode); 245 smb_node_release(src_fqi->fq_dnode); 246 smb_node_release(dst_fqi->fq_dnode); 247 return (rc); 248 } 249 250 /* dst node must not exist */ 251 if (dst_fqi->fq_fnode) { 252 smb_node_end_crit(src_node); 253 smb_node_release(src_fqi->fq_fnode); 254 smb_node_release(src_fqi->fq_dnode); 255 smb_node_release(dst_fqi->fq_fnode); 256 smb_node_release(dst_fqi->fq_dnode); 257 return (EEXIST); 258 } 259 260 /* 261 * If the source name is mangled but the source and destination 262 * on-disk names are identical, we'll use the on-disk name. 263 */ 264 if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) && 265 (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) { 266 dstname = src_fqi->fq_fnode->od_name; 267 } else { 268 dstname = dst_fqi->fq_last_comp; 269 } 270 271 rc = smb_fsop_rename(sr, sr->user_cr, 272 src_fqi->fq_dnode, src_fqi->fq_fnode->od_name, 273 dst_fqi->fq_dnode, dstname); 274 275 smb_node_end_crit(src_node); 276 if (rc == 0) 277 smb_node_notify_change(dst_fqi->fq_dnode); 278 smb_node_release(src_fqi->fq_fnode); 279 smb_node_release(src_fqi->fq_dnode); 280 smb_node_release(dst_fqi->fq_dnode); 281 return (rc); 282 } 283 284 /* 285 * smb_com_nt_rename 286 * 287 * Rename a file. Files OldFileName must exist and NewFileName must not. 288 * Both pathnames must be relative to the Tid specified in the request. 289 * Open files may be renamed. 290 * 291 * Multiple files may be renamed in response to a single request as Rename 292 * File supports wildcards in the file name (last component of the path). 293 * NOTE: we don't support rename with wildcards. 294 * 295 * SearchAttributes indicates the attributes that the target file(s) must 296 * have. If SearchAttributes is zero then only normal files are renamed. 297 * If the system file or hidden attributes are specified then the rename 298 * is inclusive - both the specified type(s) of files and normal files are 299 * renamed. The encoding of SearchAttributes is described in section 3.10 300 * - File Attribute Encoding. 301 * 302 * Client Request Description 303 * ================================= ================================== 304 * UCHAR WordCount; Count of parameter words = 4 305 * USHORT SearchAttributes; 306 * USHORT InformationLevel; 0x0103 Create a hard link 307 * 0x0104 In-place rename 308 * 0x0105 Move (rename) a file 309 * ULONG ClusterCount Servers should ignore this value 310 * USHORT ByteCount; Count of data bytes; min = 4 311 * UCHAR Buffer[]; Buffer containing: 312 * UCHAR BufferFormat1 0x04 313 * UCHAR OldFileName[] OldFileName 314 * UCHAR BufferFormat1 0x04 315 * UCHAR OldFileName[] NewFileName 316 * 317 * Server Response Description 318 * ================================= ================================== 319 * UCHAR WordCount; Count of parameter words = 0 320 * UCHAR ByteCount; Count of data bytes = 0 321 */ 322 smb_sdrc_t 323 smb_pre_nt_rename(smb_request_t *sr) 324 { 325 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 326 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 327 uint32_t clusters; 328 int rc; 329 330 rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr, 331 &sr->arg.dirop.info_level, &clusters); 332 if (rc == 0) { 333 rc = smbsr_decode_data(sr, "%SS", sr, 334 &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path); 335 336 dst_fqi->fq_sattr = 0; 337 } 338 339 DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr, 340 struct dirop *, &sr->arg.dirop); 341 342 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 343 } 344 345 void 346 smb_post_nt_rename(smb_request_t *sr) 347 { 348 DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr); 349 } 350 351 smb_sdrc_t 352 smb_com_nt_rename(smb_request_t *sr) 353 { 354 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 355 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 356 int rc; 357 358 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 359 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 360 ERRDOS, ERROR_ACCESS_DENIED); 361 return (SDRC_ERROR); 362 } 363 364 if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) { 365 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 366 ERRDOS, ERROR_BAD_PATHNAME); 367 return (SDRC_ERROR); 368 } 369 370 switch (sr->arg.dirop.info_level) { 371 case SMB_NT_RENAME_SET_LINK_INFO: 372 rc = smb_make_link(sr, src_fqi, dst_fqi); 373 break; 374 case SMB_NT_RENAME_RENAME_FILE: 375 case SMB_NT_RENAME_MOVE_FILE: 376 rc = smb_do_rename(sr, src_fqi, dst_fqi); 377 break; 378 case SMB_NT_RENAME_MOVE_CLUSTER_INFO: 379 rc = EINVAL; 380 break; 381 default: 382 rc = EACCES; 383 break; 384 } 385 386 if (rc != 0) { 387 smb_rename_set_error(sr, rc); 388 return (SDRC_ERROR); 389 } 390 391 rc = smbsr_encode_empty_result(sr); 392 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 393 } 394 395 /* 396 * smb_make_link 397 * 398 * Common code for creating a hard link (adding an additional name 399 * for a file. 400 * 401 * If the source and destination are identical, we go through all 402 * the checks but we don't create a link. 403 * 404 * Returns errno values. 405 */ 406 static int 407 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 408 { 409 smb_node_t *src_fnode, *tnode; 410 DWORD status; 411 int rc; 412 int count; 413 char *path; 414 415 tnode = sr->tid_tree->t_snode; 416 417 /* Lookup the source node. It MUST exist. */ 418 path = src_fqi->fq_path.pn_path; 419 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 420 &src_fqi->fq_dnode, src_fqi->fq_last_comp); 421 if (rc != 0) 422 return (rc); 423 424 rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, 425 src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode); 426 if (rc != 0) { 427 smb_node_release(src_fqi->fq_dnode); 428 return (rc); 429 } 430 431 src_fnode = src_fqi->fq_fnode; 432 rc = smb_rename_check_attr(sr, src_fnode, src_fqi->fq_sattr); 433 if (rc != 0) { 434 smb_node_release(src_fqi->fq_fnode); 435 smb_node_release(src_fqi->fq_dnode); 436 return (rc); 437 } 438 439 /* 440 * Break the oplock before access checks. If a client 441 * has a file open, this will force a flush or close, 442 * which may affect the outcome of any share checking. 443 */ 444 (void) smb_oplock_break(src_fnode, sr->session, B_FALSE); 445 446 for (count = 0; count <= 3; count++) { 447 if (count) { 448 smb_node_end_crit(src_fnode); 449 delay(MSEC_TO_TICK(400)); 450 } 451 452 smb_node_start_crit(src_fnode, RW_READER); 453 status = smb_node_rename_check(src_fnode); 454 455 if (status != NT_STATUS_SHARING_VIOLATION) 456 break; 457 } 458 459 if (status == NT_STATUS_SHARING_VIOLATION) { 460 smb_node_end_crit(src_fnode); 461 smb_node_release(src_fqi->fq_fnode); 462 smb_node_release(src_fqi->fq_dnode); 463 return (EPIPE); /* = ERRbadshare */ 464 } 465 466 status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE); 467 if (status != NT_STATUS_SUCCESS) { 468 smb_node_end_crit(src_fnode); 469 smb_node_release(src_fqi->fq_fnode); 470 smb_node_release(src_fqi->fq_dnode); 471 return (EACCES); 472 } 473 474 if (utf8_strcasecmp(src_fqi->fq_path.pn_path, 475 dst_fqi->fq_path.pn_path) == 0) { 476 smb_node_end_crit(src_fnode); 477 smb_node_release(src_fqi->fq_fnode); 478 smb_node_release(src_fqi->fq_dnode); 479 return (0); 480 } 481 482 /* Lookup the destination node. It MUST NOT exist. */ 483 path = dst_fqi->fq_path.pn_path; 484 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 485 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 486 if (rc != 0) { 487 smb_node_end_crit(src_fnode); 488 smb_node_release(src_fqi->fq_fnode); 489 smb_node_release(src_fqi->fq_dnode); 490 return (rc); 491 } 492 493 rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS, tnode, 494 dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode); 495 if (rc == 0) { 496 smb_node_release(dst_fqi->fq_fnode); 497 rc = EEXIST; 498 } 499 if (rc != ENOENT) { 500 smb_node_end_crit(src_fnode); 501 smb_node_release(src_fqi->fq_fnode); 502 smb_node_release(src_fqi->fq_dnode); 503 smb_node_release(dst_fqi->fq_dnode); 504 return (rc); 505 } 506 507 rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode, 508 dst_fqi->fq_last_comp); 509 510 smb_node_end_crit(src_fnode); 511 if (rc == 0) 512 smb_node_notify_change(dst_fqi->fq_dnode); 513 smb_node_release(src_fqi->fq_fnode); 514 smb_node_release(src_fqi->fq_dnode); 515 smb_node_release(dst_fqi->fq_dnode); 516 return (rc); 517 } 518 519 static int 520 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr) 521 { 522 smb_attr_t attr; 523 524 if (smb_node_getattr(sr, node, &attr) != 0) 525 return (EIO); 526 527 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && 528 !(SMB_SEARCH_HIDDEN(sattr))) 529 return (ESRCH); 530 531 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && 532 !(SMB_SEARCH_SYSTEM(sattr))) 533 return (ESRCH); 534 535 return (0); 536 } 537 538 /* 539 * The following values are based on observed WFWG, Windows 9x, Windows NT 540 * and Windows 2000 behaviour. 541 * 542 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients. 543 * 544 * Windows 95 clients don't see the problem because the target is deleted 545 * before the rename request. 546 */ 547 static void 548 smb_rename_set_error(smb_request_t *sr, int errnum) 549 { 550 static struct { 551 int errnum; 552 uint16_t errcode; 553 uint32_t status32; 554 } rc_map[] = { 555 { EEXIST, ERROR_ALREADY_EXISTS, NT_STATUS_OBJECT_NAME_COLLISION }, 556 { EPIPE, ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION }, 557 { ENOENT, ERROR_FILE_NOT_FOUND, NT_STATUS_OBJECT_NAME_NOT_FOUND }, 558 { ESRCH, ERROR_FILE_NOT_FOUND, NT_STATUS_NO_SUCH_FILE }, 559 { EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER }, 560 { EACCES, ERROR_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED }, 561 { EIO, ERROR_INTERNAL_ERROR, NT_STATUS_INTERNAL_ERROR } 562 }; 563 564 int i; 565 566 if (errnum == 0) 567 return; 568 569 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) { 570 if (rc_map[i].errnum == errnum) { 571 smbsr_error(sr, rc_map[i].status32, 572 ERRDOS, rc_map[i].errcode); 573 return; 574 } 575 } 576 577 smbsr_errno(sr, errnum); 578 } 579