xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_delete.c (revision e38a713ad4e0a9c42f8cccd9350412b2c6ccccdb)
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 int
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 		smbsr_decode_error(sr);
109 		/* NOTREACHED */
110 	}
111 
112 	if (smbsr_decode_data(sr, "%S", sr, &path) != 0) {
113 		smbsr_decode_error(sr);
114 		/* NOTREACHED */
115 	}
116 
117 	pc = kmem_zalloc(sizeof (*pc), KM_SLEEP);
118 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
119 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
120 	name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
121 	fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
122 
123 	is_stream = smb_stream_parse_name(path, fname, sname);
124 
125 	(void) smb_rdir_open(sr, path, sattr);
126 	dir_snode = sr->sid_odir->d_dir_snode;
127 
128 	/*
129 	 * This while loop is meant to deal with wildcards.
130 	 * It is not expected that wildcards will exist for
131 	 * streams.  For the streams case, it is expected
132 	 * that the below loop will be executed only once.
133 	 */
134 
135 	while ((rc = smb_rdir_next(sr, &node, pc)) == 0) {
136 		(void) strlcpy(name, pc->dc_name, MAXNAMELEN);
137 
138 		if (pc->dc_dattr & SMB_FA_DIRECTORY) {
139 			smberr.errcls = ERRDOS;
140 			smberr.errcode = ERROR_ACCESS_DENIED;
141 			smberr.status = NT_STATUS_FILE_IS_A_DIRECTORY;
142 			smb_node_release(node);
143 			goto delete_error;
144 		}
145 
146 		if ((pc->dc_dattr & SMB_FA_READONLY) ||
147 		    (node->flags & NODE_CREATED_READONLY)) {
148 			smberr.errcls = ERRDOS;
149 			smberr.errcode = ERROR_ACCESS_DENIED;
150 			smberr.status = NT_STATUS_CANNOT_DELETE;
151 			smb_node_release(node);
152 			goto delete_error;
153 		}
154 
155 		/*
156 		 * NT does not always close a file immediately, which
157 		 * can cause the share and access checking to fail
158 		 * (the node refcnt is greater than one), and the file
159 		 * doesn't get deleted. Breaking the oplock before
160 		 * share and access checking gives the client a chance
161 		 * to close the file.
162 		 */
163 
164 		if (OPLOCKS_IN_FORCE(node)) {
165 			smberr.status = smb_break_oplock(sr, node);
166 
167 			if (smberr.status != NT_STATUS_SUCCESS) {
168 				smberr.errcls = ERRDOS;
169 				smberr.errcode = ERROR_VC_DISCONNECTED;
170 				smb_node_release(node);
171 				goto delete_error;
172 			}
173 		}
174 
175 		smb_node_start_crit(node, RW_READER);
176 
177 		if (smb_delete_check(sr, node, &smberr)) {
178 			smb_node_end_crit(node);
179 			smb_node_release(node);
180 			goto delete_error;
181 		}
182 
183 		if (is_stream) {
184 			/*
185 			 * It is assumed that fname does not contain
186 			 * any wildcards .
187 			 * smb_fsop_remove() requires filename+streamname
188 			 */
189 			(void) snprintf(fullname, MAXPATHLEN, "%s%s",
190 			    fname, sname);
191 			rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
192 			    fullname, 0);
193 		} else {
194 			/*
195 			 * name (i.e. pc->dc_name) is the on-disk name
196 			 * unless there is a case collision, in which
197 			 * case readdir will have returned a mangled name.
198 			 */
199 			if (smb_maybe_mangled_name(name) == 0)
200 				od = 1;
201 
202 			rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
203 			    name, od);
204 		}
205 
206 		smb_node_end_crit(node);
207 		smb_node_release(node);
208 		node = NULL;
209 
210 		if (rc != 0) {
211 			if (rc != ENOENT) {
212 				smb_rdir_close(sr);
213 				kmem_free(pc, sizeof (*pc));
214 				kmem_free(name, MAXNAMELEN);
215 				kmem_free(fname, MAXNAMELEN);
216 				kmem_free(sname, MAXNAMELEN);
217 				kmem_free(fullname, MAXPATHLEN);
218 				smbsr_errno(sr, rc);
219 				/* NOTREACHED */
220 			}
221 		} else {
222 			deleted++;
223 		}
224 	}
225 
226 	if ((rc != 0) && (rc != ENOENT)) {
227 		/* rc returned by smb_rdir_next() */
228 		smb_rdir_close(sr);
229 		kmem_free(pc, sizeof (*pc));
230 		kmem_free(name, MAXNAMELEN);
231 		kmem_free(fname, MAXNAMELEN);
232 		kmem_free(sname, MAXNAMELEN);
233 		kmem_free(fullname, MAXPATHLEN);
234 		smbsr_errno(sr, rc);
235 		/* NOTREACHED */
236 	}
237 
238 	if (deleted == 0) {
239 		smberr.errcls = ERRDOS;
240 		smberr.errcode = ERROR_FILE_NOT_FOUND;
241 		smberr.status = (sr->sid_odir->d_wildcards == 0)
242 		    ? NT_STATUS_OBJECT_NAME_NOT_FOUND : NT_STATUS_NO_SUCH_FILE;
243 		goto delete_error;
244 	}
245 
246 	smb_rdir_close(sr);
247 
248 	smbsr_encode_empty_result(sr);
249 
250 	kmem_free(pc, sizeof (*pc));
251 	kmem_free(name, MAXNAMELEN);
252 	kmem_free(fname, MAXNAMELEN);
253 	kmem_free(sname, MAXNAMELEN);
254 	kmem_free(fullname, MAXPATHLEN);
255 	return (SDRC_NORMAL_REPLY);
256 
257 delete_error:
258 	smb_rdir_close(sr);
259 	kmem_free(pc, sizeof (*pc));
260 	kmem_free(name, MAXNAMELEN);
261 	kmem_free(fname, MAXNAMELEN);
262 	kmem_free(sname, MAXNAMELEN);
263 	kmem_free(fullname, MAXPATHLEN);
264 	smbsr_error(sr, smberr.status, smberr.errcls, smberr.errcode);
265 	/* NOTREACHED */
266 	return (SDRC_NORMAL_REPLY); /* compiler complains otherwise */
267 }
268 
269 uint32_t
270 smb_delete_check(smb_request_t *sr, smb_node_t *node, smb_error_t *smberr)
271 {
272 	smberr->status = smb_node_delete_check(node);
273 
274 	if (smberr->status == NT_STATUS_SHARING_VIOLATION) {
275 		smberr->errcls = ERRDOS;
276 		smberr->errcode = ERROR_SHARING_VIOLATION;
277 		return (smberr->status);
278 	}
279 
280 	/*
281 	 * This should be done after Share checking due to tests with
282 	 * W2K. I got sharing violation error trying to delete a
283 	 * locked file which is basically the same error if you
284 	 * try to delete a non-locked open file.
285 	 *
286 	 * One thing that I discovered during these tests is that
287 	 * W2K rejects lock requests on open files which are opened
288 	 * with Metadata open modes. The error is STATUS_ACCESS_DENIED.
289 	 */
290 
291 	smberr->status = smb_range_check(sr, sr->user_cr, node, 0,
292 	    UINT64_MAX, B_TRUE);
293 
294 	if (smberr->status != NT_STATUS_SUCCESS) {
295 		smberr->errcls = ERRDOS;
296 		smberr->errcode = ERROR_ACCESS_DENIED;
297 		smberr->status = NT_STATUS_ACCESS_DENIED;
298 	}
299 
300 	return (smberr->status);
301 }
302