#include <sys/sunddi.h>
#include <sys/nbmlock.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smbinfo.h>
static int smb_delete_check_path(smb_request_t *);
static int smb_delete_single_file(smb_request_t *, smb_error_t *);
static int smb_delete_multiple_files(smb_request_t *, smb_error_t *);
static int smb_delete_find_fname(smb_request_t *, smb_odir_t *, char *, int);
static int smb_delete_check_dosattr(smb_request_t *, smb_error_t *);
static int smb_delete_remove_file(smb_request_t *, smb_error_t *);
static void smb_delete_error(smb_error_t *, uint32_t, uint16_t, uint16_t);
smb_sdrc_t
smb_pre_delete(smb_request_t *sr)
{
int rc;
smb_fqi_t *fqi;
fqi = &sr->arg.dirop.fqi;
if ((rc = smbsr_decode_vwv(sr, "w", &fqi->fq_sattr)) == 0)
rc = smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path);
DTRACE_SMB_START(op__Delete, smb_request_t *, sr);
return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
}
void
smb_post_delete(smb_request_t *sr)
{
DTRACE_SMB_DONE(op__Delete, smb_request_t *, sr);
}
smb_sdrc_t
smb_com_delete(smb_request_t *sr)
{
int rc;
smb_error_t err;
uint32_t status;
boolean_t wildcards = B_FALSE;
smb_fqi_t *fqi;
smb_pathname_t *pn;
fqi = &sr->arg.dirop.fqi;
pn = &fqi->fq_path;
smb_pathname_init(sr, pn, pn->pn_path);
if (!smb_pathname_validate(sr, pn))
return (SDRC_ERROR);
if (smb_delete_check_path(sr) != 0)
return (SDRC_ERROR);
wildcards = smb_contains_wildcards(pn->pn_fname);
rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path,
sr->tid_tree->t_snode, sr->tid_tree->t_snode,
&fqi->fq_dnode, fqi->fq_last_comp);
if (rc == 0) {
if (!smb_node_is_dir(fqi->fq_dnode)) {
smb_node_release(fqi->fq_dnode);
rc = ENOTDIR;
}
}
if (rc != 0) {
if (rc == ENOTDIR) {
if (wildcards)
status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
else
status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
smbsr_error(sr, status, ERRDOS, ERROR_FILE_NOT_FOUND);
} else {
smbsr_errno(sr, rc);
}
return (SDRC_ERROR);
}
if ((fqi->fq_dnode == sr->tid_tree->t_snode) &&
(strcmp(fqi->fq_last_comp, "..") == 0)) {
smb_node_release(fqi->fq_dnode);
smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
ERRDOS, ERROR_BAD_PATHNAME);
return (SDRC_ERROR);
}
rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode,
FILE_LIST_DIRECTORY);
if (rc != 0) {
smb_node_release(fqi->fq_dnode);
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (SDRC_ERROR);
}
if (wildcards)
rc = smb_delete_multiple_files(sr, &err);
else
rc = smb_delete_single_file(sr, &err);
smb_node_release(fqi->fq_dnode);
if (rc != 0)
smbsr_set_error(sr, &err);
else
rc = smbsr_encode_empty_result(sr);
return (rc == 0 ? SDRC_SUCCESS : SDRC_ERROR);
}
static int
smb_delete_single_file(smb_request_t *sr, smb_error_t *err)
{
smb_fqi_t *fqi;
smb_pathname_t *pn;
fqi = &sr->arg.dirop.fqi;
pn = &fqi->fq_path;
if (!smb_validate_object_name(sr, pn)) {
smb_delete_error(err, sr->smb_error.status,
ERRDOS, ERROR_INVALID_NAME);
return (-1);
}
if (smb_fsop_lookup_name(sr, sr->user_cr, 0, sr->tid_tree->t_snode,
fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode) != 0) {
smb_delete_error(err, NT_STATUS_OBJECT_NAME_NOT_FOUND,
ERRDOS, ERROR_FILE_NOT_FOUND);
return (-1);
}
if (smb_delete_check_dosattr(sr, err) != 0) {
smb_node_release(fqi->fq_fnode);
return (-1);
}
if (smb_delete_remove_file(sr, err) != 0) {
smb_node_release(fqi->fq_fnode);
return (-1);
}
smb_node_release(fqi->fq_fnode);
return (0);
}
static int
smb_delete_multiple_files(smb_request_t *sr, smb_error_t *err)
{
char namebuf[MAXNAMELEN];
smb_fqi_t *fqi;
smb_odir_t *od;
uint32_t status;
int rc, deleted = 0;
fqi = &sr->arg.dirop.fqi;
status = smb_odir_openpath(sr, fqi->fq_path.pn_path,
SMB_SEARCH_ATTRIBUTES, 0, &od);
if (status != 0) {
err->status = status;
return (-1);
}
for (;;) {
rc = smb_delete_find_fname(sr, od, namebuf, MAXNAMELEN);
if (rc != 0)
break;
rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_CASE_SENSITIVE,
sr->tid_tree->t_snode, fqi->fq_dnode,
namebuf, &fqi->fq_fnode);
if (rc != 0)
break;
if (smb_delete_check_dosattr(sr, err) != 0) {
smb_node_release(fqi->fq_fnode);
if (err->status == NT_STATUS_CANNOT_DELETE) {
smb_odir_close(od);
smb_odir_release(od);
return (-1);
}
if ((err->status == NT_STATUS_FILE_IS_A_DIRECTORY) &&
(SMB_SEARCH_DIRECTORY(fqi->fq_sattr) != 0))
break;
continue;
}
if (smb_delete_remove_file(sr, err) == 0) {
++deleted;
smb_node_release(fqi->fq_fnode);
continue;
}
if (err->status == NT_STATUS_OBJECT_NAME_NOT_FOUND) {
smb_node_release(fqi->fq_fnode);
continue;
}
smb_odir_close(od);
smb_odir_release(od);
smb_node_release(fqi->fq_fnode);
return (-1);
}
smb_odir_close(od);
smb_odir_release(od);
if ((rc != 0) && (rc != ENOENT)) {
smbsr_map_errno(rc, err);
return (-1);
}
if (deleted == 0) {
smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
ERRDOS, ERROR_FILE_NOT_FOUND);
return (-1);
}
return (0);
}
static int
smb_delete_find_fname(smb_request_t *sr, smb_odir_t *od, char *namebuf, int len)
{
int rc;
smb_odirent_t *odirent;
boolean_t eos;
odirent = kmem_alloc(sizeof (smb_odirent_t), KM_SLEEP);
rc = smb_odir_read(sr, od, odirent, &eos);
if (rc == 0) {
(void) strlcpy(namebuf, odirent->od_name, len);
}
kmem_free(odirent, sizeof (smb_odirent_t));
return (rc);
}
static int
smb_delete_check_dosattr(smb_request_t *sr, smb_error_t *err)
{
smb_fqi_t *fqi;
smb_node_t *node;
smb_attr_t attr;
uint16_t sattr;
fqi = &sr->arg.dirop.fqi;
sattr = fqi->fq_sattr;
node = fqi->fq_fnode;
bzero(&attr, sizeof (attr));
attr.sa_mask = SMB_AT_DOSATTR;
if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0) {
smb_delete_error(err, NT_STATUS_INTERNAL_ERROR,
ERRDOS, ERROR_INTERNAL_ERROR);
return (-1);
}
if (attr.sa_dosattr & FILE_ATTRIBUTE_DIRECTORY) {
smb_delete_error(err, NT_STATUS_FILE_IS_A_DIRECTORY,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
if (SMB_PATHFILE_IS_READONLY(sr, node)) {
smb_delete_error(err, NT_STATUS_CANNOT_DELETE,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
!(SMB_SEARCH_HIDDEN(sattr))) {
smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
ERRDOS, ERROR_FILE_NOT_FOUND);
return (-1);
}
if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
!(SMB_SEARCH_SYSTEM(sattr))) {
smb_delete_error(err, NT_STATUS_NO_SUCH_FILE,
ERRDOS, ERROR_FILE_NOT_FOUND);
return (-1);
}
return (0);
}
static int
smb_delete_remove_file(smb_request_t *sr, smb_error_t *err)
{
int rc;
uint32_t status;
smb_fqi_t *fqi;
smb_node_t *node;
uint32_t flags = 0;
fqi = &sr->arg.dirop.fqi;
node = fqi->fq_fnode;
status = smb_oplock_break_DELETE(node, NULL);
if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
(void) smb_oplock_wait_break(sr, node, 0);
status = 0;
}
if (status != 0) {
err->status = status;
return (-1);
}
smb_node_rdlock(node);
status = smb_node_delete_check(node);
if (status != NT_STATUS_SUCCESS) {
smb_node_unlock(node);
smb_delete_error(err, NT_STATUS_SHARING_VIOLATION,
ERRDOS, ERROR_SHARING_VIOLATION);
return (-1);
}
nbl_start_crit(node->vp, RW_READER);
status = smb_nbl_conflict(node, 0, UINT64_MAX, NBL_REMOVE);
if (status == NT_STATUS_SHARING_VIOLATION) {
smb_node_end_crit(node);
smb_delete_error(err, NT_STATUS_SHARING_VIOLATION,
ERRDOS, ERROR_SHARING_VIOLATION);
return (-1);
}
if (status != NT_STATUS_SUCCESS) {
smb_node_end_crit(node);
smb_delete_error(err, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
if (SMB_TREE_SUPPORTS_CATIA(sr))
flags |= SMB_CATIA;
rc = smb_fsop_remove(sr, sr->user_cr, node->n_dnode,
node->od_name, flags);
if (rc != 0) {
smbsr_map_errno(rc, err);
rc = -1;
}
smb_node_end_crit(node);
return (rc);
}
static int
smb_delete_check_path(smb_request_t *sr)
{
smb_fqi_t *fqi = &sr->arg.dirop.fqi;
smb_pathname_t *pn = &fqi->fq_path;
if (pn->pn_fname == NULL) {
smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY,
ERRDOS, ERROR_ACCESS_DENIED);
return (-1);
}
if ((strcmp(pn->pn_fname, ".") == 0) ||
(SMB_SEARCH_DIRECTORY(fqi->fq_sattr) &&
(smb_match(pn->pn_fname, ".", B_FALSE)))) {
smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
ERRDOS, ERROR_INVALID_NAME);
return (-1);
}
return (0);
}
static void
smb_delete_error(smb_error_t *err,
uint32_t status, uint16_t errcls, uint16_t errcode)
{
err->status = status;
err->errcls = errcls;
err->errcode = errcode;
}