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 /* 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 #include <sys/sunddi.h> 28 #include <sys/nbmlock.h> 29 30 #include <smbsrv/smb_kproto.h> 31 #include <smbsrv/smb_fsops.h> 32 #include <smbsrv/smbinfo.h> 33 34 static int smb_delete_check_path(smb_request_t *); 35 static int smb_delete_single_file(smb_request_t *, smb_error_t *); 36 static int smb_delete_multiple_files(smb_request_t *, smb_error_t *); 37 static int smb_delete_find_fname(smb_request_t *, smb_odir_t *, char *, int); 38 static int smb_delete_check_dosattr(smb_request_t *, smb_error_t *); 39 static int smb_delete_remove_file(smb_request_t *, smb_error_t *); 40 41 static void smb_delete_error(smb_error_t *, uint32_t, uint16_t, uint16_t); 42 43 /* 44 * smb_com_delete 45 * 46 * The delete file message is sent to delete a data file. The appropriate 47 * Tid and additional pathname are passed. Read only files may not be 48 * deleted, the read-only attribute must be reset prior to file deletion. 49 * 50 * NT supports a hidden permission known as File Delete Child (FDC). If 51 * the user has FullControl access to a directory, the user is permitted 52 * to delete any object in the directory regardless of the permissions 53 * on the object. 54 * 55 * Client Request Description 56 * ================================== ================================= 57 * UCHAR WordCount; Count of parameter words = 1 58 * USHORT SearchAttributes; 59 * USHORT ByteCount; Count of data bytes; min = 2 60 * UCHAR BufferFormat; 0x04 61 * STRING FileName[]; File name 62 * 63 * Multiple files may be deleted in response to a single request as 64 * SMB_COM_DELETE supports wildcards 65 * 66 * SearchAttributes indicates the attributes that the target file(s) must 67 * have. If the attribute is zero then only normal files are deleted. If 68 * the system file or hidden attributes are specified then the delete is 69 * inclusive -both the specified type(s) of files and normal files are 70 * deleted. Attributes are described in the "Attribute Encoding" section 71 * of this document. 72 * 73 * If bit0 of the Flags2 field of the SMB header is set, a pattern is 74 * passed in, and the file has a long name, then the passed pattern much 75 * match the long file name for the delete to succeed. If bit0 is clear, a 76 * pattern is passed in, and the file has a long name, then the passed 77 * pattern must match the file's short name for the deletion to succeed. 78 * 79 * Server Response Description 80 * ================================== ================================= 81 * UCHAR WordCount; Count of parameter words = 0 82 * USHORT ByteCount; Count of data bytes = 0 83 * 84 * 4.2.10.1 Errors 85 * 86 * ERRDOS/ERRbadpath 87 * ERRDOS/ERRbadfile 88 * ERRDOS/ERRnoaccess 89 * ERRDOS/ERRbadshare # returned by NT for files that are already open 90 * ERRHRD/ERRnowrite 91 * ERRSRV/ERRaccess 92 * ERRSRV/ERRinvdevice 93 * ERRSRV/ERRinvid 94 * ERRSRV/ERRbaduid 95 */ 96 smb_sdrc_t 97 smb_pre_delete(smb_request_t *sr) 98 { 99 int rc; 100 smb_fqi_t *fqi; 101 102 fqi = &sr->arg.dirop.fqi; 103 104 if ((rc = smbsr_decode_vwv(sr, "w", &fqi->fq_sattr)) == 0) 105 rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path); 106 107 DTRACE_SMB_2(op__Delete__start, smb_request_t *, sr, smb_fqi_t *, fqi); 108 109 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 110 } 111 112 void 113 smb_post_delete(smb_request_t *sr) 114 { 115 DTRACE_SMB_1(op__Delete__done, smb_request_t *, sr); 116 } 117 118 /* 119 * smb_com_delete 120 * 121 * 1. intialize, pre-process and validate pathname 122 * 123 * 2. process the path to get directory node & last_comp, 124 * store these in fqi 125 * - If smb_pathname_reduce cannot find the specified path, 126 * the error (ENOTDIR) is translated to NT_STATUS_OBJECT_PATH_NOT_FOUND 127 * if the target is a single file (no wildcards). If there are 128 * wildcards in the last_comp, NT_STATUS_OBJECT_NAME_NOT_FOUND is 129 * used instead. 130 * - If the directory node is the mount point and the last component 131 * is ".." NT_STATUS_OBJECT_PATH_SYNTAX_BAD is returned. 132 * 133 * 3. check access permissions 134 * 135 * 4. invoke the appropriate deletion routine to find and remove 136 * the specified file(s). 137 * - if target is a single file (no wildcards) - smb_delete_single_file 138 * - if the target contains wildcards - smb_delete_multiple_files 139 * 140 * Returns: SDRC_SUCCESS or SDRC_ERROR 141 */ 142 smb_sdrc_t 143 smb_com_delete(smb_request_t *sr) 144 { 145 int rc; 146 smb_error_t err; 147 uint32_t status; 148 boolean_t wildcards = B_FALSE; 149 smb_fqi_t *fqi; 150 smb_pathname_t *pn; 151 152 fqi = &sr->arg.dirop.fqi; 153 pn = &fqi->fq_path; 154 155 smb_pathname_init(sr, pn, pn->pn_path); 156 if (!smb_pathname_validate(sr, pn)) 157 return (SDRC_ERROR); 158 if (smb_delete_check_path(sr) != 0) 159 return (SDRC_ERROR); 160 161 wildcards = smb_contains_wildcards(pn->pn_fname); 162 163 rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path, 164 sr->tid_tree->t_snode, sr->tid_tree->t_snode, 165 &fqi->fq_dnode, fqi->fq_last_comp); 166 if (rc == 0) { 167 if (!smb_node_is_dir(fqi->fq_dnode)) { 168 smb_node_release(fqi->fq_dnode); 169 rc = ENOTDIR; 170 } 171 } 172 if (rc != 0) { 173 if (rc == ENOTDIR) { 174 if (wildcards) 175 status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 176 else 177 status = NT_STATUS_OBJECT_PATH_NOT_FOUND; 178 smbsr_error(sr, status, ERRDOS, ERROR_FILE_NOT_FOUND); 179 } else { 180 smbsr_errno(sr, rc); 181 } 182 183 return (SDRC_ERROR); 184 } 185 186 if ((fqi->fq_dnode == sr->tid_tree->t_snode) && 187 (strcmp(fqi->fq_last_comp, "..") == 0)) { 188 smb_node_release(fqi->fq_dnode); 189 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 190 ERRDOS, ERROR_BAD_PATHNAME); 191 return (SDRC_ERROR); 192 } 193 194 rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode, 195 FILE_LIST_DIRECTORY); 196 if (rc != 0) { 197 smb_node_release(fqi->fq_dnode); 198 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 199 ERRDOS, ERROR_ACCESS_DENIED); 200 return (SDRC_ERROR); 201 } 202 203 if (wildcards) 204 rc = smb_delete_multiple_files(sr, &err); 205 else 206 rc = smb_delete_single_file(sr, &err); 207 208 smb_node_release(fqi->fq_dnode); 209 210 if (rc != 0) 211 smbsr_set_error(sr, &err); 212 else 213 rc = smbsr_encode_empty_result(sr); 214 215 return (rc == 0 ? SDRC_SUCCESS : SDRC_ERROR); 216 } 217 218 /* 219 * smb_delete_single_file 220 * 221 * Find the specified file and, if its attributes match the search 222 * criteria, delete it. 223 * 224 * Returns 0 - success (file deleted) 225 * -1 - error, err is populated with error details 226 */ 227 static int 228 smb_delete_single_file(smb_request_t *sr, smb_error_t *err) 229 { 230 smb_fqi_t *fqi; 231 smb_pathname_t *pn; 232 233 fqi = &sr->arg.dirop.fqi; 234 pn = &fqi->fq_path; 235 236 /* pn already initialized and validated */ 237 if (!smb_validate_object_name(sr, pn)) { 238 smb_delete_error(err, sr->smb_error.status, 239 ERRDOS, ERROR_INVALID_NAME); 240 return (-1); 241 } 242 243 if (smb_fsop_lookup_name(sr, sr->user_cr, 0, sr->tid_tree->t_snode, 244 fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode) != 0) { 245 smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND, 246 ERRDOS, ERROR_FILE_NOT_FOUND); 247 return (-1); 248 } 249 250 if (smb_delete_check_dosattr(sr, err) != 0) { 251 smb_node_release(fqi->fq_fnode); 252 return (-1); 253 } 254 255 if (smb_delete_remove_file(sr, err) != 0) { 256 smb_node_release(fqi->fq_fnode); 257 return (-1); 258 } 259 260 smb_node_release(fqi->fq_fnode); 261 return (0); 262 } 263 264 /* 265 * smb_delete_multiple_files 266 * 267 * For each matching file found by smb_delete_find_fname: 268 * 1. lookup file 269 * 2. check the file's attributes 270 * - The search ends with an error if a readonly file 271 * (NT_STATUS_CANNOT_DELETE) is matched. 272 * - The search ends (but not an error) if a directory is 273 * matched and the request's search did not include 274 * directories. 275 * - Otherwise, if smb_delete_check_dosattr fails the file 276 * is skipped and the search continues (at step 1) 277 * 3. delete the file 278 * 279 * Returns 0 - success 280 * -1 - error, err is populated with error details 281 */ 282 static int 283 smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err) 284 { 285 int rc, deleted = 0; 286 smb_fqi_t *fqi; 287 uint16_t odid; 288 smb_odir_t *od; 289 char namebuf[MAXNAMELEN]; 290 291 fqi = &sr->arg.dirop.fqi; 292 293 /* 294 * Specify all search attributes (SMB_SEARCH_ATTRIBUTES) so that 295 * delete-specific checking can be done (smb_delete_check_dosattr). 296 */ 297 odid = smb_odir_open(sr, fqi->fq_path.pn_path, 298 SMB_SEARCH_ATTRIBUTES, 0); 299 if (odid == 0) 300 return (-1); 301 302 if ((od = smb_tree_lookup_odir(sr, odid)) == NULL) 303 return (-1); 304 305 for (;;) { 306 rc = smb_delete_find_fname(sr, od, namebuf, MAXNAMELEN); 307 if (rc != 0) 308 break; 309 310 rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_CASE_SENSITIVE, 311 sr->tid_tree->t_snode, fqi->fq_dnode, 312 namebuf, &fqi->fq_fnode); 313 if (rc != 0) 314 break; 315 316 if (smb_delete_check_dosattr(sr, err) != 0) { 317 smb_node_release(fqi->fq_fnode); 318 if (err->status == NT_STATUS_CANNOT_DELETE) { 319 smb_odir_close(od); 320 smb_odir_release(od); 321 return (-1); 322 } 323 if ((err->status == NT_STATUS_FILE_IS_A_DIRECTORY) && 324 (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) != 0)) 325 break; 326 continue; 327 } 328 329 if (smb_delete_remove_file(sr, err) == 0) { 330 ++deleted; 331 smb_node_release(fqi->fq_fnode); 332 continue; 333 } 334 if (err->status == NT_STATUS_OBJECT_NAME_NOT_FOUND) { 335 smb_node_release(fqi->fq_fnode); 336 continue; 337 } 338 339 smb_odir_close(od); 340 smb_odir_release(od); 341 smb_node_release(fqi->fq_fnode); 342 return (-1); 343 } 344 345 smb_odir_close(od); 346 smb_odir_release(od); 347 348 if ((rc != 0) && (rc != ENOENT)) { 349 smbsr_map_errno(rc, err); 350 return (-1); 351 } 352 353 if (deleted == 0) { 354 smb_delete_error(err, NT_STATUS_NO_SUCH_FILE, 355 ERRDOS, ERROR_FILE_NOT_FOUND); 356 return (-1); 357 } 358 359 return (0); 360 } 361 362 /* 363 * smb_delete_find_fname 364 * 365 * Find next filename that matches search pattern and return it 366 * in namebuf. 367 * 368 * Returns: 0 - success 369 * errno 370 */ 371 static int 372 smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od, char *namebuf, int len) 373 { 374 int rc; 375 smb_odirent_t *odirent; 376 boolean_t eos; 377 378 odirent = kmem_alloc(sizeof (smb_odirent_t), KM_SLEEP); 379 380 rc = smb_odir_read(sr, od, odirent, &eos); 381 if (rc == 0) { 382 if (eos) 383 rc = ENOENT; 384 else 385 (void) strlcpy(namebuf, odirent->od_name, len); 386 } 387 kmem_free(odirent, sizeof (smb_odirent_t)); 388 return (rc); 389 } 390 391 /* 392 * smb_delete_check_dosattr 393 * 394 * Check file's dos atributes to ensure that 395 * 1. the file is not a directory - NT_STATUS_FILE_IS_A_DIRECTORY 396 * 2. the file is not readonly - NT_STATUS_CANNOT_DELETE 397 * 3. the file's dos attributes comply with the specified search attributes 398 * If the file is either hidden or system and those attributes 399 * are not specified in the search attributes - NT_STATUS_NO_SUCH_FILE 400 * 401 * Returns: 0 - file's attributes pass all checks 402 * -1 - err populated with error details 403 */ 404 static int 405 smb_delete_check_dosattr(smb_request_t *sr, smb_error_t *err) 406 { 407 smb_fqi_t *fqi; 408 smb_node_t *node; 409 smb_attr_t attr; 410 uint16_t sattr; 411 412 fqi = &sr->arg.dirop.fqi; 413 sattr = fqi->fq_sattr; 414 node = fqi->fq_fnode; 415 416 bzero(&attr, sizeof (attr)); 417 attr.sa_mask = SMB_AT_DOSATTR; 418 if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0) { 419 smb_delete_error(err, NT_STATUS_INTERNAL_ERROR, 420 ERRDOS, ERROR_INTERNAL_ERROR); 421 return (-1); 422 } 423 424 if (attr.sa_dosattr & FILE_ATTRIBUTE_DIRECTORY) { 425 smb_delete_error(err, NT_STATUS_FILE_IS_A_DIRECTORY, 426 ERRDOS, ERROR_ACCESS_DENIED); 427 return (-1); 428 } 429 430 if (SMB_PATHFILE_IS_READONLY(sr, node)) { 431 smb_delete_error(err, NT_STATUS_CANNOT_DELETE, 432 ERRDOS, ERROR_ACCESS_DENIED); 433 return (-1); 434 } 435 436 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && 437 !(SMB_SEARCH_HIDDEN(sattr))) { 438 smb_delete_error(err, NT_STATUS_NO_SUCH_FILE, 439 ERRDOS, ERROR_FILE_NOT_FOUND); 440 return (-1); 441 } 442 443 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && 444 !(SMB_SEARCH_SYSTEM(sattr))) { 445 smb_delete_error(err, NT_STATUS_NO_SUCH_FILE, 446 ERRDOS, ERROR_FILE_NOT_FOUND); 447 return (-1); 448 } 449 450 return (0); 451 } 452 453 /* 454 * smb_delete_remove_file 455 * 456 * For consistency with Windows 2000, the range check should be done 457 * after checking for sharing violations. Attempting to delete a 458 * locked file will result in sharing violation, which is the same 459 * thing that will happen if you try to delete a non-locked open file. 460 * 461 * Note that windows 2000 rejects lock requests on open files that 462 * have been opened with metadata open modes. The error is 463 * STATUS_ACCESS_DENIED. 464 * 465 * NT does not always close a file immediately, which can cause the 466 * share and access checking to fail (the node refcnt is greater 467 * than one), and the file doesn't get deleted. Breaking the oplock 468 * before share and lock checking gives the client a chance to 469 * close the file. 470 * 471 * Returns: 0 - success 472 * -1 - error, err populated with error details 473 */ 474 static int 475 smb_delete_remove_file(smb_request_t *sr, smb_error_t *err) 476 { 477 int rc, count; 478 uint32_t status; 479 smb_fqi_t *fqi; 480 smb_node_t *node; 481 uint32_t flags = 0; 482 483 fqi = &sr->arg.dirop.fqi; 484 node = fqi->fq_fnode; 485 486 /* 487 * Break BATCH oplock before ofile checks. If a client 488 * has a file open, this will force a flush or close, 489 * which may affect the outcome of any share checking. 490 */ 491 (void) smb_oplock_break(sr, node, 492 SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH); 493 494 /* 495 * Wait (a little) for the oplock break to be 496 * responded to by clients closing handles. 497 * Hold node->n_lock as reader to keep new 498 * ofiles from showing up after we check. 499 */ 500 smb_node_rdlock(node); 501 for (count = 0; count <= 12; count++) { 502 status = smb_node_delete_check(node); 503 if (status != NT_STATUS_SHARING_VIOLATION) 504 break; 505 smb_node_unlock(node); 506 delay(MSEC_TO_TICK(100)); 507 smb_node_rdlock(node); 508 } 509 if (status != NT_STATUS_SUCCESS) { 510 smb_delete_error(err, NT_STATUS_SHARING_VIOLATION, 511 ERRDOS, ERROR_SHARING_VIOLATION); 512 smb_node_unlock(node); 513 return (-1); 514 } 515 516 /* 517 * Note, the combination of these two: 518 * smb_node_rdlock(node); 519 * nbl_start_crit(node->vp, RW_READER); 520 * is equivalent to this call: 521 * smb_node_start_crit(node, RW_READER) 522 * 523 * Cleanup after this point should use: 524 * smb_node_end_crit(node) 525 */ 526 nbl_start_crit(node->vp, RW_READER); 527 528 /* 529 * This checks nbl_share_conflict, nbl_lock_conflict 530 */ 531 status = smb_nbl_conflict(node, 0, UINT64_MAX, NBL_REMOVE); 532 if (status == NT_STATUS_SHARING_VIOLATION) { 533 smb_node_end_crit(node); 534 smb_delete_error(err, NT_STATUS_SHARING_VIOLATION, 535 ERRDOS, ERROR_SHARING_VIOLATION); 536 return (-1); 537 } 538 if (status != NT_STATUS_SUCCESS) { 539 smb_node_end_crit(node); 540 smb_delete_error(err, NT_STATUS_ACCESS_DENIED, 541 ERRDOS, ERROR_ACCESS_DENIED); 542 return (-1); 543 } 544 545 if (SMB_TREE_SUPPORTS_CATIA(sr)) 546 flags |= SMB_CATIA; 547 548 rc = smb_fsop_remove(sr, sr->user_cr, node->n_dnode, 549 node->od_name, flags); 550 if (rc != 0) { 551 if (rc == ENOENT) 552 smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND, 553 ERRDOS, ERROR_FILE_NOT_FOUND); 554 else 555 smbsr_map_errno(rc, err); 556 557 smb_node_end_crit(node); 558 return (-1); 559 } 560 561 smb_node_end_crit(node); 562 return (0); 563 } 564 565 566 /* 567 * smb_delete_check_path 568 * 569 * smb_pathname_validate() should already have been used to 570 * perform initial validation on the pathname. Additional 571 * request specific validation of the filename is performed 572 * here. 573 * 574 * - pn->pn_fname is NULL should result in NT_STATUS_FILE_IS_A_DIRECTORY 575 * 576 * - Any wildcard filename that resolves to '.' should result in 577 * NT_STATUS_OBJECT_NAME_INVALID if the search attributes include 578 * FILE_ATTRIBUTE_DIRECTORY 579 * 580 * Returns: 581 * 0: path is valid. 582 * -1: path is invalid. Sets error information in sr. 583 */ 584 static int 585 smb_delete_check_path(smb_request_t *sr) 586 { 587 smb_fqi_t *fqi = &sr->arg.dirop.fqi; 588 smb_pathname_t *pn = &fqi->fq_path; 589 590 if (pn->pn_fname == NULL) { 591 smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY, 592 ERRDOS, ERROR_ACCESS_DENIED); 593 return (-1); 594 } 595 596 /* fname component is, or resolves to, '.' (dot) */ 597 if ((strcmp(pn->pn_fname, ".") == 0) || 598 (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) && 599 (smb_match(pn->pn_fname, ".", B_FALSE)))) { 600 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 601 ERRDOS, ERROR_INVALID_NAME); 602 return (-1); 603 } 604 605 return (0); 606 } 607 608 /* 609 * smb_delete_error 610 */ 611 static void 612 smb_delete_error(smb_error_t *err, 613 uint32_t status, uint16_t errcls, uint16_t errcode) 614 { 615 err->status = status; 616 err->errcls = errcls; 617 err->errcode = errcode; 618 } 619