#include "printing.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
+#include "smbd/scavenger.h"
#include "fake_file.h"
#include "transfer_file.h"
#include "auth.h"
char *fname = NULL;
NTSTATUS status;
- if (!*lp_magicscript(talloc_tos(), SNUM(conn))) {
+ if (!*lp_magic_script(talloc_tos(), SNUM(conn))) {
return NT_STATUS_OK;
}
p++;
}
- if (!strequal(lp_magicscript(talloc_tos(), SNUM(conn)),p)) {
+ if (!strequal(lp_magic_script(talloc_tos(), SNUM(conn)),p)) {
status = NT_STATUS_OK;
goto out;
}
- if (*lp_magicoutput(talloc_tos(), SNUM(conn))) {
- magic_output = lp_magicoutput(talloc_tos(), SNUM(conn));
+ if (*lp_magic_output(talloc_tos(), SNUM(conn))) {
+ magic_output = lp_magic_output(talloc_tos(), SNUM(conn));
} else {
magic_output = talloc_asprintf(ctx,
"%s.out",
NTSTATUS status = NT_STATUS_OK;
if (fsp->fh->fd != -1) {
- if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) {
+ if(flush_write_cache(fsp, SAMBA_CLOSE_FLUSH) == -1) {
status = map_nt_error_from_unix(errno);
}
delete_write_cache(fsp);
return status;
}
-static int compare_share_mode_times(const void *p1, const void *p2)
-{
- const struct share_mode_entry *s1 = (const struct share_mode_entry *)p1;
- const struct share_mode_entry *s2 = (const struct share_mode_entry *)p2;
- return timeval_compare(&s1->time, &s2->time);
-}
-
-/****************************************************************************
- If any deferred opens are waiting on this close, notify them.
-****************************************************************************/
-
-static void notify_deferred_opens(struct smbd_server_connection *sconn,
- struct share_mode_lock *lck)
-{
- struct server_id self = messaging_server_id(sconn->msg_ctx);
- uint32_t i, num_deferred;
- struct share_mode_entry *deferred;
-
- if (!should_notify_deferred_opens(sconn)) {
- return;
- }
-
- num_deferred = 0;
- for (i=0; i<lck->data->num_share_modes; i++) {
- struct share_mode_entry *e = &lck->data->share_modes[i];
-
- if (!is_deferred_open_entry(e)) {
- continue;
- }
- if (share_mode_stale_pid(lck->data, i)) {
- continue;
- }
- num_deferred += 1;
- }
- if (num_deferred == 0) {
- return;
- }
-
- deferred = talloc_array(talloc_tos(), struct share_mode_entry,
- num_deferred);
- if (deferred == NULL) {
- return;
- }
-
- num_deferred = 0;
- for (i=0; i<lck->data->num_share_modes; i++) {
- struct share_mode_entry *e = &lck->data->share_modes[i];
- if (is_deferred_open_entry(e)) {
- deferred[num_deferred] = *e;
- num_deferred += 1;
- }
- }
-
- /*
- * We need to sort the notifications by initial request time. Imagine
- * two opens come in asyncronously, both conflicting with the open we
- * just close here. If we don't sort the notifications, the one that
- * came in last might get the response before the one that came in
- * first. This is demonstrated with the smbtorture4 raw.mux test.
- *
- * As long as we had the UNUSED_SHARE_MODE_ENTRY, we happened to
- * survive this particular test. Without UNUSED_SHARE_MODE_ENTRY, we
- * shuffle the share mode entries around a bit, so that we do not
- * survive raw.mux anymore.
- *
- * We could have kept the ordering in del_share_mode, but as the
- * ordering was never formalized I think it is better to do it here
- * where it is necessary.
- */
-
- qsort(deferred, num_deferred, sizeof(struct share_mode_entry),
- compare_share_mode_times);
-
- for (i=0; i<num_deferred; i++) {
- struct share_mode_entry *e = &deferred[i];
-
- if (serverid_equal(&self, &e->pid)) {
- /*
- * We need to notify ourself to retry the open. Do
- * this by finding the queued SMB record, moving it to
- * the head of the queue and changing the wait time to
- * zero.
- */
- schedule_deferred_open_message_smb(sconn, e->op_mid);
- } else {
- char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
-
- share_mode_entry_to_message(msg, e);
-
- messaging_send_buf(sconn->msg_ctx, e->pid,
- MSG_SMB_OPEN_RETRY,
- (uint8 *)msg,
- MSG_SMB_SHARE_MODE_ENTRY_SIZE);
- }
- }
- TALLOC_FREE(deferred);
-}
-
/****************************************************************************
Delete all streams
****************************************************************************/
-NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
+NTSTATUS delete_all_streams(connection_struct *conn,
+ const struct smb_filename *smb_fname)
{
struct stream_struct *stream_info = NULL;
int i;
TALLOC_CTX *frame = talloc_stackframe();
NTSTATUS status;
- status = vfs_streaminfo(conn, NULL, fname, talloc_tos(),
+ status = vfs_streaminfo(conn, NULL, smb_fname, talloc_tos(),
&num_streams, &stream_info);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
for (i=0; i<num_streams; i++) {
int res;
- struct smb_filename *smb_fname_stream = NULL;
+ struct smb_filename *smb_fname_stream;
if (strequal(stream_info[i].name, "::$DATA")) {
continue;
}
- status = create_synthetic_smb_fname(talloc_tos(), fname,
- stream_info[i].name, NULL,
- &smb_fname_stream);
+ smb_fname_stream = synthetic_smb_fname(talloc_tos(),
+ smb_fname->base_name,
+ stream_info[i].name,
+ NULL,
+ smb_fname->flags);
- if (!NT_STATUS_IS_OK(status)) {
+ if (smb_fname_stream == NULL) {
DEBUG(0, ("talloc_aprintf failed\n"));
+ status = NT_STATUS_NO_MEMORY;
goto fail;
}
became_user = True;
}
fsp->delete_on_close = true;
- set_delete_on_close_lck(fsp, lck, True,
+ set_delete_on_close_lck(fsp, lck,
get_current_nttok(conn),
get_current_utok(conn));
if (became_user) {
if (e->name_hash != fsp->name_hash) {
continue;
}
- if (fsp->posix_open
+ if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN)
&& (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
continue;
}
}
}
- /* Notify any deferred opens waiting on this close. */
- notify_deferred_opens(conn->sconn, lck);
- reply_to_oplock_break_requests(fsp);
-
/*
* NT can set delete_on_close of the last open
* reference to a file.
normal_close = (close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE);
if (!normal_close || !delete_file) {
-
- if (!del_share_mode(lck, fsp)) {
- DEBUG(0, ("close_remove_share_mode: Could not delete "
- "share entry for file %s\n",
- fsp_str_dbg(fsp)));
- }
-
- TALLOC_FREE(lck);
- return NT_STATUS_OK;
+ status = NT_STATUS_OK;
+ goto done;
}
/*
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
&& !is_ntfs_stream_smb_fname(fsp->fsp_name)) {
- status = delete_all_streams(conn, fsp->fsp_name->base_name);
+ status = delete_all_streams(conn, fsp->fsp_name);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5, ("delete_all_streams failed: %s\n",
*/
fsp->delete_on_close = false;
- set_delete_on_close_lck(fsp, lck, false, NULL, NULL);
+ reset_delete_on_close_lck(fsp, lck);
done:
pop_sec_ctx();
}
+ if (fsp->kernel_share_modes_taken) {
+ int ret_flock;
+
+ /* remove filesystem sharemodes */
+ ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, 0, 0);
+ if (ret_flock == -1) {
+ DEBUG(2, ("close_remove_share_mode: removing kernel "
+ "flock for %s failed: %s\n",
+ fsp_str_dbg(fsp), strerror(errno)));
+ }
+ }
+
if (!del_share_mode(lck, fsp)) {
DEBUG(0, ("close_remove_share_mode: Could not delete share "
"entry for file %s\n", fsp_str_dbg(fsp)));
NTSTATUS status = NT_STATUS_OK;
NTSTATUS tmp;
connection_struct *conn = fsp->conn;
+ bool is_durable = false;
+
+ if (fsp->num_aio_requests != 0) {
+
+ if (close_type != SHUTDOWN_CLOSE) {
+ /*
+ * reply_close and the smb2 close must have
+ * taken care of this. No other callers of
+ * close_file should ever have created async
+ * I/O.
+ *
+ * We need to panic here because if we close()
+ * the fd while we have outstanding async I/O
+ * requests, in the worst case we could end up
+ * writing to the wrong file.
+ */
+ DEBUG(0, ("fsp->num_aio_requests=%u\n",
+ fsp->num_aio_requests));
+ smb_panic("can not close with outstanding aio "
+ "requests");
+ }
- aio_fsp_close(fsp);
+ /*
+ * For shutdown close, just drop the async requests
+ * including a potential close request pending for
+ * this fsp. Drop the close request first, the
+ * destructor for the aio_requests would execute it.
+ */
+ TALLOC_FREE(fsp->deferred_close);
+
+ while (fsp->num_aio_requests != 0) {
+ /*
+ * The destructor of the req will remove
+ * itself from the fsp.
+ * Don't use TALLOC_FREE here, this will overwrite
+ * what the destructor just wrote into
+ * aio_requests[0].
+ */
+ talloc_free(fsp->aio_requests[0]);
+ }
+ }
/*
* If we're flushing on a close we can get a write
tmp = close_filestruct(fsp);
status = ntstatus_keeperror(status, tmp);
+ if (NT_STATUS_IS_OK(status) && fsp->op != NULL) {
+ is_durable = fsp->op->global->durable;
+ }
+
+ if (close_type != SHUTDOWN_CLOSE) {
+ is_durable = false;
+ }
+
+ if (is_durable) {
+ DATA_BLOB new_cookie = data_blob_null;
+
+ tmp = SMB_VFS_DURABLE_DISCONNECT(fsp,
+ fsp->op->global->backend_cookie,
+ fsp->op,
+ &new_cookie);
+ if (NT_STATUS_IS_OK(tmp)) {
+ struct timeval tv;
+ NTTIME now;
+
+ if (req != NULL) {
+ tv = req->request_time;
+ } else {
+ tv = timeval_current();
+ }
+ now = timeval_to_nttime(&tv);
+
+ data_blob_free(&fsp->op->global->backend_cookie);
+ fsp->op->global->backend_cookie = new_cookie;
+
+ fsp->op->compat = NULL;
+ tmp = smbXsrv_open_close(fsp->op, now);
+ if (!NT_STATUS_IS_OK(tmp)) {
+ DEBUG(1, ("Failed to update smbXsrv_open "
+ "record when disconnecting durable "
+ "handle for file %s: %s - "
+ "proceeding with normal close\n",
+ fsp_str_dbg(fsp), nt_errstr(tmp)));
+ }
+ scavenger_schedule_disconnected(fsp);
+ } else {
+ DEBUG(1, ("Failed to disconnect durable handle for "
+ "file %s: %s - proceeding with normal "
+ "close\n", fsp_str_dbg(fsp), nt_errstr(tmp)));
+ }
+ if (!NT_STATUS_IS_OK(tmp)) {
+ is_durable = false;
+ }
+ }
+
+ if (is_durable) {
+ /*
+ * This is the case where we successfully disconnected
+ * a durable handle and closed the underlying file.
+ * In all other cases, we proceed with a genuine close.
+ */
+ DEBUG(10, ("%s disconnected durable handle for file %s\n",
+ conn->session_info->unix_info->unix_name,
+ fsp_str_dbg(fsp)));
+ file_free(req, fsp);
+ return NT_STATUS_OK;
+ }
+
+ if (fsp->op != NULL) {
+ /*
+ * Make sure the handle is not marked as durable anymore
+ */
+ fsp->op->global->durable = false;
+ }
+
if (fsp->print_file) {
/* FIXME: return spool errors */
print_spool_end(fsp, close_type);
/* Remove the oplock before potentially deleting the file. */
if(fsp->oplock_type) {
- release_file_oplock(fsp);
+ remove_oplock(fsp);
}
/* If this is an old DOS or FCB open and we have multiple opens on
return status;
}
/****************************************************************************
- Static function used by reply_rmdir to delete an entire directory
+ Function used by reply_rmdir to delete an entire directory
tree recursively. Return True on ok, False on fail.
****************************************************************************/
-static bool recursive_rmdir(TALLOC_CTX *ctx,
- connection_struct *conn,
- struct smb_filename *smb_dname)
+bool recursive_rmdir(TALLOC_CTX *ctx,
+ connection_struct *conn,
+ struct smb_filename *smb_dname)
{
const char *dname = NULL;
char *talloced = NULL;
SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));
- dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0);
+ dir_hnd = OpenDir(talloc_tos(), conn, smb_dname, NULL, 0);
if(dir_hnd == NULL)
return False;
struct smb_filename *smb_dname_full = NULL;
char *fullname = NULL;
bool do_break = true;
- NTSTATUS status;
if (ISDOT(dname) || ISDOTDOT(dname)) {
TALLOC_FREE(talloced);
goto err_break;
}
- status = create_synthetic_smb_fname(talloc_tos(), fullname,
- NULL, NULL,
- &smb_dname_full);
- if (!NT_STATUS_IS_OK(status)) {
+ smb_dname_full = synthetic_smb_fname(talloc_tos(),
+ fullname,
+ NULL,
+ NULL,
+ smb_dname->flags);
+ if (smb_dname_full == NULL) {
+ errno = ENOMEM;
goto err_break;
}
if(!recursive_rmdir(ctx, conn, smb_dname_full)) {
goto err_break;
}
- if(SMB_VFS_RMDIR(conn,
- smb_dname_full->base_name) != 0) {
+ if(SMB_VFS_RMDIR(conn, smb_dname_full) != 0) {
goto err_break;
}
} else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
}
ret = SMB_VFS_UNLINK(conn, smb_dname);
} else {
- ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
+ ret = SMB_VFS_RMDIR(conn, smb_dname);
}
if (ret == 0) {
notify_fname(conn, NOTIFY_ACTION_REMOVED,
char *talloced = NULL;
long dirpos = 0;
struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn,
- smb_dname->base_name, NULL,
+ smb_dname, NULL,
0);
if(dir_hnd == NULL) {
/* We only have veto files/directories.
* Are we allowed to delete them ? */
- if(!lp_recursive_veto_delete(SNUM(conn))) {
+ if(!lp_delete_veto_files(SNUM(conn))) {
TALLOC_FREE(dir_hnd);
errno = ENOTEMPTY;
goto err;
struct smb_filename *smb_dname_full = NULL;
char *fullname = NULL;
bool do_break = true;
- NTSTATUS status;
if (ISDOT(dname) || ISDOTDOT(dname)) {
TALLOC_FREE(talloced);
goto err_break;
}
- status = create_synthetic_smb_fname(talloc_tos(),
- fullname, NULL,
- NULL,
- &smb_dname_full);
- if (!NT_STATUS_IS_OK(status)) {
- errno = map_errno_from_nt_status(status);
+ smb_dname_full = synthetic_smb_fname(talloc_tos(),
+ fullname,
+ NULL,
+ NULL,
+ smb_dname->flags);
+ if (smb_dname_full == NULL) {
+ errno = ENOMEM;
goto err_break;
}
goto err_break;
}
if(SMB_VFS_RMDIR(conn,
- smb_dname_full->base_name) != 0) {
+ smb_dname_full) != 0) {
goto err_break;
}
} else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
}
TALLOC_FREE(dir_hnd);
/* Retry the rmdir */
- ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
+ ret = SMB_VFS_RMDIR(conn, smb_dname);
}
err:
NTSTATUS status1 = NT_STATUS_OK;
const struct security_token *del_nt_token = NULL;
const struct security_unix_token *del_token = NULL;
+ NTSTATUS notify_status;
+
+ if (fsp->conn->sconn->using_smb2) {
+ notify_status = STATUS_NOTIFY_CLEANUP;
+ } else {
+ notify_status = NT_STATUS_OK;
+ }
/*
* NT can set delete_on_close of the last open
}
send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx,
fsp->fsp_name->base_name);
- set_delete_on_close_lck(fsp, lck, true,
+ set_delete_on_close_lck(fsp, lck,
get_current_nttok(fsp->conn),
get_current_utok(fsp->conn));
fsp->delete_on_close = true;
struct share_mode_entry *e = &lck->data->share_modes[i];
if (is_valid_share_mode_entry(e) &&
e->name_hash == fsp->name_hash) {
- if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
+ if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) &&
+ (e->flags & SHARE_MODE_FLAG_POSIX_OPEN))
+ {
continue;
}
if (serverid_equal(&self, &e->pid) &&
if ((fsp->conn->fs_capabilities & FILE_NAMED_STREAMS)
&& !is_ntfs_stream_smb_fname(fsp->fsp_name)) {
- status = delete_all_streams(fsp->conn, fsp->fsp_name->base_name);
+ status = delete_all_streams(fsp->conn, fsp->fsp_name);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5, ("delete_all_streams failed: %s\n",
nt_errstr(status)));
* now fail as the directory has been deleted.
*/
- if(NT_STATUS_IS_OK(status)) {
- remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING);
+ if (NT_STATUS_IS_OK(status)) {
+ notify_status = NT_STATUS_DELETE_PENDING;
}
} else {
if (!del_share_mode(lck, fsp)) {
}
TALLOC_FREE(lck);
- remove_pending_change_notify_requests_by_fid(
- fsp, NT_STATUS_OK);
}
+ remove_pending_change_notify_requests_by_fid(fsp, notify_status);
+
status1 = fd_close(fsp);
if (!NT_STATUS_IS_OK(status1)) {