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