xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_delete.c (revision faa1795a28a5c712eed6d0a3f84d98c368a316c6)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
22dc20a302Sas200622  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw 
26da6c28aaSamw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27da6c28aaSamw 
28da6c28aaSamw #include <smbsrv/smb_incl.h>
29da6c28aaSamw #include <smbsrv/smb_fsops.h>
30da6c28aaSamw #include <smbsrv/smbinfo.h>
31dc20a302Sas200622 #include <sys/nbmlock.h>
32da6c28aaSamw 
33*faa1795aSjb150015 static uint32_t smb_delete_check(smb_request_t *, smb_node_t *);
34da6c28aaSamw 
35da6c28aaSamw /*
36da6c28aaSamw  * smb_com_delete
37da6c28aaSamw  *
38da6c28aaSamw  * The delete file message is sent to delete a data file. The appropriate
39da6c28aaSamw  * Tid and additional pathname are passed. Read only files may not be
40da6c28aaSamw  * deleted, the read-only attribute must be reset prior to file deletion.
41da6c28aaSamw  *
42da6c28aaSamw  * NT supports a hidden permission known as File Delete Child (FDC). If
43da6c28aaSamw  * the user has FullControl access to a directory, the user is permitted
44da6c28aaSamw  * to delete any object in the directory regardless of the permissions
45da6c28aaSamw  * on the object.
46da6c28aaSamw  *
47da6c28aaSamw  * Client Request                     Description
48da6c28aaSamw  * ================================== =================================
49da6c28aaSamw  * UCHAR WordCount;                   Count of parameter words = 1
50da6c28aaSamw  * USHORT SearchAttributes;
51da6c28aaSamw  * USHORT ByteCount;                  Count of data bytes; min = 2
52da6c28aaSamw  * UCHAR BufferFormat;                0x04
53da6c28aaSamw  * STRING FileName[];                 File name
54da6c28aaSamw  *
55da6c28aaSamw  * Multiple files may be deleted in response to a single request as
56da6c28aaSamw  * SMB_COM_DELETE supports wildcards
57da6c28aaSamw  *
58da6c28aaSamw  * SearchAttributes indicates the attributes that the target file(s) must
59da6c28aaSamw  * have. If the attribute is zero then only normal files are deleted. If
60da6c28aaSamw  * the system file or hidden attributes are specified then the delete is
61da6c28aaSamw  * inclusive -both the specified type(s) of files and normal files are
62da6c28aaSamw  * deleted. Attributes are described in the "Attribute Encoding" section
63da6c28aaSamw  * of this document.
64da6c28aaSamw  *
65da6c28aaSamw  * If bit0 of the Flags2 field of the SMB header is set, a pattern is
66da6c28aaSamw  * passed in, and the file has a long name, then the passed pattern  much
67da6c28aaSamw  * match the long file name for the delete to succeed. If bit0 is clear, a
68da6c28aaSamw  * pattern is passed in, and the file has a long name, then the passed
69da6c28aaSamw  * pattern must match the file's short name for the deletion to succeed.
70da6c28aaSamw  *
71da6c28aaSamw  * Server Response                    Description
72da6c28aaSamw  * ================================== =================================
73da6c28aaSamw  * UCHAR WordCount;                   Count of parameter words = 0
74da6c28aaSamw  * USHORT ByteCount;                  Count of data bytes = 0
75da6c28aaSamw  *
76da6c28aaSamw  * 4.2.10.1  Errors
77da6c28aaSamw  *
78da6c28aaSamw  * ERRDOS/ERRbadpath
79da6c28aaSamw  * ERRDOS/ERRbadfile
80da6c28aaSamw  * ERRDOS/ERRnoaccess
81da6c28aaSamw  * ERRDOS/ERRbadshare	# returned by NT for files that are already open
82da6c28aaSamw  * ERRHRD/ERRnowrite
83da6c28aaSamw  * ERRSRV/ERRaccess
84da6c28aaSamw  * ERRSRV/ERRinvdevice
85da6c28aaSamw  * ERRSRV/ERRinvid
86da6c28aaSamw  * ERRSRV/ERRbaduid
87da6c28aaSamw  */
887b59d02dSjb150015 smb_sdrc_t
89*faa1795aSjb150015 smb_pre_delete(smb_request_t *sr)
90da6c28aaSamw {
91*faa1795aSjb150015 	struct smb_fqi *fqi = &sr->arg.dirop.fqi;
92*faa1795aSjb150015 	int rc;
93*faa1795aSjb150015 
94*faa1795aSjb150015 	if ((rc = smbsr_decode_vwv(sr, "w", &fqi->srch_attr)) == 0)
95*faa1795aSjb150015 		rc = smbsr_decode_data(sr, "%S", sr, &fqi->path);
96*faa1795aSjb150015 
97*faa1795aSjb150015 	DTRACE_SMB_2(op__Delete__start, smb_request_t *, sr,
98*faa1795aSjb150015 	    struct smb_fqi *, fqi);
99*faa1795aSjb150015 
100*faa1795aSjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
101*faa1795aSjb150015 }
102*faa1795aSjb150015 
103*faa1795aSjb150015 void
104*faa1795aSjb150015 smb_post_delete(smb_request_t *sr)
105*faa1795aSjb150015 {
106*faa1795aSjb150015 	DTRACE_SMB_1(op__Delete__done, smb_request_t *, sr);
107*faa1795aSjb150015 }
108*faa1795aSjb150015 
109*faa1795aSjb150015 smb_sdrc_t
110*faa1795aSjb150015 smb_com_delete(smb_request_t *sr)
111*faa1795aSjb150015 {
112*faa1795aSjb150015 	struct smb_fqi *fqi = &sr->arg.dirop.fqi;
113da6c28aaSamw 	int	rc;
114da6c28aaSamw 	int	od = 0;
115da6c28aaSamw 	int	deleted = 0;
116da6c28aaSamw 	struct smb_node *dir_snode;
117da6c28aaSamw 	struct smb_node *node = 0;
118da6c28aaSamw 	char *name;
119da6c28aaSamw 	char *fname;
120da6c28aaSamw 	char *sname;
121da6c28aaSamw 	char *fullname;
122*faa1795aSjb150015 	uint32_t status;
123da6c28aaSamw 	int is_stream;
124da6c28aaSamw 	smb_odir_context_t *pc;
125da6c28aaSamw 
126*faa1795aSjb150015 	if (smb_rdir_open(sr, fqi->path, fqi->srch_attr) != 0)
127*faa1795aSjb150015 		return (SDRC_ERROR);
128da6c28aaSamw 
129dc20a302Sas200622 	pc = kmem_zalloc(sizeof (*pc), KM_SLEEP);
130dc20a302Sas200622 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
131dc20a302Sas200622 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
132dc20a302Sas200622 	name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
133dc20a302Sas200622 	fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
134dc20a302Sas200622 
135*faa1795aSjb150015 	is_stream = smb_stream_parse_name(fqi->path, fname, sname);
136da6c28aaSamw 	dir_snode = sr->sid_odir->d_dir_snode;
137da6c28aaSamw 
138da6c28aaSamw 	/*
139da6c28aaSamw 	 * This while loop is meant to deal with wildcards.
140da6c28aaSamw 	 * It is not expected that wildcards will exist for
141da6c28aaSamw 	 * streams.  For the streams case, it is expected
142da6c28aaSamw 	 * that the below loop will be executed only once.
143da6c28aaSamw 	 */
144da6c28aaSamw 
145da6c28aaSamw 	while ((rc = smb_rdir_next(sr, &node, pc)) == 0) {
146da6c28aaSamw 		(void) strlcpy(name, pc->dc_name, MAXNAMELEN);
147da6c28aaSamw 
148dc20a302Sas200622 		if (pc->dc_dattr & SMB_FA_DIRECTORY) {
149*faa1795aSjb150015 			smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY,
150*faa1795aSjb150015 			    ERRDOS, ERROR_ACCESS_DENIED);
151da6c28aaSamw 			smb_node_release(node);
152da6c28aaSamw 			goto delete_error;
153da6c28aaSamw 		}
154da6c28aaSamw 
155dc20a302Sas200622 		if ((pc->dc_dattr & SMB_FA_READONLY) ||
156dc20a302Sas200622 		    (node->flags & NODE_CREATED_READONLY)) {
157*faa1795aSjb150015 			smbsr_error(sr, NT_STATUS_CANNOT_DELETE,
158*faa1795aSjb150015 			    ERRDOS, ERROR_ACCESS_DENIED);
159da6c28aaSamw 			smb_node_release(node);
160dc20a302Sas200622 			goto delete_error;
161dc20a302Sas200622 		}
162dc20a302Sas200622 
163dc20a302Sas200622 		/*
164dc20a302Sas200622 		 * NT does not always close a file immediately, which
165dc20a302Sas200622 		 * can cause the share and access checking to fail
166dc20a302Sas200622 		 * (the node refcnt is greater than one), and the file
167dc20a302Sas200622 		 * doesn't get deleted. Breaking the oplock before
168dc20a302Sas200622 		 * share and access checking gives the client a chance
169dc20a302Sas200622 		 * to close the file.
170dc20a302Sas200622 		 */
171dc20a302Sas200622 
172dc20a302Sas200622 		if (OPLOCKS_IN_FORCE(node)) {
173*faa1795aSjb150015 			status = smb_break_oplock(sr, node);
174dc20a302Sas200622 
175*faa1795aSjb150015 			if (status != NT_STATUS_SUCCESS) {
176*faa1795aSjb150015 				smbsr_error(sr, status,
177*faa1795aSjb150015 				    ERRDOS, ERROR_VC_DISCONNECTED);
178dc20a302Sas200622 				smb_node_release(node);
179dc20a302Sas200622 				goto delete_error;
180dc20a302Sas200622 			}
181dc20a302Sas200622 		}
182dc20a302Sas200622 
183dc20a302Sas200622 		smb_node_start_crit(node, RW_READER);
184dc20a302Sas200622 
185*faa1795aSjb150015 		if (smb_delete_check(sr, node) != NT_STATUS_SUCCESS) {
186dc20a302Sas200622 			smb_node_end_crit(node);
187dc20a302Sas200622 			smb_node_release(node);
188dc20a302Sas200622 			goto delete_error;
189dc20a302Sas200622 		}
190da6c28aaSamw 
191da6c28aaSamw 		if (is_stream) {
192da6c28aaSamw 			/*
193da6c28aaSamw 			 * It is assumed that fname does not contain
194da6c28aaSamw 			 * any wildcards .
195da6c28aaSamw 			 * smb_fsop_remove() requires filename+streamname
196da6c28aaSamw 			 */
197da6c28aaSamw 			(void) snprintf(fullname, MAXPATHLEN, "%s%s",
198da6c28aaSamw 			    fname, sname);
199da6c28aaSamw 			rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
200da6c28aaSamw 			    fullname, 0);
201da6c28aaSamw 		} else {
202da6c28aaSamw 			/*
203da6c28aaSamw 			 * name (i.e. pc->dc_name) is the on-disk name
204da6c28aaSamw 			 * unless there is a case collision, in which
205da6c28aaSamw 			 * case readdir will have returned a mangled name.
206da6c28aaSamw 			 */
207da6c28aaSamw 			if (smb_maybe_mangled_name(name) == 0)
208da6c28aaSamw 				od = 1;
209da6c28aaSamw 
210da6c28aaSamw 			rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
211da6c28aaSamw 			    name, od);
212da6c28aaSamw 		}
213da6c28aaSamw 
214dc20a302Sas200622 		smb_node_end_crit(node);
215dc20a302Sas200622 		smb_node_release(node);
216dc20a302Sas200622 		node = NULL;
217dc20a302Sas200622 
218da6c28aaSamw 		if (rc != 0) {
219*faa1795aSjb150015 			if (rc != ENOENT) {
220*faa1795aSjb150015 				smbsr_errno(sr, rc);
221*faa1795aSjb150015 				goto delete_error;
222*faa1795aSjb150015 			}
223da6c28aaSamw 		} else {
224da6c28aaSamw 			deleted++;
225da6c28aaSamw 		}
226da6c28aaSamw 	}
227da6c28aaSamw 
228da6c28aaSamw 	if ((rc != 0) && (rc != ENOENT)) {
229*faa1795aSjb150015 		smbsr_errno(sr, rc);
230*faa1795aSjb150015 		goto delete_error;
231da6c28aaSamw 	}
232da6c28aaSamw 
233da6c28aaSamw 	if (deleted == 0) {
234*faa1795aSjb150015 		if (sr->sid_odir->d_wildcards == 0)
235*faa1795aSjb150015 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
236*faa1795aSjb150015 			    ERRDOS, ERROR_FILE_NOT_FOUND);
237*faa1795aSjb150015 		else
238*faa1795aSjb150015 			smbsr_error(sr, NT_STATUS_NO_SUCH_FILE,
239*faa1795aSjb150015 			    ERRDOS, ERROR_FILE_NOT_FOUND);
240da6c28aaSamw 		goto delete_error;
241da6c28aaSamw 	}
242da6c28aaSamw 
243da6c28aaSamw 	smb_rdir_close(sr);
244da6c28aaSamw 	kmem_free(pc, sizeof (*pc));
245da6c28aaSamw 	kmem_free(name, MAXNAMELEN);
246da6c28aaSamw 	kmem_free(fname, MAXNAMELEN);
247da6c28aaSamw 	kmem_free(sname, MAXNAMELEN);
248da6c28aaSamw 	kmem_free(fullname, MAXPATHLEN);
2497b59d02dSjb150015 
2507b59d02dSjb150015 	rc = smbsr_encode_empty_result(sr);
251*faa1795aSjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
252da6c28aaSamw 
253da6c28aaSamw delete_error:
254da6c28aaSamw 	smb_rdir_close(sr);
255da6c28aaSamw 	kmem_free(pc, sizeof (*pc));
256da6c28aaSamw 	kmem_free(name, MAXNAMELEN);
257da6c28aaSamw 	kmem_free(fname, MAXNAMELEN);
258da6c28aaSamw 	kmem_free(sname, MAXNAMELEN);
259da6c28aaSamw 	kmem_free(fullname, MAXPATHLEN);
260*faa1795aSjb150015 	return (SDRC_ERROR);
261da6c28aaSamw }
262da6c28aaSamw 
263da6c28aaSamw /*
264*faa1795aSjb150015  * For consistency with Windows 2000, the range check should be done
265*faa1795aSjb150015  * after checking for sharing violations.  Attempting to delete a
266*faa1795aSjb150015  * locked file will result in sharing violation, which is the same
267*faa1795aSjb150015  * thing that will happen if you try to delete a non-locked open file.
268da6c28aaSamw  *
269*faa1795aSjb150015  * Note that windows 2000 rejects lock requests on open files that
270*faa1795aSjb150015  * have been opened with metadata open modes.  The error is
271*faa1795aSjb150015  * STATUS_ACCESS_DENIED.
272da6c28aaSamw  */
273*faa1795aSjb150015 static uint32_t
274*faa1795aSjb150015 smb_delete_check(smb_request_t *sr, smb_node_t *node)
275*faa1795aSjb150015 {
276*faa1795aSjb150015 	uint32_t status;
277dc20a302Sas200622 
278*faa1795aSjb150015 	status = smb_node_delete_check(node);
279dc20a302Sas200622 
280*faa1795aSjb150015 	if (status == NT_STATUS_SHARING_VIOLATION) {
281*faa1795aSjb150015 		smbsr_error(sr, NT_STATUS_SHARING_VIOLATION,
282*faa1795aSjb150015 		    ERRDOS, ERROR_SHARING_VIOLATION);
283*faa1795aSjb150015 		return (status);
284da6c28aaSamw 	}
285da6c28aaSamw 
286*faa1795aSjb150015 	status = smb_range_check(sr, sr->user_cr, node, 0, UINT64_MAX, B_TRUE);
287*faa1795aSjb150015 
288*faa1795aSjb150015 	if (status != NT_STATUS_SUCCESS) {
289*faa1795aSjb150015 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
290*faa1795aSjb150015 		    ERRDOS, ERROR_ACCESS_DENIED);
291*faa1795aSjb150015 	}
292*faa1795aSjb150015 
293*faa1795aSjb150015 	return (status);
294da6c28aaSamw }
295