From 02d42be2589ff821ea9f63140694099d518f3046 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 9 Jul 2012 16:59:49 -0700 Subject: Add function fd_open_atomic() which uses O_CREAT|O_EXCL to return a guaranteed indication of creation of a new file. --- source3/smbd/open.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 4 deletions(-) (limited to 'source3/smbd') 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 @@ -533,6 +533,107 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn, return status; } +/**************************************************************************** + 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)) { -- cgit