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
smb_pre_delete(smb_request_t * sr)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
smb_post_delete(smb_request_t * sr)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
smb_com_delete(smb_request_t * sr)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
smb_delete_single_file(smb_request_t * sr,smb_error_t * err)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
smb_delete_multiple_files(smb_request_t * sr,smb_error_t * err)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
smb_delete_find_fname(smb_request_t * sr,smb_odir_t * od,char * namebuf,int len)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
smb_delete_check_dosattr(smb_request_t * sr,smb_error_t * err)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
smb_delete_remove_file(smb_request_t * sr,smb_error_t * err)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
smb_delete_check_path(smb_request_t * sr)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
smb_delete_error(smb_error_t * err,uint32_t status,uint16_t errcls,uint16_t errcode)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