smb_delete.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
7a14e8f66c0e932fe2954d792614a3b61d444bd1Jakub Hrozek/*
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * CDDL HEADER START
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek *
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * The contents of this file are subject to the terms of the
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * Common Development and Distribution License (the "License").
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * You may not use this file except in compliance with the License.
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek *
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * or http://www.opensolaris.org/os/licensing.
03532fb1cbb7e8c1d5cf2e93aa3719f926631cabStephen Gallagher * See the License for the specific language governing permissions
8a2a49333b7df3a4b86db42cd20ec8286d2788d3Pavel Březina * and limitations under the License.
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek *
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * When distributing Covered Code, include this CDDL HEADER in each
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * If applicable, add the following below this CDDL HEADER, with the
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * fields enclosed by brackets "[]" replaced with your own identifying
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * information: Portions Copyright [yyyy] [name of copyright owner]
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek *
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek * CDDL HEADER END
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek */
46e44f86067b7f77c3f71cb7ac57456434e354dcStephen Gallagher/*
46e44f86067b7f77c3f71cb7ac57456434e354dcStephen Gallagher * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
6ea6ec5cb7d9985e2730fb9d4657624d10aed4d8Nick Guay * Use is subject to license terms.
46e44f86067b7f77c3f71cb7ac57456434e354dcStephen Gallagher */
46e44f86067b7f77c3f71cb7ac57456434e354dcStephen Gallagher
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek#pragma ident "%Z%%M% %I% %E% SMI"
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek#include <smbsrv/smb_incl.h>
a23919ed39d212f9f5694d9b103c84641fdb7680Stephen Gallagher#include <smbsrv/smb_fsops.h>
6f51c802311fd81a409a26763ed45b28a3234d0dJakub Hrozek#include <smbsrv/smbinfo.h>
813086b16adabc6c1a0f37d7518c6a709ae1c57aJakub Hrozek
46e44f86067b7f77c3f71cb7ac57456434e354dcStephen Gallagherstatic DWORD smb_delete_check(struct smb_request *sr, struct smb_node *node,
46e44f86067b7f77c3f71cb7ac57456434e354dcStephen Gallagher uint16_t dattr, smb_error_t *smberr);
9b3f37cb0c70c7b18c49b657e3799094a8711cadJakub Hrozekstatic DWORD smb_delete_share_check(struct smb_node *node);
544525ee1fc54d744c08465066e2b4a521f78224Stephen Gallagher
5ef295d1cf410ceaa92c03a7843df8a36409f465Stephen Gallagher/*
5ef295d1cf410ceaa92c03a7843df8a36409f465Stephen Gallagher * smb_com_delete
*
* The delete file message is sent to delete a data file. The appropriate
* Tid and additional pathname are passed. Read only files may not be
* deleted, the read-only attribute must be reset prior to file deletion.
*
* NT supports a hidden permission known as File Delete Child (FDC). If
* the user has FullControl access to a directory, the user is permitted
* to delete any object in the directory regardless of the permissions
* on the object.
*
* Client Request Description
* ================================== =================================
* UCHAR WordCount; Count of parameter words = 1
* USHORT SearchAttributes;
* USHORT ByteCount; Count of data bytes; min = 2
* UCHAR BufferFormat; 0x04
* STRING FileName[]; File name
*
* Multiple files may be deleted in response to a single request as
* SMB_COM_DELETE supports wildcards
*
* SearchAttributes indicates the attributes that the target file(s) must
* have. If the attribute is zero then only normal files are deleted. If
* the system file or hidden attributes are specified then the delete is
* inclusive -both the specified type(s) of files and normal files are
* deleted. Attributes are described in the "Attribute Encoding" section
* of this document.
*
* If bit0 of the Flags2 field of the SMB header is set, a pattern is
* passed in, and the file has a long name, then the passed pattern much
* match the long file name for the delete to succeed. If bit0 is clear, a
* pattern is passed in, and the file has a long name, then the passed
* pattern must match the file's short name for the deletion to succeed.
*
* Server Response Description
* ================================== =================================
* UCHAR WordCount; Count of parameter words = 0
* USHORT ByteCount; Count of data bytes = 0
*
* 4.2.10.1 Errors
*
* ERRDOS/ERRbadpath
* ERRDOS/ERRbadfile
* ERRDOS/ERRnoaccess
* ERRDOS/ERRbadshare # returned by NT for files that are already open
* ERRHRD/ERRnowrite
* ERRSRV/ERRaccess
* ERRSRV/ERRinvdevice
* ERRSRV/ERRinvid
* ERRSRV/ERRbaduid
*/
int
smb_com_delete(struct smb_request *sr)
{
int rc;
int od = 0;
int deleted = 0;
unsigned short sattr;
char *path;
struct smb_node *dir_snode;
struct smb_node *node = 0;
char *name;
char *fname;
char *sname;
char *fullname;
smb_error_t smberr;
int is_stream;
smb_odir_context_t *pc;
pc = kmem_zalloc(sizeof (*pc), KM_SLEEP);
fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
fullname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
if (smbsr_decode_vwv(sr, "w", &sattr) != 0) {
kmem_free(pc, sizeof (*pc));
kmem_free(name, MAXNAMELEN);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
kmem_free(fullname, MAXPATHLEN);
smbsr_decode_error(sr);
/* NOTREACHED */
}
if (smbsr_decode_data(sr, "%S", sr, &path) != 0) {
kmem_free(pc, sizeof (*pc));
kmem_free(name, MAXNAMELEN);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
kmem_free(fullname, MAXPATHLEN);
smbsr_decode_error(sr);
/* NOTREACHED */
}
is_stream = smb_stream_parse_name(path, fname, sname);
(void) smb_rdir_open(sr, path, sattr);
dir_snode = sr->sid_odir->d_dir_snode;
/*
* This while loop is meant to deal with wildcards.
* It is not expected that wildcards will exist for
* streams. For the streams case, it is expected
* that the below loop will be executed only once.
*/
while ((rc = smb_rdir_next(sr, &node, pc)) == 0) {
(void) strlcpy(name, pc->dc_name, MAXNAMELEN);
if (smb_delete_check(sr, node, pc->dc_dattr, &smberr)
!= NT_STATUS_SUCCESS) {
smb_node_release(node);
goto delete_error;
}
smb_node_release(node);
node = NULL;
if (is_stream) {
/*
* It is assumed that fname does not contain
* any wildcards .
* smb_fsop_remove() requires filename+streamname
*/
(void) snprintf(fullname, MAXPATHLEN, "%s%s",
fname, sname);
rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
fullname, 0);
} else {
/*
* name (i.e. pc->dc_name) is the on-disk name
* unless there is a case collision, in which
* case readdir will have returned a mangled name.
*/
if (smb_maybe_mangled_name(name) == 0)
od = 1;
rc = smb_fsop_remove(sr, sr->user_cr, dir_snode,
name, od);
}
if (rc != 0) {
if (rc != ENOENT) {
smb_rdir_close(sr);
kmem_free(pc, sizeof (*pc));
kmem_free(name, MAXNAMELEN);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
kmem_free(fullname, MAXPATHLEN);
smbsr_raise_errno(sr, rc);
/* NOTREACHED */
}
} else {
deleted++;
}
}
if ((rc != 0) && (rc != ENOENT)) {
/* rc returned by smb_rdir_next() */
smb_rdir_close(sr);
kmem_free(pc, sizeof (*pc));
kmem_free(name, MAXNAMELEN);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
kmem_free(fullname, MAXPATHLEN);
smbsr_raise_errno(sr, rc);
/* NOTREACHED */
}
if (deleted == 0) {
smberr.errcls = ERRDOS;
smberr.errcode = ERROR_FILE_NOT_FOUND;
smberr.status = (sr->sid_odir->d_wildcards == 0)
? NT_STATUS_OBJECT_NAME_NOT_FOUND : NT_STATUS_NO_SUCH_FILE;
goto delete_error;
}
smb_rdir_close(sr);
smbsr_encode_empty_result(sr);
kmem_free(pc, sizeof (*pc));
kmem_free(name, MAXNAMELEN);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
kmem_free(fullname, MAXPATHLEN);
return (SDRC_NORMAL_REPLY);
delete_error:
smb_rdir_close(sr);
kmem_free(pc, sizeof (*pc));
kmem_free(name, MAXNAMELEN);
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
kmem_free(fullname, MAXPATHLEN);
smbsr_raise_cifs_error(sr,
smberr.status, smberr.errcls, smberr.errcode);
/* NOTREACHED */
return (SDRC_NORMAL_REPLY); /* compiler complains otherwise */
}
static DWORD
smb_delete_check(
struct smb_request *sr,
struct smb_node *node,
uint16_t dattr,
smb_error_t *smberr)
{
if (dattr & SMB_FA_DIRECTORY) {
smberr->errcls = ERRDOS;
smberr->errcode = ERROR_ACCESS_DENIED;
smberr->status = NT_STATUS_FILE_IS_A_DIRECTORY;
return (NT_STATUS_UNSUCCESSFUL);
}
if ((dattr & SMB_FA_READONLY) ||
(node->flags & NODE_CREATED_READONLY)) {
smberr->errcls = ERRDOS;
smberr->errcode = ERROR_ACCESS_DENIED;
smberr->status = NT_STATUS_CANNOT_DELETE;
return (NT_STATUS_UNSUCCESSFUL);
}
/*
* NT does not always close a file immediately, which
* can cause the share and access checking to fail
* (the node refcnt is greater than one), and the file
* doesn't get deleted. Breaking the oplock before
* share and access checking gives the client a chance
* to close the file.
*/
if (OPLOCKS_IN_FORCE(node)) {
smberr->status = smb_break_oplock(sr, node);
if (smberr->status != NT_STATUS_SUCCESS) {
smberr->errcls = ERRDOS;
smberr->errcode = ERROR_VC_DISCONNECTED;
return (NT_STATUS_UNSUCCESSFUL);
}
}
smberr->status = smb_delete_share_check(node);
if (smberr->status == NT_STATUS_SHARING_VIOLATION) {
smberr->errcls = ERRDOS;
smberr->errcode = ERROR_SHARING_VIOLATION;
return (NT_STATUS_UNSUCCESSFUL);
}
/*
* This should be done after Share checking due to tests with
* W2K. I got sharing violation error trying to delete a
* locked file which is basically the same error if you
* try to delete a non-locked open file.
*
* One thing that I discovered during these tests is that
* W2K rejects lock requests on open files which are opened
* with Metadata open modes. The error is STATUS_ACCESS_DENIED.
*/
if (smb_lock_range_access(sr, node, 0, 0, FILE_WRITE_DATA) !=
NT_STATUS_SUCCESS) {
smberr->errcls = ERRDOS;
smberr->errcode = ERROR_ACCESS_DENIED;
smberr->status = NT_STATUS_ACCESS_DENIED;
return (NT_STATUS_UNSUCCESSFUL);
}
return (NT_STATUS_SUCCESS);
}
/*
* smb_delete_share_check
*
* An open file can be deleted only if opened for
* accessing meta data. Share modes aren't important
* in this case.
*
* NOTE: there is another mechanism for deleting an
* open file that NT clients usually use this method.
* That's setting "Delete on close" flag for an open
* file, in this way the file will be deleted after
* last close. This flag can be set by SmbTrans2SetFileInfo
* with FILE_DISPOSITION_INFO information level.
* For setting this flag file should be opened by
* DELETE access in the FID that is passed in the Trans2
* request.
*/
static DWORD
smb_delete_share_check(struct smb_node *node)
{
smb_ofile_t *file;
if (node == 0 || node->n_refcnt <= 1)
return (NT_STATUS_SUCCESS);
if (node->attr.sa_vattr.va_type == VDIR)
return (NT_STATUS_SUCCESS);
smb_llist_enter(&node->n_ofile_list, RW_READER);
file = smb_llist_head(&node->n_ofile_list);
while (file) {
ASSERT(file->f_magic == SMB_OFILE_MAGIC);
if (file->f_granted_access &
(FILE_READ_DATA |
FILE_WRITE_DATA |
FILE_APPEND_DATA |
FILE_EXECUTE |
DELETE)) {
smb_llist_exit(&node->n_ofile_list);
return (NT_STATUS_SHARING_VIOLATION);
}
file = smb_llist_next(&node->n_ofile_list, file);
}
smb_llist_exit(&node->n_ofile_list);
return (NT_STATUS_SUCCESS);
}