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