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 /*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
28 #include <smbsrv/smbinfo.h>
29 #include <sys/nbmlock.h>
30
31 static int smb_delete_check_path(smb_request_t *);
32 static int smb_delete_single_file(smb_request_t *, smb_error_t *);
33 static int smb_delete_multiple_files(smb_request_t *, smb_error_t *);
34 static int smb_delete_find_fname(smb_request_t *, smb_odir_t *, char *, int);
35 static int smb_delete_check_dosattr(smb_request_t *, smb_error_t *);
36 static int smb_delete_remove_file(smb_request_t *, smb_error_t *);
37
38 static void smb_delete_error(smb_error_t *, uint32_t, uint16_t, uint16_t);
39
40 /*
41 * smb_com_delete
42 *
43 * The delete file message is sent to delete a data file. The appropriate
44 * Tid and additional pathname are passed. Read only files may not be
45 * deleted, the read-only attribute must be reset prior to file deletion.
46 *
47 * NT supports a hidden permission known as File Delete Child (FDC). If
48 * the user has FullControl access to a directory, the user is permitted
49 * to delete any object in the directory regardless of the permissions
50 * on the object.
51 *
52 * Client Request Description
53 * ================================== =================================
54 * UCHAR WordCount; Count of parameter words = 1
55 * USHORT SearchAttributes;
56 * USHORT ByteCount; Count of data bytes; min = 2
57 * UCHAR BufferFormat; 0x04
58 * STRING FileName[]; File name
59 *
60 * Multiple files may be deleted in response to a single request as
61 * SMB_COM_DELETE supports wildcards
62 *
63 * SearchAttributes indicates the attributes that the target file(s) must
64 * have. If the attribute is zero then only normal files are deleted. If
65 * the system file or hidden attributes are specified then the delete is
66 * inclusive -both the specified type(s) of files and normal files are
67 * deleted. Attributes are described in the "Attribute Encoding" section
68 * of this document.
69 *
70 * If bit0 of the Flags2 field of the SMB header is set, a pattern is
71 * passed in, and the file has a long name, then the passed pattern much
72 * match the long file name for the delete to succeed. If bit0 is clear, a
73 * pattern is passed in, and the file has a long name, then the passed
74 * pattern must match the file's short name for the deletion to succeed.
75 *
76 * Server Response Description
77 * ================================== =================================
78 * UCHAR WordCount; Count of parameter words = 0
79 * USHORT ByteCount; Count of data bytes = 0
80 *
81 * 4.2.10.1 Errors
82 *
83 * ERRDOS/ERRbadpath
84 * ERRDOS/ERRbadfile
85 * ERRDOS/ERRnoaccess
86 * ERRDOS/ERRbadshare # returned by NT for files that are already open
87 * ERRHRD/ERRnowrite
88 * ERRSRV/ERRaccess
89 * ERRSRV/ERRinvdevice
90 * ERRSRV/ERRinvid
91 * ERRSRV/ERRbaduid
92 */
93 smb_sdrc_t
smb_pre_delete(smb_request_t * sr)94 smb_pre_delete(smb_request_t *sr)
95 {
96 int rc;
97 smb_fqi_t *fqi;
98
99 fqi = &sr->arg.dirop.fqi;
100
101 if ((rc = smbsr_decode_vwv(sr, "w", &fqi->fq_sattr)) == 0)
102 rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path);
103
104 DTRACE_SMB_2(op__Delete__start, smb_request_t *, sr, smb_fqi_t *, fqi);
105
106 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
107 }
108
109 void
smb_post_delete(smb_request_t * sr)110 smb_post_delete(smb_request_t *sr)
111 {
112 DTRACE_SMB_1(op__Delete__done, smb_request_t *, sr);
113 }
114
115 /*
116 * smb_com_delete
117 *
118 * 1. intialize, pre-process and validate pathname
119 *
120 * 2. process the path to get directory node & last_comp,
121 * store these in fqi
122 * - If smb_pathname_reduce cannot find the specified path,
123 * the error (ENOTDIR) is translated to NT_STATUS_OBJECT_PATH_NOT_FOUND
124 * if the target is a single file (no wildcards). If there are
125 * wildcards in the last_comp, NT_STATUS_OBJECT_NAME_NOT_FOUND is
126 * used instead.
127 * - If the directory node is the mount point and the last component
128 * is ".." NT_STATUS_OBJECT_PATH_SYNTAX_BAD is returned.
129 *
130 * 3. check access permissions
131 *
132 * 4. invoke the appropriate deletion routine to find and remove
133 * the specified file(s).
134 * - if target is a single file (no wildcards) - smb_delete_single_file
135 * - if the target contains wildcards - smb_delete_multiple_files
136 *
137 * Returns: SDRC_SUCCESS or SDRC_ERROR
138 */
139 smb_sdrc_t
smb_com_delete(smb_request_t * sr)140 smb_com_delete(smb_request_t *sr)
141 {
142 int rc;
143 smb_error_t err;
144 uint32_t status;
145 boolean_t wildcards = B_FALSE;
146 smb_fqi_t *fqi;
147 smb_pathname_t *pn;
148
149 fqi = &sr->arg.dirop.fqi;
150 pn = &fqi->fq_path;
151
152 smb_pathname_init(sr, pn, pn->pn_path);
153 if (!smb_pathname_validate(sr, pn))
154 return (SDRC_ERROR);
155 if (smb_delete_check_path(sr) != 0)
156 return (SDRC_ERROR);
157
158 wildcards = smb_contains_wildcards(pn->pn_fname);
159
160 rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path,
161 sr->tid_tree->t_snode, sr->tid_tree->t_snode,
162 &fqi->fq_dnode, fqi->fq_last_comp);
163 if (rc == 0) {
164 if (!smb_node_is_dir(fqi->fq_dnode)) {
165 smb_node_release(fqi->fq_dnode);
166 rc = ENOTDIR;
167 }
168 }
169 if (rc != 0) {
170 if (rc == ENOTDIR) {
171 if (wildcards)
172 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
173 else
174 status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
175 smbsr_error(sr, status, ERRDOS, ERROR_FILE_NOT_FOUND);
176 } else {
177 smbsr_errno(sr, rc);
178 }
179
180 return (SDRC_ERROR);
181 }
182
183 if ((fqi->fq_dnode == sr->tid_tree->t_snode) &&
184 (strcmp(fqi->fq_last_comp, "..") == 0)) {
185 smb_node_release(fqi->fq_dnode);
186 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
187 ERRDOS, ERROR_BAD_PATHNAME);
188 return (SDRC_ERROR);
189 }
190
191 rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode,
192 FILE_LIST_DIRECTORY);
193 if (rc != 0) {
194 smb_node_release(fqi->fq_dnode);
195 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
196 ERRDOS, ERROR_ACCESS_DENIED);
197 return (SDRC_ERROR);
198 }
199
200 if (wildcards)
201 rc = smb_delete_multiple_files(sr, &err);
202 else
203 rc = smb_delete_single_file(sr, &err);
204
205 smb_node_release(fqi->fq_dnode);
206
207 if (rc != 0)
208 smbsr_set_error(sr, &err);
209 else
210 rc = smbsr_encode_empty_result(sr);
211
212 return (rc == 0 ? SDRC_SUCCESS : SDRC_ERROR);
213 }
214
215 /*
216 * smb_delete_single_file
217 *
218 * Find the specified file and, if its attributes match the search
219 * criteria, delete it.
220 *
221 * Returns 0 - success (file deleted)
222 * -1 - error, err is populated with error details
223 */
224 static int
smb_delete_single_file(smb_request_t * sr,smb_error_t * err)225 smb_delete_single_file(smb_request_t *sr, smb_error_t *err)
226 {
227 smb_fqi_t *fqi;
228 smb_pathname_t *pn;
229
230 fqi = &sr->arg.dirop.fqi;
231 pn = &fqi->fq_path;
232
233 /* pn already initialized and validated */
234 if (!smb_validate_object_name(sr, pn)) {
235 smb_delete_error(err, sr->smb_error.status,
236 ERRDOS, ERROR_INVALID_NAME);
237 return (-1);
238 }
239
240 if (smb_fsop_lookup_name(sr, sr->user_cr, 0, sr->tid_tree->t_snode,
241 fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode) != 0) {
242 smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND,
243 ERRDOS, ERROR_FILE_NOT_FOUND);
244 return (-1);
245 }
246
247 if (smb_delete_check_dosattr(sr, err) != 0) {
248 smb_node_release(fqi->fq_fnode);
249 return (-1);
250 }
251
252 if (smb_delete_remove_file(sr, err) != 0) {
253 smb_node_release(fqi->fq_fnode);
254 return (-1);
255 }
256
257 smb_node_release(fqi->fq_fnode);
258 return (0);
259 }
260
261 /*
262 * smb_delete_multiple_files
263 *
264 * For each matching file found by smb_delete_find_fname:
265 * 1. lookup file
266 * 2. check the file's attributes
267 * - The search ends with an error if a readonly file
268 * (NT_STATUS_CANNOT_DELETE) is matched.
269 * - The search ends (but not an error) if a directory is
270 * matched and the request's search did not include
271 * directories.
272 * - Otherwise, if smb_delete_check_dosattr fails the file
273 * is skipped and the search continues (at step 1)
274 * 3. delete the file
275 *
276 * Returns 0 - success
277 * -1 - error, err is populated with error details
278 */
279 static int
smb_delete_multiple_files(smb_request_t * sr,smb_error_t * err)280 smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err)
281 {
282 int rc, deleted = 0;
283 smb_fqi_t *fqi;
284 uint16_t odid;
285 smb_odir_t *od;
286 char namebuf[MAXNAMELEN];
287
288 fqi = &sr->arg.dirop.fqi;
289
290 /*
291 * Specify all search attributes (SMB_SEARCH_ATTRIBUTES) so that
292 * delete-specific checking can be done (smb_delete_check_dosattr).
293 */
294 odid = smb_odir_open(sr, fqi->fq_path.pn_path,
295 SMB_SEARCH_ATTRIBUTES, 0);
296 if (odid == 0)
297 return (-1);
298
299 if ((od = smb_tree_lookup_odir(sr->tid_tree, odid)) == NULL)
300 return (-1);
301
302 for (;;) {
303 rc = smb_delete_find_fname(sr, od, namebuf, MAXNAMELEN);
304 if (rc != 0)
305 break;
306
307 rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_CASE_SENSITIVE,
308 sr->tid_tree->t_snode, fqi->fq_dnode,
309 namebuf, &fqi->fq_fnode);
310 if (rc != 0)
311 break;
312
313 if (smb_delete_check_dosattr(sr, err) != 0) {
314 smb_node_release(fqi->fq_fnode);
315 if (err->status == NT_STATUS_CANNOT_DELETE) {
316 smb_odir_close(od);
317 smb_odir_release(od);
318 return (-1);
319 }
320 if ((err->status == NT_STATUS_FILE_IS_A_DIRECTORY) &&
321 (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) != 0))
322 break;
323 continue;
324 }
325
326 if (smb_delete_remove_file(sr, err) == 0) {
327 ++deleted;
328 smb_node_release(fqi->fq_fnode);
329 continue;
330 }
331 if (err->status == NT_STATUS_OBJECT_NAME_NOT_FOUND) {
332 smb_node_release(fqi->fq_fnode);
333 continue;
334 }
335
336 smb_odir_close(od);
337 smb_odir_release(od);
338 smb_node_release(fqi->fq_fnode);
339 return (-1);
340 }
341
342 smb_odir_close(od);
343 smb_odir_release(od);
344
345 if ((rc != 0) && (rc != ENOENT)) {
346 smbsr_map_errno(rc, err);
347 return (-1);
348 }
349
350 if (deleted == 0) {
351 smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
352 ERRDOS, ERROR_FILE_NOT_FOUND);
353 return (-1);
354 }
355
356 return (0);
357 }
358
359 /*
360 * smb_delete_find_fname
361 *
362 * Find next filename that matches search pattern and return it
363 * in namebuf.
364 *
365 * Returns: 0 - success
366 * errno
367 */
368 static int
smb_delete_find_fname(smb_request_t * sr,smb_odir_t * od,char * namebuf,int len)369 smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od, char *namebuf, int len)
370 {
371 int rc;
372 smb_odirent_t *odirent;
373 boolean_t eos;
374
375 odirent = kmem_alloc(sizeof (smb_odirent_t), KM_SLEEP);
376
377 rc = smb_odir_read(sr, od, odirent, &eos);
378 if (rc == 0) {
379 if (eos)
380 rc = ENOENT;
381 else
382 (void) strlcpy(namebuf, odirent->od_name, len);
383 }
384 kmem_free(odirent, sizeof (smb_odirent_t));
385 return (rc);
386 }
387
388 /*
389 * smb_delete_check_dosattr
390 *
391 * Check file's dos atributes to ensure that
392 * 1. the file is not a directory - NT_STATUS_FILE_IS_A_DIRECTORY
393 * 2. the file is not readonly - NT_STATUS_CANNOT_DELETE
394 * 3. the file's dos attributes comply with the specified search attributes
395 * If the file is either hidden or system and those attributes
396 * are not specified in the search attributes - NT_STATUS_NO_SUCH_FILE
397 *
398 * Returns: 0 - file's attributes pass all checks
399 * -1 - err populated with error details
400 */
401 static int
smb_delete_check_dosattr(smb_request_t * sr,smb_error_t * err)402 smb_delete_check_dosattr(smb_request_t *sr, smb_error_t *err)
403 {
404 smb_fqi_t *fqi;
405 smb_node_t *node;
406 smb_attr_t attr;
407 uint16_t sattr;
408
409 fqi = &sr->arg.dirop.fqi;
410 sattr = fqi->fq_sattr;
411 node = fqi->fq_fnode;
412
413 if (smb_node_getattr(sr, node, &attr) != 0) {
414 smb_delete_error(err, NT_STATUS_INTERNAL_ERROR,
415 ERRDOS, ERROR_INTERNAL_ERROR);
416 return (-1);
417 }
418
419 if (attr.sa_dosattr & FILE_ATTRIBUTE_DIRECTORY) {
420 smb_delete_error(err, NT_STATUS_FILE_IS_A_DIRECTORY,
421 ERRDOS, ERROR_ACCESS_DENIED);
422 return (-1);
423 }
424
425 if (SMB_PATHFILE_IS_READONLY(sr, node)) {
426 smb_delete_error(err, NT_STATUS_CANNOT_DELETE,
427 ERRDOS, ERROR_ACCESS_DENIED);
428 return (-1);
429 }
430
431 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
432 !(SMB_SEARCH_HIDDEN(sattr))) {
433 smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
434 ERRDOS, ERROR_FILE_NOT_FOUND);
435 return (-1);
436 }
437
438 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
439 !(SMB_SEARCH_SYSTEM(sattr))) {
440 smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
441 ERRDOS, ERROR_FILE_NOT_FOUND);
442 return (-1);
443 }
444
445 return (0);
446 }
447
448 /*
449 * smb_delete_remove_file
450 *
451 * For consistency with Windows 2000, the range check should be done
452 * after checking for sharing violations. Attempting to delete a
453 * locked file will result in sharing violation, which is the same
454 * thing that will happen if you try to delete a non-locked open file.
455 *
456 * Note that windows 2000 rejects lock requests on open files that
457 * have been opened with metadata open modes. The error is
458 * STATUS_ACCESS_DENIED.
459 *
460 * NT does not always close a file immediately, which can cause the
461 * share and access checking to fail (the node refcnt is greater
462 * than one), and the file doesn't get deleted. Breaking the oplock
463 * before share and access checking gives the client a chance to
464 * close the file.
465 *
466 * Returns: 0 - success
467 * -1 - error, err populated with error details
468 */
469 static int
smb_delete_remove_file(smb_request_t * sr,smb_error_t * err)470 smb_delete_remove_file(smb_request_t *sr, smb_error_t *err)
471 {
472 int rc;
473 uint32_t status;
474 smb_fqi_t *fqi;
475 smb_node_t *node;
476 uint32_t flags = 0;
477
478 fqi = &sr->arg.dirop.fqi;
479 node = fqi->fq_fnode;
480
481 (void) smb_oplock_break(sr, node,
482 SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH);
483
484 smb_node_start_crit(node, RW_READER);
485
486 status = smb_node_delete_check(node);
487 if (status != NT_STATUS_SUCCESS) {
488 smb_delete_error(err, NT_STATUS_SHARING_VIOLATION,
489 ERRDOS, ERROR_SHARING_VIOLATION);
490 smb_node_end_crit(node);
491 return (-1);
492 }
493
494 status = smb_range_check(sr, node, 0, UINT64_MAX, B_TRUE);
495 if (status != NT_STATUS_SUCCESS) {
496 smb_delete_error(err, NT_STATUS_ACCESS_DENIED,
497 ERRDOS, ERROR_ACCESS_DENIED);
498 smb_node_end_crit(node);
499 return (-1);
500 }
501
502 if (SMB_TREE_SUPPORTS_CATIA(sr))
503 flags |= SMB_CATIA;
504
505 rc = smb_fsop_remove(sr, sr->user_cr, node->n_dnode,
506 node->od_name, flags);
507 if (rc != 0) {
508 if (rc == ENOENT)
509 smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND,
510 ERRDOS, ERROR_FILE_NOT_FOUND);
511 else
512 smbsr_map_errno(rc, err);
513
514 smb_node_end_crit(node);
515 return (-1);
516 }
517
518 smb_node_end_crit(node);
519 return (0);
520 }
521
522
523 /*
524 * smb_delete_check_path
525 *
526 * smb_pathname_validate() should already have been used to
527 * perform initial validation on the pathname. Additional
528 * request specific validation of the filename is performed
529 * here.
530 *
531 * - pn->pn_fname is NULL should result in NT_STATUS_FILE_IS_A_DIRECTORY
532 *
533 * - Any wildcard filename that resolves to '.' should result in
534 * NT_STATUS_OBJECT_NAME_INVALID if the search attributes include
535 * FILE_ATTRIBUTE_DIRECTORY
536 *
537 * Returns:
538 * 0: path is valid.
539 * -1: path is invalid. Sets error information in sr.
540 */
541 static int
smb_delete_check_path(smb_request_t * sr)542 smb_delete_check_path(smb_request_t *sr)
543 {
544 smb_fqi_t *fqi = &sr->arg.dirop.fqi;
545 smb_pathname_t *pn = &fqi->fq_path;
546
547 if (pn->pn_fname == NULL) {
548 smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY,
549 ERRDOS, ERROR_ACCESS_DENIED);
550 return (-1);
551 }
552
553 /* fname component is, or resolves to, '.' (dot) */
554 if ((strcmp(pn->pn_fname, ".") == 0) ||
555 (SMB_SEARCH_DIRECTORY(fqi->fq_sattr) &&
556 (smb_match(pn->pn_fname, ".")))) {
557 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
558 ERRDOS, ERROR_INVALID_NAME);
559 return (-1);
560 }
561
562 return (0);
563 }
564
565 /*
566 * smb_delete_error
567 */
568 static void
smb_delete_error(smb_error_t * err,uint32_t status,uint16_t errcls,uint16_t errcode)569 smb_delete_error(smb_error_t *err,
570 uint32_t status, uint16_t errcls, uint16_t errcode)
571 {
572 err->status = status;
573 err->errcls = errcls;
574 err->errcode = errcode;
575 }
576