*/
#include "includes.h"
-
-extern struct current_user current_user;
+#include "system/filesys.h"
+#include "printing.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.
****************************************************************************/
-static void check_magic(struct files_struct *fsp)
+static NTSTATUS check_magic(struct files_struct *fsp)
{
int ret;
const char *magic_output = NULL;
TALLOC_CTX *ctx = NULL;
const char *p;
struct connection_struct *conn = fsp->conn;
+ char *fname = NULL;
+ NTSTATUS status;
if (!*lp_magicscript(SNUM(conn))) {
- return;
+ return NT_STATUS_OK;
}
- DEBUG(5,("checking magic for %s\n",fsp->fsp_name));
+ DEBUG(5,("checking magic for %s\n", fsp_str_dbg(fsp)));
+
+ ctx = talloc_stackframe();
+
+ fname = fsp->fsp_name->base_name;
- if (!(p = strrchr_m(fsp->fsp_name,'/'))) {
- p = fsp->fsp_name;
+ if (!(p = strrchr_m(fname,'/'))) {
+ p = fname;
} else {
p++;
}
if (!strequal(lp_magicscript(SNUM(conn)),p)) {
- return;
+ status = NT_STATUS_OK;
+ goto out;
}
- ctx = talloc_stackframe();
-
if (*lp_magicoutput(SNUM(conn))) {
magic_output = lp_magicoutput(SNUM(conn));
} else {
magic_output = talloc_asprintf(ctx,
"%s.out",
- fsp->fsp_name);
+ fname);
}
if (!magic_output) {
- TALLOC_FREE(ctx);
- return;
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
}
/* Ensure we don't depend on user's PATH. */
- p = talloc_asprintf(ctx, "./%s", fsp->fsp_name);
+ p = talloc_asprintf(ctx, "./%s", fname);
if (!p) {
- TALLOC_FREE(ctx);
- return;
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
}
- if (chmod(fsp->fsp_name,0755) == -1) {
- TALLOC_FREE(ctx);
- return;
+ if (chmod(fname, 0755) == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto out;
}
ret = smbrun(p,&tmp_fd);
DEBUG(3,("Invoking magic command %s gave %d\n",
p,ret));
- unlink(fsp->fsp_name);
+ unlink(fname);
if (ret != 0 || tmp_fd == -1) {
if (tmp_fd != -1) {
close(tmp_fd);
}
- TALLOC_FREE(ctx);
- return;
+ 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);
- TALLOC_FREE(ctx);
- return;
+ status = map_nt_error_from_unix(err);
+ goto out;
}
- if (sys_fstat(tmp_fd,&st) == -1) {
+ if (sys_fstat(tmp_fd, &st, false) == -1) {
+ int err = errno;
close(tmp_fd);
close(outfd);
- return;
+ status = map_nt_error_from_unix(err);
+ goto out;
}
- transfer_file(tmp_fd,outfd,(SMB_OFF_T)st.st_size);
+ 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);
- close(outfd);
+ if (close(outfd) == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto out;
+ }
+
+ status = NT_STATUS_OK;
+
+ out:
TALLOC_FREE(ctx);
+ return status;
}
/****************************************************************************
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 share_mode_lock *lck)
+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; i<lck->num_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; 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 (procid_is_me(&e->pid)) {
/*
* We need to notify ourself to retry the open. Do
* the head of the queue and changing the wait time to
* zero.
*/
- schedule_deferred_open_smb_message(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(smbd_messaging_context(),
- 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;
}
for (i=0; i<num_streams; i++) {
int res;
- char *streamname;
+ struct smb_filename *smb_fname_stream = NULL;
if (strequal(stream_info[i].name, "::$DATA")) {
continue;
}
- streamname = talloc_asprintf(talloc_tos(), "%s%s", fname,
- stream_info[i].name);
+ status = create_synthetic_smb_fname(talloc_tos(), fname,
+ stream_info[i].name, NULL,
+ &smb_fname_stream);
- if (streamname == NULL) {
+ if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("talloc_aprintf failed\n"));
- status = NT_STATUS_NO_MEMORY;
goto fail;
}
- res = SMB_VFS_UNLINK(conn, streamname);
-
- TALLOC_FREE(streamname);
+ res = SMB_VFS_UNLINK(conn, smb_fname_stream);
if (res == -1) {
status = map_nt_error_from_unix(errno);
DEBUG(10, ("Could not delete stream %s: %s\n",
- streamname, strerror(errno)));
+ smb_fname_str_dbg(smb_fname_stream),
+ strerror(errno)));
+ TALLOC_FREE(smb_fname_stream);
break;
}
+ TALLOC_FREE(smb_fname_stream);
}
fail:
connection_struct *conn = fsp->conn;
bool delete_file = false;
bool changed_user = false;
- struct share_mode_lock *lck;
- SMB_STRUCT_STAT sbuf;
+ struct share_mode_lock *lck = NULL;
NTSTATUS status = NT_STATUS_OK;
- int ret;
+ 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(fsp->conn->sconn->ev_ctx,
+ fsp->update_write_time_event,
+ timeval_current(),
+ (void *)fsp);
+ }
/*
* Lock the share entries, and determine if we should delete
* 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->fsp_name));
- return NT_STATUS_INVALID_PARAMETER;
+ "lock for file %s\n", fsp_str_dbg(fsp)));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
}
if (fsp->write_time_forced) {
- set_close_write_time(fsp, lck->changed_write_time);
- }
-
- if (!del_share_mode(lck, fsp)) {
- DEBUG(0, ("close_remove_share_mode: Could not delete share "
- "entry for file %s\n", fsp->fsp_name));
+ DEBUG(10,("close_remove_share_mode: write time forced "
+ "for file %s\n",
+ fsp_str_dbg(fsp)));
+ 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)) {
+ DEBUG(10,("close_remove_share_mode: update to current time "
+ "for file %s\n",
+ fsp_str_dbg(fsp)));
+ /* Update to current time due to "normal" write. */
+ set_close_write_time(fsp, timespec_current());
+ } else {
+ DEBUG(10,("close_remove_share_mode: write time pending "
+ "for file %s\n",
+ fsp_str_dbg(fsp)));
+ /* Update to time set on close call. */
+ set_close_write_time(fsp, fsp->close_write_time);
+ }
}
- 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
* wrote a real delete on close. */
- if (current_user.vuid != fsp->vuid) {
+ if (get_current_vuid(conn) != fsp->vuid) {
become_user(conn, fsp->vuid);
became_user = True;
}
- set_delete_on_close_lck(lck, True, ¤t_user.ut);
+ fsp->delete_on_close = true;
+ 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; i<lck->num_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; i<lck->data->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;
}
}
/* Notify any deferred opens waiting on this close. */
- notify_deferred_opens(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
- || (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;
}
*/
DEBUG(5,("close_remove_share_mode: file %s. Delete on close was set "
- "- deleting file.\n", fsp->fsp_name));
+ "- deleting file.\n", fsp_str_dbg(fsp)));
/*
* Don't try to update the write time when we delete the file
*/
fsp->update_write_time_on_close = false;
- if (!unix_token_equal(lck->delete_token, ¤t_user.ut)) {
+ 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->fsp_name,
- (unsigned int)lck->delete_token->uid));
+ fsp_str_dbg(fsp),
+ (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;
}
/* We can only delete the file if the name we have is still valid and
hasn't been renamed. */
- 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) {
+ tmp_status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(tmp_status)) {
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) ));
+ fsp_str_dbg(fsp), nt_errstr(tmp_status)));
/*
* Don't save the errno here, we ignore this error
*/
goto done;
}
- id = vfs_file_id_from_sbuf(conn, &sbuf);
+ id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
if (!file_id_equal(&fsp->file_id, &id)) {
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 ));
+ fsp_str_dbg(fsp)));
DEBUG(5,("close_remove_share_mode: file %s. stored file_id %s, "
"stat file_id %s\n",
- fsp->fsp_name,
+ fsp_str_dbg(fsp),
file_id_string_tos(&fsp->file_id),
file_id_string_tos(&id)));
/*
}
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
- && !is_ntfs_stream_name(fsp->fsp_name)) {
+ && !is_ntfs_stream_smb_fname(fsp->fsp_name)) {
- status = delete_all_streams(conn, fsp->fsp_name);
+ status = delete_all_streams(conn, fsp->fsp_name->base_name);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5, ("delete_all_streams failed: %s\n",
}
- if (SMB_VFS_UNLINK(conn,fsp->fsp_name) != 0) {
+ if (SMB_VFS_UNLINK(conn, fsp->fsp_name) != 0) {
/*
* This call can potentially fail as another smbd may
* have had the file open with delete on close set and
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) ));
+ fsp_str_dbg(fsp), strerror(errno)));
status = map_nt_error_from_unix(errno);
}
- notify_fname(conn, NOTIFY_ACTION_REMOVED,
- FILE_NOTIFY_CHANGE_FILE_NAME,
- fsp->fsp_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
* the delete on close flag. JRA.
*/
- set_delete_on_close_lck(lck, False, NULL);
+ fsp->delete_on_close = false;
+ set_delete_on_close_lck(fsp, lck, false, NULL, NULL);
done:
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;
}
if (null_timespec(ts)) {
return;
}
- /*
- * if the write time on close is explict set, then don't
- * need to fix it up to the value in the locking db
- */
fsp->write_time_forced = false;
-
fsp->update_write_time_on_close = true;
fsp->close_write_time = ts;
}
static NTSTATUS update_write_time_on_close(struct files_struct *fsp)
{
- SMB_STRUCT_STAT sbuf;
struct smb_file_time ft;
NTSTATUS status;
- int ret = -1;
+ struct share_mode_lock *lck = NULL;
- ZERO_STRUCT(sbuf);
ZERO_STRUCT(ft);
if (!fsp->update_write_time_on_close) {
}
/* Ensure we have a valid stat struct for the source. */
- if (fsp->fh->fd != -1) {
- ret = SMB_VFS_FSTAT(fsp, &sbuf);
- } else {
- if (fsp->posix_open) {
- ret = SMB_VFS_LSTAT(fsp->conn,fsp->fsp_name,&sbuf);
- } else {
- ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name,&sbuf);
- }
- }
-
- if (ret == -1) {
- return map_nt_error_from_unix(errno);
+ status = vfs_stat_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- if (!VALID_STAT(sbuf)) {
+ if (!VALID_STAT(fsp->fsp_name->st)) {
/* if it doesn't seem to be a real file */
return NT_STATUS_OK;
}
+ /*
+ * 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_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->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;
- status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name,
- &sbuf, &ft, true);
+ /* 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;
}
- return NT_STATUS_OK;
+ return status;
+}
+
+static NTSTATUS ntstatus_keeperror(NTSTATUS s1, NTSTATUS s2)
+{
+ if (!NT_STATUS_IS_OK(s1)) {
+ return s1;
+ }
+ return s2;
}
/****************************************************************************
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;
- NTSTATUS saved_status4 = NT_STATUS_OK;
+ NTSTATUS tmp;
connection_struct *conn = fsp->conn;
+ int ret;
- if (fsp->aio_write_behind) {
- /*
- * If we're finishing write behind on a close we can get a write
- * error here, we must remember this.
- */
- int ret = wait_for_aio_completion(fsp);
- if (ret) {
- saved_status1 = map_nt_error_from_unix(ret);
- }
- } else {
- cancel_aio_by_fsp(fsp);
+ /*
+ * 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));
}
-
+
/*
* If we're flushing on a close we can get a write
* error here, we must remember this.
*/
- saved_status2 = close_filestruct(fsp);
+ tmp = close_filestruct(fsp);
+ status = ntstatus_keeperror(status, tmp);
if (fsp->print_file) {
- print_fsp_end(fsp, close_type);
+ /* FIXME: return spool errors */
+ print_spool_end(fsp, close_type);
file_free(req, fsp);
return NT_STATUS_OK;
}
if (fsp->fh->ref_count == 1) {
/* Should we return on error here... ? */
- saved_status3 = close_remove_share_mode(fsp, close_type);
+ tmp = close_remove_share_mode(fsp, close_type);
+ status = ntstatus_keeperror(status, tmp);
}
- locking_close_file(smbd_messaging_context(), fsp);
+ locking_close_file(conn->sconn->msg_ctx, fsp, close_type);
- status = fd_close(fsp);
+ tmp = fd_close(fsp);
+ status = ntstatus_keeperror(status, tmp);
/* check for magic scripts */
if (close_type == NORMAL_CLOSE) {
- check_magic(fsp);
+ tmp = check_magic(fsp);
+ status = ntstatus_keeperror(status, tmp);
}
/*
* Ensure pending modtime is set after close.
*/
- saved_status4 = update_write_time_on_close(fsp);
- if (NT_STATUS_EQUAL(saved_status4, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ tmp = update_write_time_on_close(fsp);
+ if (NT_STATUS_EQUAL(tmp, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
/* Someone renamed the file or a parent directory containing
* this file. We can't do anything about this, we don't have
* an "update timestamp by fd" call in POSIX. Eat the error. */
- saved_status4 = NT_STATUS_OK;
+ tmp = NT_STATUS_OK;
}
- 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;
- } else if (!NT_STATUS_IS_OK(saved_status4)) {
- status = saved_status4;
- }
- }
+ status = ntstatus_keeperror(status, tmp);
DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
- conn->server_info->unix_name,fsp->fsp_name,
+ conn->session_info->unix_info->unix_name, fsp_str_dbg(fsp),
conn->num_files_open - 1,
nt_errstr(status) ));
file_free(req, fsp);
return status;
}
+/****************************************************************************
+ Static 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)
+{
+ const char *dname = NULL;
+ char *talloced = NULL;
+ bool ret = True;
+ long offset = 0;
+ SMB_STRUCT_STAT st;
+ struct smb_Dir *dir_hnd;
+
+ SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));
+
+ dir_hnd = OpenDir(talloc_tos(), conn, smb_dname->base_name, NULL, 0);
+ if(dir_hnd == NULL)
+ return False;
+
+ while((dname = ReadDirName(dir_hnd, &offset, &st, &talloced))) {
+ struct smb_filename *smb_dname_full = NULL;
+ char *fullname = NULL;
+ bool do_break = true;
+ NTSTATUS status;
+
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ TALLOC_FREE(talloced);
+ continue;
+ }
+
+ if (!is_visible_file(conn, smb_dname->base_name, dname, &st,
+ false)) {
+ TALLOC_FREE(talloced);
+ continue;
+ }
+
+ /* Construct the full name. */
+ fullname = talloc_asprintf(ctx,
+ "%s/%s",
+ smb_dname->base_name,
+ dname);
+ if (!fullname) {
+ errno = ENOMEM;
+ goto err_break;
+ }
+
+ status = create_synthetic_smb_fname(talloc_tos(), fullname,
+ NULL, NULL,
+ &smb_dname_full);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto err_break;
+ }
+
+ if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) {
+ goto err_break;
+ }
+
+ if(smb_dname_full->st.st_ex_mode & S_IFDIR) {
+ if(!recursive_rmdir(ctx, conn, smb_dname_full)) {
+ goto err_break;
+ }
+ if(SMB_VFS_RMDIR(conn,
+ smb_dname_full->base_name) != 0) {
+ goto err_break;
+ }
+ } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
+ goto err_break;
+ }
+
+ /* Successful iteration. */
+ do_break = false;
+
+ err_break:
+ TALLOC_FREE(smb_dname_full);
+ TALLOC_FREE(fullname);
+ TALLOC_FREE(talloced);
+ if (do_break) {
+ ret = false;
+ break;
+ }
+ }
+ TALLOC_FREE(dir_hnd);
+ return ret;
+}
+
+/****************************************************************************
+ The internals of the rmdir code - called elsewhere.
+****************************************************************************/
+
+static NTSTATUS rmdir_internals(TALLOC_CTX *ctx, files_struct *fsp)
+{
+ connection_struct *conn = fsp->conn;
+ struct smb_filename *smb_dname = fsp->fsp_name;
+ int ret;
+
+ SMB_ASSERT(!is_ntfs_stream_smb_fname(smb_dname));
+
+ /* Might be a symlink. */
+ if(SMB_VFS_LSTAT(conn, smb_dname) != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+
+ if (S_ISLNK(smb_dname->st.st_ex_mode)) {
+ /* Is what it points to a directory ? */
+ if(SMB_VFS_STAT(conn, smb_dname) != 0) {
+ return map_nt_error_from_unix(errno);
+ }
+ if (!(S_ISDIR(smb_dname->st.st_ex_mode))) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+ ret = SMB_VFS_UNLINK(conn, smb_dname);
+ } else {
+ ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
+ }
+ if (ret == 0) {
+ notify_fname(conn, NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ smb_dname->base_name);
+ return NT_STATUS_OK;
+ }
+
+ 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
+ * retry. If we fail to delete any of them (and we *don't*
+ * do a recursive delete) then fail the rmdir.
+ */
+ SMB_STRUCT_STAT st;
+ const char *dname = NULL;
+ char *talloced = NULL;
+ long dirpos = 0;
+ struct smb_Dir *dir_hnd = OpenDir(talloc_tos(), conn,
+ smb_dname->base_name, NULL,
+ 0);
+
+ if(dir_hnd == NULL) {
+ errno = ENOTEMPTY;
+ goto err;
+ }
+
+ while ((dname = ReadDirName(dir_hnd, &dirpos, &st,
+ &talloced)) != NULL) {
+ if((strcmp(dname, ".") == 0) || (strcmp(dname, "..")==0)) {
+ TALLOC_FREE(talloced);
+ continue;
+ }
+ if (!is_visible_file(conn, smb_dname->base_name, dname,
+ &st, false)) {
+ TALLOC_FREE(talloced);
+ continue;
+ }
+ if(!IS_VETO_PATH(conn, dname)) {
+ TALLOC_FREE(dir_hnd);
+ TALLOC_FREE(talloced);
+ errno = ENOTEMPTY;
+ goto err;
+ }
+ TALLOC_FREE(talloced);
+ }
+
+ /* We only have veto files/directories.
+ * Are we allowed to delete them ? */
+
+ if(!lp_recursive_veto_delete(SNUM(conn))) {
+ TALLOC_FREE(dir_hnd);
+ errno = ENOTEMPTY;
+ goto err;
+ }
+
+ /* Do a recursive delete. */
+ RewindDir(dir_hnd,&dirpos);
+ while ((dname = ReadDirName(dir_hnd, &dirpos, &st,
+ &talloced)) != NULL) {
+ struct smb_filename *smb_dname_full = NULL;
+ char *fullname = NULL;
+ bool do_break = true;
+ NTSTATUS status;
+
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ TALLOC_FREE(talloced);
+ continue;
+ }
+ if (!is_visible_file(conn, smb_dname->base_name, dname,
+ &st, false)) {
+ TALLOC_FREE(talloced);
+ continue;
+ }
+
+ fullname = talloc_asprintf(ctx,
+ "%s/%s",
+ smb_dname->base_name,
+ dname);
+
+ if(!fullname) {
+ errno = ENOMEM;
+ 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);
+ goto err_break;
+ }
+
+ if(SMB_VFS_LSTAT(conn, smb_dname_full) != 0) {
+ goto err_break;
+ }
+ if(smb_dname_full->st.st_ex_mode & S_IFDIR) {
+ if(!recursive_rmdir(ctx, conn,
+ smb_dname_full)) {
+ goto err_break;
+ }
+ if(SMB_VFS_RMDIR(conn,
+ smb_dname_full->base_name) != 0) {
+ goto err_break;
+ }
+ } else if(SMB_VFS_UNLINK(conn, smb_dname_full) != 0) {
+ goto err_break;
+ }
+
+ /* Successful iteration. */
+ do_break = false;
+
+ err_break:
+ TALLOC_FREE(fullname);
+ TALLOC_FREE(smb_dname_full);
+ TALLOC_FREE(talloced);
+ if (do_break)
+ break;
+ }
+ TALLOC_FREE(dir_hnd);
+ /* Retry the rmdir */
+ ret = SMB_VFS_RMDIR(conn, smb_dname->base_name);
+ }
+
+ err:
+
+ if (ret != 0) {
+ DEBUG(3,("rmdir_internals: couldn't remove directory %s : "
+ "%s\n", smb_fname_str_dbg(smb_dname),
+ strerror(errno)));
+ return map_nt_error_from_unix(errno);
+ }
+
+ notify_fname(conn, NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ smb_dname->base_name);
+
+ return NT_STATUS_OK;
+}
/****************************************************************************
Close a directory opened by an NT SMB call.
static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp,
enum file_close_type close_type)
{
- struct share_mode_lock *lck = 0;
+ 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;
/*
* 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->fsp_name));
- return NT_STATUS_INVALID_PARAMETER;
+ 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->fsp_name));
+ DEBUG(0, ("close_directory: Could not delete share entry for "
+ "%s\n", fsp_str_dbg(fsp)));
}
if (fsp->initial_delete_on_close) {
* directories we don't care if anyone else
* wrote a real delete on close. */
- if (current_user.vuid != fsp->vuid) {
+ if (get_current_vuid(fsp->conn) != 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);
+ 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) {
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; i<lck->num_share_modes; i++) {
- struct share_mode_entry *e = &lck->share_modes[i];
- if (is_valid_share_mode_entry(e)) {
+ 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 (share_mode_stale_pid(lck->data, i)) {
+ continue;
+ }
delete_dir = False;
break;
}
}
if ((close_type == NORMAL_CLOSE || close_type == SHUTDOWN_CLOSE) &&
- delete_dir &&
- lck->delete_token) {
+ delete_dir) {
/* Become the user who requested the delete. */
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);
- status = rmdir_internals(talloc_tos(),
- fsp->conn, fsp->fsp_name);
+ 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 - "
"deleting directory returned %s.\n",
- fsp->fsp_name, nt_errstr(status)));
+ fsp_str_dbg(fsp), nt_errstr(status)));
/* unbecome user. */
pop_sec_ctx();
fsp, NT_STATUS_OK);
}
- status = fd_close(fsp);
+ status1 = fd_close(fsp);
- if (!NT_STATUS_IS_OK(status)) {
+ if (!NT_STATUS_IS_OK(status1)) {
DEBUG(0, ("Could not close dir! fname=%s, fd=%d, err=%d=%s\n",
- fsp->fsp_name, fsp->fh->fd, errno, strerror(errno)));
+ fsp_str_dbg(fsp), fsp->fh->fd, errno,
+ strerror(errno)));
}
/*
*/
close_filestruct(fsp);
file_free(req, fsp);
+
+ out:
+ TALLOC_FREE(lck);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(status1)) {
+ status = status1;
+ }
return status;
}
{
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);
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;