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