Unix SMB/CIFS implementation.
file closing
Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1992-2004.
+ Copyright (C) Volker Lendecke 2005
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
connection_struct *conn = fsp->conn;
int ret = 0;
- if (fsp->fd != -1) {
+ if (fsp->fh->fd != -1) {
if(flush_write_cache(fsp, CLOSE_FLUSH) == -1)
ret = -1;
return ret;
}
+/****************************************************************************
+ If any deferred opens are waiting on this close, notify them.
+****************************************************************************/
+
+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)) {
+ /*
+ * 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_smb_message(e->op_mid);
+ } else {
+ message_send_pid(e->pid, MSG_SMB_OPEN_RETRY,
+ e, sizeof(*e), True);
+ }
+ }
+}
+
/****************************************************************************
Close a file.
static int close_normal_file(files_struct *fsp, BOOL normal_close)
{
- share_mode_entry *share_entry = NULL;
- size_t share_entry_count = 0;
- BOOL delete_on_close = False;
+ BOOL delete_file = False;
connection_struct *conn = fsp->conn;
+ int saved_errno = 0;
int err = 0;
int err1 = 0;
+ struct share_mode_lock *lck;
remove_pending_lock_requests_by_fid(fsp);
+ 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_errno = ret;
+ err1 = -1;
+ }
+ } else {
+ cancel_aio_by_fsp(fsp);
+ }
+
/*
* If we're flushing on a close we can get a write
* error here, we must remember this.
*/
- if (close_filestruct(fsp) == -1)
+ if (close_filestruct(fsp) == -1) {
+ saved_errno = errno;
err1 = -1;
+ }
if (fsp->print_file) {
print_fsp_end(fsp, normal_close);
* This prevents race conditions with the file being created. JRA.
*/
- lock_share_entry_fsp(fsp);
+ lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL);
- if (fsp->delete_on_close) {
-
- /*
- * Modify the share mode entry for all files open
- * on this device and inode to tell other smbds we have
- * changed the delete on close flag. The last closer will delete the file
- * if flag is set.
- */
-
- NTSTATUS status =set_delete_on_close_over_all(fsp, fsp->delete_on_close);
- if (NT_STATUS_V(status) != NT_STATUS_V(NT_STATUS_OK))
- DEBUG(0,("close_normal_file: failed to change delete on close flag for file %s\n",
- fsp->fsp_name ));
+ if (lck == NULL) {
+ DEBUG(0, ("close_file: Could not get share mode lock for file %s\n", fsp->fsp_name));
+ return EINVAL;
}
- share_entry_count = del_share_mode(fsp, &share_entry);
-
- DEBUG(10,("close_normal_file: share_entry_count = %d for file %s\n",
- share_entry_count, fsp->fsp_name ));
-
- /*
- * We delete on close if it's the last open, and the
- * delete on close flag was set in the entry we just deleted.
- */
+ if (!del_share_mode(lck, fsp)) {
+ DEBUG(0, ("close_file: Could not delete share entry for file %s\n", fsp->fsp_name));
+ }
- if ((share_entry_count == 0) && share_entry &&
- GET_DELETE_ON_CLOSE_FLAG(share_entry->share_mode) )
- delete_on_close = True;
+ delete_file = lck->delete_on_close;
+
+ if (delete_file) {
+ int i;
+ /* See if others still have the file open. If this is the
+ * case, then don't delete */
+ for (i=0; i<lck->num_share_modes; i++) {
+ if (is_valid_share_mode_entry(&lck->share_modes[i])) {
+ delete_file = False;
+ break;
+ }
+ }
+ }
- SAFE_FREE(share_entry);
+ /* Notify any deferred opens waiting on this close. */
+ notify_deferred_opens(lck);
+ reply_to_oplock_break_requests(fsp);
/*
* NT can set delete_on_close of the last open
* reference to a file.
*/
- if (normal_close && delete_on_close) {
+ if (normal_close && delete_file) {
+ SMB_STRUCT_STAT sbuf;
+
DEBUG(5,("close_file: file %s. Delete on close was set - deleting file.\n",
fsp->fsp_name));
- 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 deleted
- * it when its last reference to this file went away. Hence
- * we log this but not at debug level zero.
- */
-
- DEBUG(5,("close_file: file %s. Delete on close was set and unlink failed \
-with error %s\n", fsp->fsp_name, strerror(errno) ));
+
+ /* We can only delete the file if the name we have
+ is still valid and hasn't been renamed. */
+
+ if(SMB_VFS_STAT(conn,fsp->fsp_name,&sbuf) != 0) {
+ DEBUG(5,("close_file: file %s. Delete on close was set "
+ "and stat failed with error %s\n",
+ fsp->fsp_name, strerror(errno) ));
+ } else {
+ if(sbuf.st_dev != fsp->dev || sbuf.st_ino != fsp->inode) {
+ DEBUG(5,("close_file: file %s. Delete on close was set and "
+ "dev and/or inode does not match\n",
+ fsp->fsp_name ));
+ DEBUG(5,("close_file: 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 ));
+
+ } else 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 deleted
+ * it when its last reference to this file went away. Hence
+ * we log this but not at debug level zero.
+ */
+
+ DEBUG(5,("close_file: file %s. Delete on close was set "
+ "and unlink failed with error %s\n",
+ fsp->fsp_name, strerror(errno) ));
+ }
+ process_pending_change_notify_queue((time_t)0);
}
- process_pending_change_notify_queue((time_t)0);
}
- unlock_share_entry_fsp(fsp);
+ talloc_free(lck);
if(fsp->oplock_type)
release_file_oplock(fsp);
err = fd_close(conn, fsp);
+ /* Only save errno if fd_close failed and we don't already
+ have an errno saved from a flush call. */
+ if ((err1 != -1) && (err == -1)) {
+ saved_errno = errno;
+ }
+
/* check for magic scripts */
if (normal_close) {
check_magic(fsp,conn);
* Ensure pending modtime is set after close.
*/
- if(fsp->pending_modtime) {
- int saved_errno = errno;
+ if(fsp->pending_modtime && fsp->pending_modtime_owner) {
set_filetime(conn, fsp->fsp_name, fsp->pending_modtime);
- errno = saved_errno;
+ } else if (fsp->last_write_time) {
+ set_filetime(conn, fsp->fsp_name, fsp->last_write_time);
}
DEBUG(2,("%s closed file %s (numopen=%d) %s\n",
- conn->user,fsp->fsp_name,
- conn->num_files_open, err ? strerror(err) : ""));
+ conn->user,fsp->fsp_name,
+ conn->num_files_open,
+ (err == -1 || err1 == -1) ? strerror(saved_errno) : ""));
if (fsp->fsp_name)
string_free(&fsp->fsp_name);
file_free(fsp);
- if (err == -1 || err1 == -1)
- return -1;
- else
+ if (err == -1 || err1 == -1) {
+ errno = saved_errno;
+ return saved_errno;
+ } else {
return 0;
+ }
}
/****************************************************************************
static int close_directory(files_struct *fsp, BOOL normal_close)
{
- remove_pending_change_notify_requests_by_fid(fsp);
+ struct share_mode_lock *lck = 0;
+ BOOL delete_dir = False;
/*
* NT can set delete_on_close of the last open
* reference to a directory also.
*/
- if (normal_close && fsp->directory_delete_on_close) {
+ 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 EINVAL;
+ }
+
+ if (!del_share_mode(lck, fsp)) {
+ DEBUG(0, ("close_directory: Could not delete share entry for %s\n", fsp->fsp_name));
+ }
+
+ delete_dir = lck->delete_on_close;
+
+ talloc_free(lck);
+
+ if (normal_close && delete_dir) {
BOOL ok = rmdir_internals(fsp->conn, fsp->fsp_name);
DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n",
fsp->fsp_name, ok ? "succeeded" : "failed" ));
* now fail as the directory has been deleted.
*/
- if(ok)
- remove_pending_change_notify_requests_by_filename(fsp);
+ if(ok) {
+ remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING);
+ remove_pending_change_notify_requests_by_filename(fsp, NT_STATUS_DELETE_PENDING);
+
+ }
process_pending_change_notify_queue((time_t)0);
+ } else {
+ remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_CANCELLED);
}
/*
*/
close_filestruct(fsp);
- if (fsp->fsp_name)
+ if (fsp->fsp_name) {
string_free(&fsp->fsp_name);
+ }
file_free(fsp);
return 0;