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