/*
Unix SMB/CIFS implementation.
file closing
Copyright (C) Andrew Tridgell 1992-1998
Copyright (C) Jeremy Allison 1992-2007.
Copyright (C) Volker Lendecke 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "system/filesys.h"
#include "lib/util/server_id.h"
#include "printing.h"
#include "locking/share_mode_lock.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "smbd/smbXsrv_open.h"
#include "smbd/scavenger.h"
#include "fake_file.h"
#include "transfer_file.h"
#include "auth.h"
#include "messages.h"
#include "librpc/gen_ndr/ndr_open_files.h"
#include "lib/util/tevent_ntstatus.h"
#include "source3/smbd/dir.h"
/****************************************************************************
Run a file if it is a magic script.
****************************************************************************/
static NTSTATUS check_magic(struct files_struct *fsp)
{
int ret;
const struct loadparm_substitution *lp_sub =
loadparm_s3_global_substitution();
const char *magic_output = NULL;
SMB_STRUCT_STAT st;
int tmp_fd, outfd;
TALLOC_CTX *ctx = NULL;
const char *p;
struct connection_struct *conn = fsp->conn;
char *fname = NULL;
NTSTATUS status;
if (!*lp_magic_script(talloc_tos(), lp_sub, SNUM(conn))) {
return NT_STATUS_OK;
}
DEBUG(5,("checking magic for %s\n", fsp_str_dbg(fsp)));
ctx = talloc_stackframe();
fname = fsp->fsp_name->base_name;
if (!(p = strrchr_m(fname,'/'))) {
p = fname;
} else {
p++;
}
if (!strequal(lp_magic_script(talloc_tos(), lp_sub, SNUM(conn)),p)) {
status = NT_STATUS_OK;
goto out;
}
if (*lp_magic_output(talloc_tos(), lp_sub, SNUM(conn))) {
magic_output = lp_magic_output(talloc_tos(), lp_sub, SNUM(conn));
} else {
magic_output = talloc_asprintf(ctx,
"%s.out",
fname);
}
if (!magic_output) {
status = NT_STATUS_NO_MEMORY;
goto out;
}
/* Ensure we don't depend on user's PATH. */
p = talloc_asprintf(ctx, "./%s", fname);
if (!p) {
status = NT_STATUS_NO_MEMORY;
goto out;
}
if (chmod(fname, 0755) == -1) {
status = map_nt_error_from_unix(errno);
goto out;
}
ret = smbrun(p, &tmp_fd, NULL);
DEBUG(3,("Invoking magic command %s gave %d\n",
p,ret));
unlink(fname);
if (ret != 0 || tmp_fd == -1) {
if (tmp_fd != -1) {
close(tmp_fd);
}
status = NT_STATUS_UNSUCCESSFUL;
goto out;
}
outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
if (outfd == -1) {
int err = errno;
close(tmp_fd);
status = map_nt_error_from_unix(err);
goto out;
}
if (sys_fstat(tmp_fd, &st, false) == -1) {
int err = errno;
close(tmp_fd);
close(outfd);
status = map_nt_error_from_unix(err);
goto out;
}
if (transfer_file(tmp_fd,outfd,(off_t)st.st_ex_size) == (off_t)-1) {
int err = errno;
close(tmp_fd);
close(outfd);
status = map_nt_error_from_unix(err);
goto out;
}
close(tmp_fd);
if (close(outfd) == -1) {
status = map_nt_error_from_unix(errno);
goto out;
}
status = NT_STATUS_OK;
out:
TALLOC_FREE(ctx);
return status;
}
/****************************************************************************
Delete all streams
****************************************************************************/
NTSTATUS delete_all_streams(struct files_struct *fsp,
struct files_struct *dirfsp,
struct smb_filename *fsp_atname)
{
struct smb_filename *smb_fname = fsp->fsp_name;
struct stream_struct *stream_info = NULL;
unsigned int i;
unsigned int num_streams = 0;
TALLOC_CTX *frame = talloc_stackframe();
NTSTATUS status;
status = vfs_fstreaminfo(fsp,
talloc_tos(),
&num_streams,
&stream_info);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
DEBUG(10, ("no streams around\n"));
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("vfs_fstreaminfo failed: %s\n",
nt_errstr(status)));
goto fail;
}
DEBUG(10, ("delete_all_streams found %d streams\n",
num_streams));
if (num_streams == 0) {
TALLOC_FREE(frame);
return NT_STATUS_OK;
}
for (i=0; i<num_streams; i++) {
int res;
struct smb_filename *smb_fname_stream;
if (strequal(stream_info[i].name, "::$DATA")) {
continue;
}
smb_fname_stream = synthetic_smb_fname(
talloc_tos(),
fsp_atname->base_name,
stream_info[i].name,
NULL,
smb_fname->twrp,
(smb_fname->flags & ~SMB_FILENAME_POSIX_PATH));
if (smb_fname_stream == NULL) {
DEBUG(0, ("talloc_aprintf failed\n"));
status = NT_STATUS_NO_MEMORY;
goto fail;
}
res = SMB_VFS_UNLINKAT(dirfsp->conn,
dirfsp,
smb_fname_stream,
0);
if (res == -1) {
status = map_nt_error_from_unix(errno);
DEBUG(10, ("Could not delete stream %s: %s\n",
smb_fname_str_dbg(smb_fname_stream),
strerror(errno)));
TALLOC_FREE(smb_fname_stream);
break;
}
TALLOC_FREE(smb_fname_stream);
}
fail:
TALLOC_FREE(frame);
return status;
}
struct has_other_nonposix_opens_state {
files_struct *fsp;
bool found_another;
};
static bool has_other_nonposix_opens_fn(
struct share_mode_entry *e,
bool *modified,
void *private_data)
{
struct has_other_nonposix_opens_state *state = private_data;
struct files_struct *fsp = state->fsp;
if (e->flags & SHARE_ENTRY_FLAG_POSIX_OPEN) {
return false;
}
if (fsp != NULL) {
if (e->name_hash != fsp->name_hash) {
return false;
}
if (e->share_file_id == fh_get_gen_id(fsp->fh)) {
struct server_id self = messaging_server_id(
fsp->conn->sconn->msg_ctx);
if (server_id_equal(&self, &e->pid)) {
return false;
}
}
}
if (share_entry_stale_pid(e)) {
return false;
}
state->found_another = true;
return true;
}
bool has_other_nonposix_opens(struct share_mode_lock *lck,
struct files_struct *fsp)
{
struct has_other_nonposix_opens_state state = { .fsp = fsp };
bool ok;
ok = share_mode_forall_entries(
lck, has_other_nonposix_opens_fn, &state);
if (!ok) {
return false;
}
return state.found_another;
}
struct has_delete_access_opens_state {
bool delete_access;
bool delete_pending;
};
static bool has_delete_access_opens_fn(
struct share_mode_entry *e,
bool *modified,
void *private_data)
{
struct has_delete_access_opens_state *state = private_data;
if (share_entry_stale_pid(e)) {
return false;
}
if (e->access_mask & SEC_STD_DELETE) {
state->delete_access = true;
}
return false;
}
bool has_delete_opens(struct files_struct *fsp,
struct share_mode_lock *lck,
bool *delete_access,
bool *delete_pending)
{
struct has_delete_access_opens_state state = {};
bool ok;
ok = share_mode_forall_entries(
lck, has_delete_access_opens_fn, &state);
if (!ok) {
return false;
}
*delete_access = state.delete_access;
*delete_pending = is_delete_on_close_set(lck, fsp->name_hash);
return true;
}
bool has_nonposix_opens(struct share_mode_lock *lck)
{
struct has_other_nonposix_opens_state state = {};
bool ok;
ok = share_mode_forall_entries(
lck, has_other_nonposix_opens_fn, &state);
if (!ok) {
return false;
}
return state.found_another;
}
struct close_share_mode_lock_state {
struct share_mode_entry_prepare_state prepare_state;
const char *object_type;
struct files_struct *fsp
|