xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_delete.c (revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0)
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 2007 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 
32 static DWORD smb_delete_check(struct smb_request *sr, struct smb_node *node,
33     uint16_t dattr, smb_error_t *smberr);
34 static DWORD smb_delete_share_check(struct smb_node *node);
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 	pc = kmem_zalloc(sizeof (*pc), KM_SLEEP);
108 	fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
109 	sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
110 	name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
111 	fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
112 
113 	if (smbsr_decode_vwv(sr, "w", &sattr) != 0) {
114 		kmem_free(pc, sizeof (*pc));
115 		kmem_free(name, MAXNAMELEN);
116 		kmem_free(fname, MAXNAMELEN);
117 		kmem_free(sname, MAXNAMELEN);
118 		kmem_free(fullname, MAXPATHLEN);
119 		smbsr_decode_error(sr);
120 		/* NOTREACHED */
121 	}
122 
123 	if (smbsr_decode_data(sr, "%S", sr, &path) != 0) {
124 		kmem_free(pc, sizeof (*pc));
125 		kmem_free(name, MAXNAMELEN);
126 		kmem_free(fname, MAXNAMELEN);
127 		kmem_free(sname, MAXNAMELEN);
128 		kmem_free(fullname, MAXPATHLEN);
129 		smbsr_decode_error(sr);
130 		/* NOTREACHED */
131 	}
132 
133 	is_stream = smb_stream_parse_name(path, fname, sname);
134 
135 	(void) smb_rdir_open(sr, path, sattr);
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 (smb_delete_check(sr, node, pc->dc_dattr, &smberr)
149 		    != NT_STATUS_SUCCESS) {
150 			smb_node_release(node);
151 			goto delete_error;
152 		}
153 
154 		smb_node_release(node);
155 		node = NULL;
156 
157 		if (is_stream) {
158 			/*
159 			 * It is assumed that fname does not contain
160 			 * any wildcards .
161 			 * smb_fsop_remove() requires filename+streamname
162 			 */
163 			(void) snprintf(fullname, MAXPATHLEN, "%s%s",
164 			    fname, sname);
165 			rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
166 			    fullname, 0);
167 		} else {
168 			/*
169 			 * name (i.e. pc->dc_name) is the on-disk name
170 			 * unless there is a case collision, in which
171 			 * case readdir will have returned a mangled name.
172 			 */
173 			if (smb_maybe_mangled_name(name) == 0)
174 				od = 1;
175 
176 			rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
177 			    name, od);
178 		}
179 
180 		if (rc != 0) {
181 			if (rc != ENOENT) {
182 				smb_rdir_close(sr);
183 				kmem_free(pc, sizeof (*pc));
184 				kmem_free(name, MAXNAMELEN);
185 				kmem_free(fname, MAXNAMELEN);
186 				kmem_free(sname, MAXNAMELEN);
187 				kmem_free(fullname, MAXPATHLEN);
188 				smbsr_raise_errno(sr, rc);
189 				/* NOTREACHED */
190 			}
191 		} else {
192 			deleted++;
193 		}
194 	}
195 
196 	if ((rc != 0) && (rc != ENOENT)) {
197 		/* rc returned by smb_rdir_next() */
198 		smb_rdir_close(sr);
199 		kmem_free(pc, sizeof (*pc));
200 		kmem_free(name, MAXNAMELEN);
201 		kmem_free(fname, MAXNAMELEN);
202 		kmem_free(sname, MAXNAMELEN);
203 		kmem_free(fullname, MAXPATHLEN);
204 		smbsr_raise_errno(sr, rc);
205 		/* NOTREACHED */
206 	}
207 
208 	if (deleted == 0) {
209 		smberr.errcls = ERRDOS;
210 		smberr.errcode = ERROR_FILE_NOT_FOUND;
211 		smberr.status = (sr->sid_odir->d_wildcards == 0)
212 		    ? NT_STATUS_OBJECT_NAME_NOT_FOUND : NT_STATUS_NO_SUCH_FILE;
213 		goto delete_error;
214 	}
215 
216 	smb_rdir_close(sr);
217 
218 	smbsr_encode_empty_result(sr);
219 
220 	kmem_free(pc, sizeof (*pc));
221 	kmem_free(name, MAXNAMELEN);
222 	kmem_free(fname, MAXNAMELEN);
223 	kmem_free(sname, MAXNAMELEN);
224 	kmem_free(fullname, MAXPATHLEN);
225 	return (SDRC_NORMAL_REPLY);
226 
227 delete_error:
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_raise_cifs_error(sr,
235 	    smberr.status, smberr.errcls, smberr.errcode);
236 	/* NOTREACHED */
237 	return (SDRC_NORMAL_REPLY); /* compiler complains otherwise */
238 }
239 
240 static DWORD
241 smb_delete_check(
242     struct smb_request *sr,
243     struct smb_node *node,
244     uint16_t dattr,
245     smb_error_t *smberr)
246 {
247 	if (dattr & SMB_FA_DIRECTORY) {
248 		smberr->errcls = ERRDOS;
249 		smberr->errcode = ERROR_ACCESS_DENIED;
250 		smberr->status = NT_STATUS_FILE_IS_A_DIRECTORY;
251 		return (NT_STATUS_UNSUCCESSFUL);
252 	}
253 
254 	if ((dattr & SMB_FA_READONLY) ||
255 	    (node->flags & NODE_CREATED_READONLY)) {
256 		smberr->errcls = ERRDOS;
257 		smberr->errcode = ERROR_ACCESS_DENIED;
258 		smberr->status = NT_STATUS_CANNOT_DELETE;
259 		return (NT_STATUS_UNSUCCESSFUL);
260 	}
261 
262 	/*
263 	 * NT does not always close a file immediately, which
264 	 * can cause the share and access checking to fail
265 	 * (the node refcnt is greater than one), and the file
266 	 * doesn't get deleted. Breaking the oplock before
267 	 * share and access checking gives the client a chance
268 	 * to close the file.
269 	 */
270 	if (OPLOCKS_IN_FORCE(node)) {
271 		smberr->status = smb_break_oplock(sr, node);
272 
273 		if (smberr->status != NT_STATUS_SUCCESS) {
274 			smberr->errcls = ERRDOS;
275 			smberr->errcode = ERROR_VC_DISCONNECTED;
276 			return (NT_STATUS_UNSUCCESSFUL);
277 		}
278 	}
279 
280 	smberr->status = smb_delete_share_check(node);
281 	if (smberr->status == NT_STATUS_SHARING_VIOLATION) {
282 		smberr->errcls = ERRDOS;
283 		smberr->errcode = ERROR_SHARING_VIOLATION;
284 		return (NT_STATUS_UNSUCCESSFUL);
285 	}
286 
287 	/*
288 	 * This should be done after Share checking due to tests with
289 	 * W2K. I got sharing violation error trying to delete a
290 	 * locked file which is basically the same error if you
291 	 * try to delete a non-locked open file.
292 	 *
293 	 * One thing that I discovered during these tests is that
294 	 * W2K rejects lock requests on open files which are opened
295 	 * with Metadata open modes. The error is STATUS_ACCESS_DENIED.
296 	 */
297 	if (smb_lock_range_access(sr, node, 0, 0, FILE_WRITE_DATA) !=
298 	    NT_STATUS_SUCCESS) {
299 		smberr->errcls = ERRDOS;
300 		smberr->errcode = ERROR_ACCESS_DENIED;
301 		smberr->status = NT_STATUS_ACCESS_DENIED;
302 		return (NT_STATUS_UNSUCCESSFUL);
303 	}
304 
305 
306 	return (NT_STATUS_SUCCESS);
307 }
308 
309 /*
310  * smb_delete_share_check
311  *
312  * An open file can be deleted only if opened for
313  * accessing meta data. Share modes aren't important
314  * in this case.
315  *
316  * NOTE: there is another mechanism for deleting an
317  * open file that NT clients usually use this method.
318  * That's setting "Delete on close" flag for an open
319  * file, in this way the file will be deleted after
320  * last close. This flag can be set by SmbTrans2SetFileInfo
321  * with FILE_DISPOSITION_INFO information level.
322  * For setting this flag file should be opened by
323  * DELETE access in the FID that is passed in the Trans2
324  * request.
325  */
326 static DWORD
327 smb_delete_share_check(struct smb_node *node)
328 {
329 	smb_ofile_t	*file;
330 
331 	if (node == 0 || node->n_refcnt <= 1)
332 		return (NT_STATUS_SUCCESS);
333 
334 	if (node->attr.sa_vattr.va_type == VDIR)
335 		return (NT_STATUS_SUCCESS);
336 
337 	smb_llist_enter(&node->n_ofile_list, RW_READER);
338 	file = smb_llist_head(&node->n_ofile_list);
339 	while (file) {
340 		ASSERT(file->f_magic == SMB_OFILE_MAGIC);
341 		if (file->f_granted_access &
342 		    (FILE_READ_DATA |
343 		    FILE_WRITE_DATA |
344 		    FILE_APPEND_DATA |
345 		    FILE_EXECUTE |
346 		    DELETE)) {
347 			smb_llist_exit(&node->n_ofile_list);
348 			return (NT_STATUS_SHARING_VIOLATION);
349 		}
350 		file = smb_llist_next(&node->n_ofile_list, file);
351 	}
352 	smb_llist_exit(&node->n_ofile_list);
353 	return (NT_STATUS_SUCCESS);
354 }
355