#include "includes.h"
#include "system/filesys.h"
#include "printing.h"
-#include "librpc/gen_ndr/messaging.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "fake_file.h"
#include "transfer_file.h"
#include "auth.h"
+#include "messages.h"
+#include "../librpc/gen_ndr/open_files.h"
/****************************************************************************
Run a file if it is a magic script.
char *fname = NULL;
NTSTATUS status;
- if (!*lp_magicscript(SNUM(conn))) {
+ if (!*lp_magicscript(talloc_tos(), SNUM(conn))) {
return NT_STATUS_OK;
}
p++;
}
- if (!strequal(lp_magicscript(SNUM(conn)),p)) {
+ if (!strequal(lp_magicscript(talloc_tos(), SNUM(conn)),p)) {
status = NT_STATUS_OK;
goto out;
}
- if (*lp_magicoutput(SNUM(conn))) {
- magic_output = lp_magicoutput(SNUM(conn));
+ if (*lp_magicoutput(talloc_tos(), SNUM(conn))) {
+ magic_output = lp_magicoutput(talloc_tos(), SNUM(conn));
} else {
magic_output = talloc_asprintf(ctx,
"%s.out",
goto out;
}
- if (transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_ex_size) == (SMB_OFF_T)-1) {
+ if (transfer_file(tmp_fd,outfd,(off_t)st.st_ex_size) == (off_t)-1) {
int err = errno;
close(tmp_fd);
close(outfd);
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 messaging_context *msg_ctx,
+static void notify_deferred_opens(struct smbd_server_connection *sconn,
struct share_mode_lock *lck)
{
- int i;
+ 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()) {
+ if (!should_notify_deferred_opens(sconn)) {
return;
}
-
- 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)) {
+
+ 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(e->op_mid);
+ 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(msg_ctx, e->pid, MSG_SMB_OPEN_RETRY,
+ messaging_send_buf(sconn->msg_ctx, e->pid,
+ MSG_SMB_OPEN_RETRY,
(uint8 *)msg,
MSG_SMB_SHARE_MODE_ENTRY_SIZE);
}
}
+ TALLOC_FREE(deferred);
}
/****************************************************************************
NTSTATUS delete_all_streams(connection_struct *conn, const char *fname)
{
- struct stream_struct *stream_info;
+ struct stream_struct *stream_info = NULL;
int i;
- unsigned int num_streams;
+ unsigned int num_streams = 0;
TALLOC_CTX *frame = talloc_stackframe();
NTSTATUS status;
- status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
- &num_streams, &stream_info);
+ status = vfs_streaminfo(conn, NULL, fname, talloc_tos(),
+ &num_streams, &stream_info);
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
DEBUG(10, ("no streams around\n"));
}
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
+ DEBUG(10, ("vfs_streaminfo failed: %s\n",
nt_errstr(status)));
goto fail;
}
enum file_close_type close_type)
{
connection_struct *conn = fsp->conn;
+ struct server_id self = messaging_server_id(conn->sconn->msg_ctx);
bool delete_file = false;
bool changed_user = false;
struct share_mode_lock *lck = NULL;
NTSTATUS tmp_status;
struct file_id id;
const struct security_unix_token *del_token = NULL;
+ const struct security_token *del_nt_token = NULL;
+ bool got_tokens = false;
+ bool normal_close;
/* Ensure any pending write time updates are done. */
if (fsp->update_write_time_event) {
- update_write_time_handler(smbd_event_context(),
+ update_write_time_handler(fsp->conn->sconn->ev_ctx,
fsp->update_write_time_event,
timeval_current(),
(void *)fsp);
* This prevents race conditions with the file being created. JRA.
*/
- lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
- NULL);
-
+ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
if (lck == NULL) {
DEBUG(0, ("close_remove_share_mode: Could not get share mode "
"lock for file %s\n", fsp_str_dbg(fsp)));
- status = NT_STATUS_INVALID_PARAMETER;
- goto done;
+ return NT_STATUS_INVALID_PARAMETER;
}
if (fsp->write_time_forced) {
DEBUG(10,("close_remove_share_mode: write time forced "
"for file %s\n",
fsp_str_dbg(fsp)));
- set_close_write_time(fsp, lck->changed_write_time);
+ set_close_write_time(fsp, lck->data->changed_write_time);
} else if (fsp->update_write_time_on_close) {
/* Someone had a pending write. */
if (null_timespec(fsp->close_write_time)) {
}
}
- 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)));
- }
-
if (fsp->initial_delete_on_close &&
!is_delete_on_close_set(lck, fsp->name_hash)) {
bool became_user = False;
became_user = True;
}
fsp->delete_on_close = true;
- set_delete_on_close_lck(fsp, lck, True, get_current_utok(conn));
+ set_delete_on_close_lck(fsp, lck, True,
+ get_current_nttok(conn),
+ get_current_utok(conn));
if (became_user) {
unbecome_user();
}
/* See if others still have the file open via this pathname.
If this is the case, then don't delete. If all opens are
POSIX delete now. */
- 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) &&
- e->name_hash == fsp->name_hash) {
- if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
- continue;
- }
- delete_file = False;
- break;
+ for (i=0; i<lck->data->num_share_modes; i++) {
+ struct share_mode_entry *e = &lck->data->share_modes[i];
+
+ if (!is_valid_share_mode_entry(e)) {
+ continue;
+ }
+ if (e->name_hash != fsp->name_hash) {
+ continue;
}
+ if (fsp->posix_open
+ && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) {
+ continue;
+ }
+ if (serverid_equal(&self, &e->pid) &&
+ (e->share_file_id == fsp->fh->gen_id)) {
+ continue;
+ }
+ if (share_mode_stale_pid(lck->data, i)) {
+ continue;
+ }
+ delete_file = False;
+ break;
}
}
/* Notify any deferred opens waiting on this close. */
- notify_deferred_opens(conn->sconn->msg_ctx, lck);
+ notify_deferred_opens(conn->sconn, lck);
reply_to_oplock_break_requests(fsp);
/*
* reference to a file.
*/
- if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) ||
- !delete_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;
}
*/
fsp->update_write_time_on_close = false;
- del_token = get_delete_on_close_token(lck, fsp->name_hash);
- SMB_ASSERT(del_token != NULL);
+ got_tokens = get_delete_on_close_token(lck, fsp->name_hash,
+ &del_nt_token, &del_token);
+ SMB_ASSERT(got_tokens);
if (!unix_token_equal(del_token, get_current_utok(conn))) {
/* Become the user who requested the delete. */
del_token->gid,
del_token->ngroups,
del_token->groups,
- NULL);
+ del_nt_token);
changed_user = true;
}
*/
fsp->delete_on_close = false;
- set_delete_on_close_lck(fsp, lck, false, NULL);
+ set_delete_on_close_lck(fsp, lck, false, NULL, NULL);
done:
pop_sec_ctx();
}
+ 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);
if (delete_file) {
return NT_STATUS_OK;
}
- /* On close if we're changing the real file time we
- * must update it in the open file db too. */
- (void)set_write_time(fsp->file_id, fsp->close_write_time);
+ /*
+ * get_existing_share_mode_lock() isn't really the right
+ * call here, as we're being called after
+ * close_remove_share_mode() inside close_normal_file()
+ * so it's quite normal to not have an existing share
+ * mode here. However, get_share_mode_lock() doesn't
+ * work because that will create a new share mode if
+ * one doesn't exist - so stick with this call (just
+ * ignore any error we get if the share mode doesn't
+ * exist.
+ */
- lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL);
+ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
if (lck) {
+ /* On close if we're changing the real file time we
+ * must update it in the open file db too. */
+ (void)set_write_time(fsp->file_id, fsp->close_write_time);
+
/* Close write times overwrite sticky write times
so we must replace any sticky write time here. */
- if (!null_timespec(lck->changed_write_time)) {
+ if (!null_timespec(lck->data->changed_write_time)) {
(void)set_sticky_write_time(fsp->file_id, fsp->close_write_time);
}
TALLOC_FREE(lck);
}
ft.mtime = fsp->close_write_time;
- /* We must use NULL for the fsp handle here, as smb_set_file_time()
- checks the fsp access_mask, which may not include FILE_WRITE_ATTRIBUTES.
- As this is a close based update, we are not directly changing the
+ /* As this is a close based update, we are not directly changing the
file attributes from a client call, but indirectly from a write. */
- status = smb_set_file_time(fsp->conn, NULL, fsp->fsp_name, &ft, false);
+ status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10,("update_write_time_on_close: smb_set_file_time "
"on file %s returned %s\n",
NTSTATUS status = NT_STATUS_OK;
NTSTATUS tmp;
connection_struct *conn = fsp->conn;
+ int ret;
- if (close_type == ERROR_CLOSE) {
- cancel_aio_by_fsp(fsp);
- } else {
- /*
- * If we're finishing async io on a close we can get a write
- * error here, we must remember this.
- */
- int ret = wait_for_aio_completion(fsp);
- if (ret) {
- status = ntstatus_keeperror(
- status, map_nt_error_from_unix(ret));
- }
+ /*
+ * If we're finishing async io on a close we can get a write
+ * error here, we must remember this.
+ */
+ ret = wait_for_aio_completion(fsp);
+ if (ret) {
+ status = ntstatus_keeperror(
+ status, map_nt_error_from_unix(ret));
}
/*
status = ntstatus_keeperror(status, tmp);
DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
- conn->session_info->unix_name, fsp_str_dbg(fsp),
+ conn->session_info->unix_info->unix_name, fsp_str_dbg(fsp),
conn->num_files_open - 1,
nt_errstr(status) ));
return NT_STATUS_OK;
}
- if(((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) {
+ if(((errno == ENOTEMPTY)||(errno == EEXIST)) && *lp_veto_files(talloc_tos(), SNUM(conn))) {
/*
* Check to see if the only thing in this directory are
* vetoed files/directories. If so then delete them and
static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
enum file_close_type close_type)
{
+ struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx);
struct share_mode_lock *lck = NULL;
bool delete_dir = False;
NTSTATUS status = NT_STATUS_OK;
NTSTATUS status1 = NT_STATUS_OK;
+ const struct security_token *del_nt_token = NULL;
const struct security_unix_token *del_token = NULL;
/*
* reference to a directory also.
*/
- lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL,
- NULL);
-
+ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
if (lck == NULL) {
DEBUG(0, ("close_directory: Could not get share mode lock for "
"%s\n", fsp_str_dbg(fsp)));
- status = NT_STATUS_INVALID_PARAMETER;
- goto out;
- }
-
- if (!del_share_mode(lck, fsp)) {
- DEBUG(0, ("close_directory: Could not delete share entry for "
- "%s\n", fsp_str_dbg(fsp)));
+ return NT_STATUS_INVALID_PARAMETER;
}
if (fsp->initial_delete_on_close) {
send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx,
fsp->fsp_name->base_name);
set_delete_on_close_lck(fsp, lck, true,
+ get_current_nttok(fsp->conn),
get_current_utok(fsp->conn));
fsp->delete_on_close = true;
if (became_user) {
}
}
- del_token = get_delete_on_close_token(lck, fsp->name_hash);
- delete_dir = (del_token != NULL);
+ delete_dir = get_delete_on_close_token(lck, fsp->name_hash,
+ &del_nt_token, &del_token);
if (delete_dir) {
int i;
/* See if others still have the dir open. If this is the
* case, then don't delete. If all opens are POSIX delete now. */
- for (i=0; i<lck->num_share_modes; i++) {
- struct share_mode_entry *e = &lck->share_modes[i];
+ for (i=0; i<lck->data->num_share_modes; i++) {
+ 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)) {
continue;
}
+ if (serverid_equal(&self, &e->pid) &&
+ (e->share_file_id == fsp->fh->gen_id)) {
+ continue;
+ }
+ if (share_mode_stale_pid(lck->data, i)) {
+ continue;
+ }
delete_dir = False;
break;
}
del_token->gid,
del_token->ngroups,
del_token->groups,
- NULL);
+ del_nt_token);
+
+ if (!del_share_mode(lck, fsp)) {
+ DEBUG(0, ("close_directory: Could not delete share entry for "
+ "%s\n", fsp_str_dbg(fsp)));
+ }
TALLOC_FREE(lck);
+ 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);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("delete_all_streams failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
status = rmdir_internals(talloc_tos(), fsp);
DEBUG(5,("close_directory: %s. Delete on close was set - "
remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING);
}
} else {
+ if (!del_share_mode(lck, fsp)) {
+ DEBUG(0, ("close_directory: Could not delete share entry for "
+ "%s\n", fsp_str_dbg(fsp)));
+ }
+
TALLOC_FREE(lck);
remove_pending_change_notify_requests_by_fid(
fsp, NT_STATUS_OK);
close_filestruct(fsp);
file_free(req, fsp);
- out:
- TALLOC_FREE(lck);
if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(status1)) {
status = status1;
}
struct server_id server_id,
DATA_BLOB *data)
{
- struct smbd_server_connection *sconn;
files_struct *fsp = NULL;
struct share_mode_entry e;
-
- sconn = msg_ctx_to_sconn(msg_ctx);
- if (sconn == NULL) {
- DEBUG(1, ("could not find sconn\n"));
- return;
- }
+ struct smbd_server_connection *sconn =
+ talloc_get_type_abort(private_data,
+ struct smbd_server_connection);
message_to_share_mode_entry(&e, (char *)data->data);