#include "includes.h"
extern struct current_user current_user;
static void check_magic(files_struct *fsp,connection_struct *conn)
{
if (!*lp_magicscript(SNUM(conn)))
return;
DEBUG(5,("checking magic for %s\n",fsp->fsp_name));
{
char *p;
if (!(p = strrchr_m(fsp->fsp_name,'/')))
p = fsp->fsp_name;
else
p++;
if (!strequal(lp_magicscript(SNUM(conn)),p))
return;
}
{
int ret;
pstring magic_output;
pstring fname;
SMB_STRUCT_STAT st;
int tmp_fd, outfd;
pstrcpy(fname,fsp->fsp_name);
if (*lp_magicoutput(SNUM(conn)))
pstrcpy(magic_output,lp_magicoutput(SNUM(conn)));
else
slprintf(magic_output,sizeof(fname)-1, "%s.out",fname);
chmod(fname,0755);
ret = smbrun(fname,&tmp_fd);
DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret));
unlink(fname);
if (ret != 0 || tmp_fd == -1) {
if (tmp_fd != -1)
close(tmp_fd);
return;
}
outfd = open(magic_output, O_CREAT|O_EXCL|O_RDWR, 0600);
if (outfd == -1) {
close(tmp_fd);
return;
}
if (sys_fstat(tmp_fd,&st) == -1) {
close(tmp_fd);
close(outfd);
return;
}
transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
close(tmp_fd);
close(outfd);
}
}
static NTSTATUS close_filestruct(files_struct *fsp)
{
NTSTATUS status = NT_STATUS_OK;
connection_struct *conn = fsp->conn;
if (fsp->fh->fd != -1) {
if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) {
status = map_nt_error_from_unix(errno);
}
delete_write_cache(fsp);
}
conn->num_files_open--;
SAFE_FREE(fsp->wbmpx_ptr);
return status;
}
static void notify_deferred_opens(struct share_mode_lock *lck)
{
int i;
for (i=0; i<lck->num_share_modes; i++) {
struct share_mode_entry *e = &lck->share_modes[i];
if (!is_deferred_open_entry(e)) {
continue;
}
if (procid_is_me(&e->pid)) {
schedule_deferred_open_smb_message(e->op_mid);
} else {
char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
share_mode_entry_to_message(msg, e);
message_send_pid(e->pid, MSG_SMB_OPEN_RETRY,
msg, MSG_SMB_SHARE_MODE_ENTRY_SIZE, True);
}
}
}
static NTSTATUS close_remove_share_mode(files_struct *fsp,
enum file_close_type close_type)
{
connection_struct *conn = fsp->conn;
BOOL delete_file = False;
struct share_mode_lock *lck;
SMB_STRUCT_STAT sbuf;
NTSTATUS status = NT_STATUS_OK;
int ret;
lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL);
if (lck == NULL) {
DEBUG(0, ("close_remove_share_mode: Could not get share mode "
"lock for file %s\n", fsp->fsp_name));
return NT_STATUS_INVALID_PARAMETER;
}
if (!del_share_mode(lck, fsp)) {
DEBUG(0, ("close_remove_share_mode: Could not delete share "
"entry for file %s\n", fsp->fsp_name));
}
if (fsp->initial_delete_on_close && (lck->delete_token == NULL)) {
BOOL became_user = False;
if (current_user.vuid != fsp->vuid) {
become_user(conn, fsp->vuid);
became_user = True;
}
set_delete_on_close_lck(lck, True, ¤t_user.ut);
if (became_user) {
unbecome_user();
}
}
delete_file = lck->delete_on_close;
if (delete_file) {
int i;
for (i=0; i<lck->num_share_modes; i++) {
struct share_mode_entry *e = &lck->share_modes[i];
if (is_valid_share_mode_entry(e)) {
if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
continue;
}
delete_file = False;
break;
}
}
}
notify_deferred_opens(lck);
reply_to_oplock_break_requests(fsp);
if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE)
|| !delete_file
|| (lck->delete_token == NULL)) {
TALLOC_FREE(lck);
return NT_STATUS_OK;
}
DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
"- deleting file.\n", fsp->fsp_name));
if (!push_sec_ctx()) {
smb_panic("close_remove_share_mode: file %s. failed to push "
"sec_ctx.\n");
}
set_sec_ctx(lck->delete_token->uid,
lck->delete_token->gid,
lck->delete_token->ngroups,
lck->delete_token->groups,
NULL);
if (fsp->posix_open) {
ret = SMB_VFS_LSTAT(conn,fsp->fsp_name,&sbuf);
} else {
ret = SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf);
}
if (ret != 0) {
DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
"was set and stat failed with error %s\n",
fsp->fsp_name, strerror(errno) ));
goto done;
}
if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) {
DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
"was set and dev and/or inode does not match\n",
fsp->fsp_name ));
DEBUG(5,("close_remove_share_mode: file %s. stored dev = %x, "
"inode = %.0f stat dev = %x, inode = %.0f\n",
fsp->fsp_name,
(unsigned int)fsp->dev, (double)fsp->inode,
(unsigned int)sbuf.st_dev, (double)sbuf.st_ino ));
goto done;
}
if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) {
DEBUG(5,("close_remove_share_mode: file %s. Delete on close "
"was set and unlink failed with error %s\n",
fsp->fsp_name, strerror(errno) ));
status = map_nt_error_from_unix(errno);
}
notify_fname(conn, NOTIFY_ACTION_REMOVED,
FILE_NOTIFY_CHANGE_FILE_NAME,
fsp->fsp_name);
set_delete_on_close_lck(lck, False, NULL);
done:
pop_sec_ctx();
TALLOC_FREE(lck);
return status;
}
static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_type)
{
NTSTATUS status = NT_STATUS_OK;
NTSTATUS saved_status1 = NT_STATUS_OK;
NTSTATUS saved_status2 = NT_STATUS_OK;
NTSTATUS saved_status3 = NT_STATUS_OK;
connection_struct *conn = fsp->conn;
if (fsp->aio_write_behind) {
int ret = wait_for_aio_completion(fsp);
if (ret) {
saved_status1 = map_nt_error_from_unix(ret);
}
} else {
cancel_aio_by_fsp(fsp);
}
saved_status2 = close_filestruct(fsp);
if (fsp->print_file) {
print_fsp_end(fsp, close_type);
file_free(fsp);
return NT_STATUS_OK;
}
if (fsp->fh->ref_count == 1) {
saved_status3 = close_remove_share_mode(fsp, close_type);
}
if(fsp->oplock_type) {
release_file_oplock(fsp);
}
locking_close_file(fsp);
status = fd_close(conn, fsp);
if (close_type == NORMAL_CLOSE) {
check_magic(fsp,conn);
}
if (fsp->pending_modtime_owner && !null_timespec(fsp->pending_modtime)) {
set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
} else if (!null_timespec(fsp->last_write_time)) {
set_filetime(conn, fsp->fsp_name, fsp->last_write_time);
}
if (NT_STATUS_IS_OK(status)) {
if (!NT_STATUS_IS_OK(saved_status1)) {
status = saved_status1;
} else if (!NT_STATUS_IS_OK(saved_status2)) {
status = saved_status2;
} else if (!NT_STATUS_IS_OK(saved_status3)) {
status = saved_status3;
}
}
DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
conn->user,fsp->fsp_name,
conn->num_files_open,
nt_errstr(status) ));
file_free(fsp);
return status;
}
static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type)
{
struct share_mode_lock *lck = 0;
BOOL delete_dir = False;
NTSTATUS status = NT_STATUS_OK;
lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL);
if (lck == NULL) {
DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name));
return NT_STATUS_INVALID_PARAMETER;
}
if (!del_share_mode(lck, fsp)) {
DEBUG(0, ("close_directory: Could not delete share entry for %s\n", fsp->fsp_name));
}
if (fsp->initial_delete_on_close) {
BOOL became_user = False;
if (current_user.vuid != fsp->vuid) {
become_user(fsp->conn, fsp->vuid);
became_user = True;
}
send_stat_cache_delete_message(fsp->fsp_name);
set_delete_on_close_lck(lck, True, ¤t_user.ut);
if (became_user) {
unbecome_user();
}
}
delete_dir = lck->delete_on_close;
if (delete_dir) {
int i;
for (i=0; i<lck->num_share_modes; i++) {
struct share_mode_entry *e = &lck->share_modes[i];
if (is_valid_share_mode_entry(e)) {
if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
continue;
}
delete_dir = False;
break;
}
}
}
if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) &&
delete_dir &&
lck->delete_token) {
if (!push_sec_ctx()) {
smb_panic("close_directory: failed to push sec_ctx.\n");
}
set_sec_ctx(lck->delete_token->uid,
lck->delete_token->gid,
lck->delete_token->ngroups,
lck->delete_token->groups,
NULL);
TALLOC_FREE(lck);
status = rmdir_internals(fsp->conn, fsp->fsp_name);
DEBUG(5,("close_directory: %s. Delete on close was set - "
"deleting directory returned %s.\n",
fsp->fsp_name, nt_errstr(status)));
pop_sec_ctx();
if(NT_STATUS_IS_OK(status)) {
remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING);
}
} else {
TALLOC_FREE(lck);
remove_pending_change_notify_requests_by_fid(
fsp, NT_STATUS_OK);
}
close_filestruct(fsp);
file_free(fsp);
return status;
}
NTSTATUS close_stat(files_struct *fsp)
{
close_filestruct(fsp);
file_free(fsp);
return NT_STATUS_OK;
}
NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type)
{
if(fsp->is_directory) {
return close_directory(fsp, close_type);
} else if (fsp->is_stat) {
return close_stat(fsp);
} else if (fsp->fake_file_handle != NULL) {
return close_fake_file(fsp);
}
return close_normal_file(fsp, close_type);
}