xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_delete.c (revision 5fd03bc0f2e00e7ba02316c2e08f45d52aab15db)
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