summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/open.c111
1 files changed, 107 insertions, 4 deletions
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 473fd9769c..6f1af4b4c4 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -534,6 +534,107 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
}
/****************************************************************************
+ Open a file - returning a guaranteed ATOMIC indication of if the
+ file was created or not.
+****************************************************************************/
+
+static NTSTATUS fd_open_atomic(struct connection_struct *conn,
+ files_struct *fsp,
+ int flags,
+ mode_t mode,
+ bool *file_created)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ bool file_existed = VALID_STAT(fsp->fsp_name->st);
+
+ *file_created = false;
+
+ if (!(flags & O_CREAT)) {
+ /*
+ * We're not creating the file, just pass through.
+ */
+ return fd_open(conn, fsp, flags, mode);
+ }
+
+ if (flags & O_EXCL) {
+ /*
+ * Fail if already exists, just pass through.
+ */
+ status = fd_open(conn, fsp, flags, mode);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * Here we've opened with O_CREAT|O_EXCL
+ * and got success. We *know* we created
+ * this file.
+ */
+ *file_created = true;
+ }
+ return status;
+ }
+
+ /*
+ * Now it gets tricky. We have O_CREAT, but not O_EXCL.
+ * To know absolutely if we created the file or not,
+ * we can never call O_CREAT without O_EXCL. So if
+ * we think the file existed, try without O_CREAT|O_EXCL.
+ * If we think the file didn't exist, try with
+ * O_CREAT|O_EXCL. Keep bouncing between these two
+ * requests until either the file is created, or
+ * opened. Either way, we keep going until we get
+ * a returnable result (error, or open/create).
+ */
+
+ while(1) {
+ int curr_flags = flags;
+
+ if (file_existed) {
+ /* Just try open, do not create. */
+ curr_flags &= ~(O_CREAT);
+ status = fd_open(conn, fsp, curr_flags, mode);
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /*
+ * Someone deleted it in the meantime.
+ * Retry with O_EXCL.
+ */
+ file_existed = false;
+ DEBUG(10,("fd_open_atomic: file %s existed. "
+ "Retry.\n",
+ smb_fname_str_dbg(fsp->fsp_name)));
+ continue;
+ }
+ } else {
+ /* Try create exclusively, fail if it exists. */
+ curr_flags |= O_EXCL;
+ status = fd_open(conn, fsp, curr_flags, mode);
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_OBJECT_NAME_COLLISION)) {
+ /*
+ * Someone created it in the meantime.
+ * Retry without O_CREAT.
+ */
+ file_existed = true;
+ DEBUG(10,("fd_open_atomic: file %s "
+ "did not exist. Retry.\n",
+ smb_fname_str_dbg(fsp->fsp_name)));
+ continue;
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * Here we've opened with O_CREAT|O_EXCL
+ * and got success. We *know* we created
+ * this file.
+ */
+ *file_created = true;
+ }
+ }
+ /* Create is done, or failed. */
+ break;
+ }
+ return status;
+}
+
+/****************************************************************************
Open a file.
****************************************************************************/
@@ -544,7 +645,8 @@ static NTSTATUS open_file(files_struct *fsp,
int flags,
mode_t unx_mode,
uint32 access_mask, /* client requested access mask. */
- uint32 open_access_mask) /* what we're actually using in the open. */
+ uint32 open_access_mask, /* what we're actually using in the open. */
+ bool *p_file_created)
{
struct smb_filename *smb_fname = fsp->fsp_name;
NTSTATUS status = NT_STATUS_OK;
@@ -670,7 +772,8 @@ static NTSTATUS open_file(files_struct *fsp,
}
/* Actually do the open */
- status = fd_open(conn, fsp, local_flags, unx_mode);
+ status = fd_open_atomic(conn, fsp, local_flags,
+ unx_mode, p_file_created);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
"(flags=%d)\n", smb_fname_str_dbg(smb_fname),
@@ -690,7 +793,7 @@ static NTSTATUS open_file(files_struct *fsp,
return status;
}
- if ((local_flags & O_CREAT) && !file_existed) {
+ if (*p_file_created) {
/* We created this file. */
bool need_re_stat = false;
@@ -2236,7 +2339,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
fsp_open = open_file(fsp, conn, req, parent_dir,
flags|flags2, unx_mode, access_mask,
- open_access_mask);
+ open_access_mask, &new_file_created);
if (!NT_STATUS_IS_OK(fsp_open)) {
if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {