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; 134 char *dstname; 135 DWORD status; 136 int rc; 137 int count; 138 139 if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) 140 return (rc); 141 142 src_node = src_fqi->fq_fnode; 143 144 if ((rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr)) != 0) 145 goto rename_cleanup_nodes; 146 147 /* 148 * Break the oplock before access checks. If a client 149 * has a file open, this will force a flush or close, 150 * which may affect the outcome of any share checking. 151 */ 152 (void) smb_oplock_break(src_node, sr->session, B_FALSE); 153 154 for (count = 0; count <= 3; count++) { 155 if (count) { 156 smb_node_end_crit(src_node); 157 delay(MSEC_TO_TICK(400)); 158 } 159 160 smb_node_start_crit(src_node, RW_READER); 161 162 status = smb_node_rename_check(src_node); 163 164 if (status != NT_STATUS_SHARING_VIOLATION) 165 break; 166 } 167 168 if (status == NT_STATUS_SHARING_VIOLATION) { 169 smb_node_end_crit(src_node); 170 rc = EPIPE; /* = ERRbadshare */ 171 goto rename_cleanup_nodes; 172 } 173 174 status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE); 175 176 if (status != NT_STATUS_SUCCESS) { 177 smb_node_end_crit(src_node); 178 rc = EACCES; 179 goto rename_cleanup_nodes; 180 } 181 182 if (utf8_strcasecmp(src_fqi->fq_path.pn_path, 183 dst_fqi->fq_path.pn_path) == 0) { 184 if ((rc = smbd_fs_query(sr, dst_fqi, 0)) != 0) { 185 smb_node_end_crit(src_node); 186 goto rename_cleanup_nodes; 187 } 188 189 /* 190 * Because the fqm parameter to smbd_fs_query() was 0, 191 * dst_fqi->fq_fnode may be NULL. 192 */ 193 if (dst_fqi->fq_fnode) 194 smb_node_release(dst_fqi->fq_fnode); 195 196 rc = strcmp(src_fqi->fq_od_name, dst_fqi->fq_last_comp); 197 if (rc == 0) { 198 smb_node_end_crit(src_node); 199 goto rename_cleanup_nodes; 200 } 201 202 rc = smb_fsop_rename(sr, sr->user_cr, 203 src_fqi->fq_dnode, src_fqi->fq_od_name, 204 dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 205 206 smb_node_end_crit(src_node); 207 if (rc == 0) 208 smb_node_notify_change(dst_fqi->fq_dnode); 209 goto rename_cleanup_nodes; 210 } 211 212 rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 213 if (rc != 0) { 214 smb_node_end_crit(src_node); 215 goto rename_cleanup_nodes; 216 } 217 218 /* 219 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode 220 * is valid (dst_fqi->fq_fnode is NULL). 221 */ 222 223 /* 224 * If the source name is mangled but the source and destination 225 * on-disk names are identical, we'll use the on-disk name. 226 */ 227 if ((smb_maybe_mangled_name(src_fqi->fq_last_comp)) && 228 (strcmp(src_fqi->fq_last_comp, dst_fqi->fq_last_comp) == 0)) { 229 dstname = src_fqi->fq_od_name; 230 } else { 231 dstname = dst_fqi->fq_last_comp; 232 } 233 234 rc = smb_fsop_rename(sr, sr->user_cr, 235 src_fqi->fq_dnode, src_fqi->fq_od_name, 236 dst_fqi->fq_dnode, dstname); 237 238 smb_node_end_crit(src_node); 239 240 if (rc == 0) 241 smb_node_notify_change(dst_fqi->fq_dnode); 242 243 rename_cleanup_nodes: 244 smb_node_release(src_node); 245 smb_node_release(src_fqi->fq_dnode); 246 247 if (dst_fqi->fq_dnode) 248 smb_node_release(dst_fqi->fq_dnode); 249 250 SMB_NULL_FQI_NODES(*src_fqi); 251 SMB_NULL_FQI_NODES(*dst_fqi); 252 return (rc); 253 } 254 255 /* 256 * smb_com_nt_rename 257 * 258 * Rename a file. Files OldFileName must exist and NewFileName must not. 259 * Both pathnames must be relative to the Tid specified in the request. 260 * Open files may be renamed. 261 * 262 * Multiple files may be renamed in response to a single request as Rename 263 * File supports wildcards in the file name (last component of the path). 264 * NOTE: we don't support rename with wildcards. 265 * 266 * SearchAttributes indicates the attributes that the target file(s) must 267 * have. If SearchAttributes is zero then only normal files are renamed. 268 * If the system file or hidden attributes are specified then the rename 269 * is inclusive - both the specified type(s) of files and normal files are 270 * renamed. The encoding of SearchAttributes is described in section 3.10 271 * - File Attribute Encoding. 272 * 273 * Client Request Description 274 * ================================= ================================== 275 * UCHAR WordCount; Count of parameter words = 4 276 * USHORT SearchAttributes; 277 * USHORT InformationLevel; 0x0103 Create a hard link 278 * 0x0104 In-place rename 279 * 0x0105 Move (rename) a file 280 * ULONG ClusterCount Servers should ignore this value 281 * USHORT ByteCount; Count of data bytes; min = 4 282 * UCHAR Buffer[]; Buffer containing: 283 * UCHAR BufferFormat1 0x04 284 * UCHAR OldFileName[] OldFileName 285 * UCHAR BufferFormat1 0x04 286 * UCHAR OldFileName[] NewFileName 287 * 288 * Server Response Description 289 * ================================= ================================== 290 * UCHAR WordCount; Count of parameter words = 0 291 * UCHAR ByteCount; Count of data bytes = 0 292 */ 293 smb_sdrc_t 294 smb_pre_nt_rename(smb_request_t *sr) 295 { 296 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 297 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 298 uint32_t clusters; 299 int rc; 300 301 rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr, 302 &sr->arg.dirop.info_level, &clusters); 303 if (rc == 0) { 304 rc = smbsr_decode_data(sr, "%SS", sr, 305 &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path); 306 307 dst_fqi->fq_sattr = 0; 308 } 309 310 DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr, 311 struct dirop *, &sr->arg.dirop); 312 313 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 314 } 315 316 void 317 smb_post_nt_rename(smb_request_t *sr) 318 { 319 DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr); 320 } 321 322 smb_sdrc_t 323 smb_com_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 int rc; 328 329 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 330 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 331 ERRDOS, ERROR_ACCESS_DENIED); 332 return (SDRC_ERROR); 333 } 334 335 if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) { 336 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 337 ERRDOS, ERROR_BAD_PATHNAME); 338 return (SDRC_ERROR); 339 } 340 341 switch (sr->arg.dirop.info_level) { 342 case SMB_NT_RENAME_SET_LINK_INFO: 343 rc = smb_make_link(sr, src_fqi, dst_fqi); 344 break; 345 case SMB_NT_RENAME_RENAME_FILE: 346 case SMB_NT_RENAME_MOVE_FILE: 347 rc = smb_do_rename(sr, src_fqi, dst_fqi); 348 break; 349 case SMB_NT_RENAME_MOVE_CLUSTER_INFO: 350 rc = EINVAL; 351 break; 352 default: 353 rc = EACCES; 354 break; 355 } 356 357 if (rc != 0) { 358 smb_rename_set_error(sr, rc); 359 return (SDRC_ERROR); 360 } 361 362 rc = smbsr_encode_empty_result(sr); 363 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 364 } 365 366 /* 367 * smb_make_link 368 * 369 * Common code for creating a hard link (adding an additional name 370 * for a file. 371 * 372 * If the source and destination are identical, we go through all 373 * the checks but we don't create a link. 374 * 375 * Returns errno values. 376 */ 377 static int 378 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 379 { 380 smb_node_t *src_fnode; 381 DWORD status; 382 int rc; 383 int count; 384 385 if ((rc = smbd_fs_query(sr, src_fqi, FQM_PATH_MUST_EXIST)) != 0) 386 return (rc); 387 388 src_fnode = src_fqi->fq_fnode; 389 390 if ((rc = smb_rename_check_attr(sr, src_fnode, src_fqi->fq_sattr)) != 0) 391 goto link_cleanup_nodes; 392 393 /* 394 * Break the oplock before access checks. If a client 395 * has a file open, this will force a flush or close, 396 * which may affect the outcome of any share checking. 397 */ 398 (void) smb_oplock_break(src_fnode, sr->session, B_FALSE); 399 400 for (count = 0; count <= 3; count++) { 401 if (count) { 402 smb_node_end_crit(src_fnode); 403 delay(MSEC_TO_TICK(400)); 404 } 405 406 smb_node_start_crit(src_fnode, RW_READER); 407 status = smb_node_rename_check(src_fnode); 408 409 if (status != NT_STATUS_SHARING_VIOLATION) 410 break; 411 } 412 413 if (status == NT_STATUS_SHARING_VIOLATION) { 414 smb_node_end_crit(src_fnode); 415 rc = EPIPE; /* = ERRbadshare */ 416 goto link_cleanup_nodes; 417 } 418 419 status = smb_range_check(sr, src_fnode, 0, UINT64_MAX, B_TRUE); 420 421 if (status != NT_STATUS_SUCCESS) { 422 smb_node_end_crit(src_fnode); 423 rc = EACCES; 424 goto link_cleanup_nodes; 425 } 426 427 if (utf8_strcasecmp(src_fqi->fq_path.pn_path, 428 dst_fqi->fq_path.pn_path) == 0) { 429 smb_node_end_crit(src_fnode); 430 rc = 0; 431 goto link_cleanup_nodes; 432 } 433 434 rc = smbd_fs_query(sr, dst_fqi, FQM_PATH_MUST_NOT_EXIST); 435 if (rc != 0) { 436 smb_node_end_crit(src_fnode); 437 goto link_cleanup_nodes; 438 } 439 440 /* 441 * On success of FQM_PATH_MUST_NOT_EXIST only dst_fqi->fq_dnode 442 * is valid (dst_fqi->fq_fnode is NULL). 443 */ 444 rc = smb_fsop_link(sr, sr->user_cr, dst_fqi->fq_dnode, src_fnode, 445 dst_fqi->fq_last_comp); 446 447 smb_node_end_crit(src_fnode); 448 449 if (rc == 0) 450 smb_node_notify_change(dst_fqi->fq_dnode); 451 452 link_cleanup_nodes: 453 smb_node_release(src_fnode); 454 smb_node_release(src_fqi->fq_dnode); 455 456 if (dst_fqi->fq_dnode) 457 smb_node_release(dst_fqi->fq_dnode); 458 459 SMB_NULL_FQI_NODES(*src_fqi); 460 SMB_NULL_FQI_NODES(*dst_fqi); 461 return (rc); 462 } 463 464 static int 465 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr) 466 { 467 smb_attr_t attr; 468 469 if (smb_node_getattr(sr, node, &attr) != 0) 470 return (EIO); 471 472 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && 473 !(SMB_SEARCH_HIDDEN(sattr))) 474 return (ESRCH); 475 476 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && 477 !(SMB_SEARCH_SYSTEM(sattr))) 478 return (ESRCH); 479 480 return (0); 481 } 482 483 /* 484 * The following values are based on observed WFWG, Windows 9x, Windows NT 485 * and Windows 2000 behaviour. 486 * 487 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients. 488 * 489 * Windows 95 clients don't see the problem because the target is deleted 490 * before the rename request. 491 */ 492 static void 493 smb_rename_set_error(smb_request_t *sr, int errnum) 494 { 495 static struct { 496 int errnum; 497 uint16_t errcode; 498 uint32_t status32; 499 } rc_map[] = { 500 { EEXIST, ERROR_ALREADY_EXISTS, NT_STATUS_OBJECT_NAME_COLLISION }, 501 { EPIPE, ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION }, 502 { ENOENT, ERROR_FILE_NOT_FOUND, NT_STATUS_OBJECT_NAME_NOT_FOUND }, 503 { ESRCH, ERROR_FILE_NOT_FOUND, NT_STATUS_NO_SUCH_FILE }, 504 { EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER }, 505 { EACCES, ERROR_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED }, 506 { EIO, ERROR_INTERNAL_ERROR, NT_STATUS_INTERNAL_ERROR } 507 }; 508 509 int i; 510 511 if (errnum == 0) 512 return; 513 514 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) { 515 if (rc_map[i].errnum == errnum) { 516 smbsr_error(sr, rc_map[i].status32, 517 ERRDOS, rc_map[i].errcode); 518 return; 519 } 520 } 521 522 smbsr_errno(sr, errnum); 523 } 524