X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fclose.c;h=bdd177cca8f38eb43347cf9ed48eb437f85138da;hb=6277592e0564db12cd25369c7d8d782d95c3583d;hp=fad960149142eb66b2c642492485be5011e112ff;hpb=c9b654f5810f46b20e082895499c0bf2a3077173;p=metze%2Fsamba%2Fwip.git diff --git a/source3/smbd/close.c b/source3/smbd/close.c index fad960149142..bdd177cca8f3 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -20,9 +20,15 @@ */ #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. @@ -112,7 +118,7 @@ static NTSTATUS check_magic(struct files_struct *fsp) 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); @@ -150,26 +156,81 @@ static NTSTATUS close_filestruct(files_struct *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 messaging_context *msg_ctx, +static void notify_deferred_opens(struct smbd_server_connection *sconn, struct share_mode_lock *lck) { - int i; + 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; inum_share_modes; i++) { - struct share_mode_entry *e = &lck->share_modes[i]; - - if (!is_deferred_open_entry(e)) { - continue; - } - + + num_deferred = 0; + for (i=0; idata->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; idata->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; ipid)) { /* * We need to notify ourself to retry the open. Do @@ -177,17 +238,19 @@ static void notify_deferred_opens(struct messaging_context *msg_ctx, * 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); } /**************************************************************************** @@ -196,14 +259,14 @@ static void notify_deferred_opens(struct messaging_context *msg_ctx, 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")); @@ -212,7 +275,7 @@ NTSTATUS delete_all_streams(connection_struct *conn, const char *fname) } 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; } @@ -274,10 +337,13 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, NTSTATUS status = NT_STATUS_OK; 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; /* 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); @@ -289,9 +355,7 @@ static NTSTATUS close_remove_share_mode(files_struct *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))); @@ -303,7 +367,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, 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)) { @@ -321,13 +385,8 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, } } - 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 && (lck->delete_token == NULL)) { + if (fsp->initial_delete_on_close && + !is_delete_on_close_set(lck, fsp->name_hash)) { bool became_user = False; /* Initial delete on close was set and no one else @@ -338,24 +397,34 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, became_user = True; } fsp->delete_on_close = true; - set_delete_on_close_lck(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(); } } - delete_file = lck->delete_on_close; + delete_file = is_delete_on_close_set(lck, fsp->name_hash); if (delete_file) { int i; - /* See if others still have the file open. If this is the - * case, then don't delete. If all opens are POSIX delete now. */ - for (i=0; inum_share_modes; i++) { - struct share_mode_entry *e = &lck->share_modes[i]; - if (is_valid_share_mode_entry(e)) { + /* 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; idata->num_share_modes; i++) { + struct share_mode_entry *e = &lck->data->share_modes[i]; + + //TODO: continue if our own entry... + + 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 (share_mode_stale_pid(lck->data, i)) { + continue; + } delete_file = False; break; } @@ -363,7 +432,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, } /* 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); /* @@ -371,9 +440,14 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, * reference to a file. */ - if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) - || !delete_file - || (lck->delete_token == NULL)) { + if (!(close_type == NORMAL_CLOSE || close_type == SHUTDOWN_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; } @@ -390,24 +464,28 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, */ fsp->update_write_time_on_close = false; - if (!unix_token_equal(lck->delete_token, get_current_utok(conn))) { + 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. */ DEBUG(5,("close_remove_share_mode: file %s. " "Change user to uid %u\n", fsp_str_dbg(fsp), - (unsigned int)lck->delete_token->uid)); + (unsigned int)del_token->uid)); 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); + set_sec_ctx(del_token->uid, + del_token->gid, + del_token->ngroups, + del_token->groups, + del_nt_token); changed_user = true; } @@ -472,10 +550,6 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, status = map_nt_error_from_unix(errno); } - notify_fname(conn, NOTIFY_ACTION_REMOVED, - FILE_NOTIFY_CHANGE_FILE_NAME, - fsp->fsp_name->base_name); - /* As we now have POSIX opens which can unlink * with other open files we may have taken * this code path with more than one share mode @@ -484,7 +558,7 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, */ fsp->delete_on_close = false; - set_delete_on_close_lck(lck, False, NULL); + set_delete_on_close_lck(fsp, lck, false, NULL, NULL); done: @@ -493,7 +567,33 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, pop_sec_ctx(); } + if (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); + + if (delete_file) { + /* + * Do the notification after we released the share + * mode lock. Inside notify_fname we take out another + * tdb lock. With ctdb also accessing our databases, + * this can lead to deadlocks. Putting this notify + * after the TALLOC_FREE(lck) above we avoid locking + * two records simultaneously. Notifies are async and + * informational only, so calling the notify_fname + * without holding the share mode lock should not do + * any harm. + */ + notify_fname(conn, NOTIFY_ACTION_REMOVED, + FILE_NOTIFY_CHANGE_FILE_NAME, + fsp->fsp_name->base_name); + } + return status; } @@ -536,23 +636,41 @@ static NTSTATUS update_write_time_on_close(struct files_struct *fsp) 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; + /* 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, 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", + fsp_str_dbg(fsp), + nt_errstr(status))); return status; } @@ -581,19 +699,16 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp, 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)); } /* @@ -653,7 +768,7 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp, status = ntstatus_keeperror(status, tmp); DEBUG(2,("%s closed file %s (numopen=%d) %s\n", - conn->server_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) )); @@ -784,7 +899,7 @@ static NTSTATUS rmdir_internals(TALLOC_CTX *ctx, files_struct *fsp) return NT_STATUS_OK; } - if(((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { + if(((errno == ENOTEMPTY)||(errno == EEXIST)) && *lp_veto_files(SNUM(conn))) { /* * Check to see if the only thing in this directory are * vetoed files/directories. If so then delete them and @@ -929,15 +1044,15 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, 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; /* * NT can set delete_on_close of the last open * 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))); @@ -963,25 +1078,32 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, } send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, fsp->fsp_name->base_name); - set_delete_on_close_lck(lck, True, get_current_utok(fsp->conn)); + 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) { unbecome_user(); } } - delete_dir = lck->delete_on_close; + 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; inum_share_modes; i++) { - struct share_mode_entry *e = &lck->share_modes[i]; - if (is_valid_share_mode_entry(e)) { + for (i=0; idata->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 (share_mode_stale_pid(lck->data, i)) { + continue; + } delete_dir = False; break; } @@ -989,8 +1111,7 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, } if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) && - delete_dir && - lck->delete_token) { + delete_dir) { /* Become the user who requested the delete. */ @@ -998,14 +1119,25 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, 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); + set_sec_ctx(del_token->uid, + del_token->gid, + del_token->ngroups, + del_token->groups, + del_nt_token); 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))); + goto out; + } + } + status = rmdir_internals(talloc_tos(), fsp); DEBUG(5,("close_directory: %s. Delete on close was set - " @@ -1037,10 +1169,6 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, strerror(errno))); } - if (fsp->dptr) { - dptr_CloseDir(fsp->dptr); - } - /* * Do the code common to files and directories. */ @@ -1104,6 +1232,9 @@ void msg_close_file(struct messaging_context *msg_ctx, { files_struct *fsp = NULL; struct share_mode_entry e; + struct smbd_server_connection *sconn = + talloc_get_type_abort(private_data, + struct smbd_server_connection); message_to_share_mode_entry(&e, (char *)data->data); @@ -1117,7 +1248,7 @@ void msg_close_file(struct messaging_context *msg_ctx, TALLOC_FREE(sm_str); } - fsp = file_find_dif(e.id, e.share_file_id); + fsp = file_find_dif(sconn, e.id, e.share_file_id); if (!fsp) { DEBUG(10,("msg_close_file: failed to find file.\n")); return;