s3/smbd: add comments and some reformatting to open_file_ntcreate()
[samba.git] / source3 / smbd / open.c
index 2ae6f835bbcde2fd52efd2754616f9cdf389aaf2..660a5bb6f6f8142cb50a8f3c1a40fef420abb4b0 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "includes.h"
 #include "system/filesys.h"
+#include "lib/util/server_id.h"
 #include "printing.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
@@ -235,7 +236,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        return NT_STATUS_OK;
 }
 
-static NTSTATUS check_parent_access(struct connection_struct *conn,
+NTSTATUS check_parent_access(struct connection_struct *conn,
                                struct smb_filename *smb_fname,
                                uint32_t access_mask)
 {
@@ -639,7 +640,9 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
                        bool *file_created)
 {
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       NTSTATUS retry_status;
        bool file_existed = VALID_STAT(fsp->fsp_name->st);
+       int curr_flags;
 
        *file_created = false;
 
@@ -671,59 +674,65 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
         * 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).
+        * O_CREAT|O_EXCL.
+        *
+        * The big problem here is dangling symlinks. Opening
+        * without O_NOFOLLOW means both bad symlink
+        * and missing path return -1, ENOENT from open(). As POSIX
+        * is pathname based it's not possible to tell
+        * the difference between these two cases in a
+        * non-racy way, so change to try only two attempts before
+        * giving up.
+        *
+        * We don't have this problem for the O_NOFOLLOW
+        * case as it just returns NT_STATUS_OBJECT_PATH_NOT_FOUND
+        * mapped from the ELOOP POSIX error.
         */
 
-       while(1) {
-               int curr_flags = flags;
+       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;
-                       }
+       if (file_existed) {
+               curr_flags &= ~(O_CREAT);
+               retry_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       } else {
+               curr_flags |= O_EXCL;
+               retry_status = NT_STATUS_OBJECT_NAME_COLLISION;
+       }
+
+       status = fd_open(conn, fsp, curr_flags, mode);
+       if (NT_STATUS_IS_OK(status)) {
+               if (!file_existed) {
+                       *file_created = true;
                }
-               /* Create is done, or failed. */
-               break;
+               return NT_STATUS_OK;
+       }
+       if (!NT_STATUS_EQUAL(status, retry_status)) {
+               return status;
        }
+
+       curr_flags = flags;
+
+       /*
+        * Keep file_existed up to date for clarity.
+        */
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+               file_existed = false;
+               curr_flags |= O_EXCL;
+               DBG_DEBUG("file %s did not exist. Retry.\n",
+                       smb_fname_str_dbg(fsp->fsp_name));
+       } else {
+               file_existed = true;
+               curr_flags &= ~(O_CREAT);
+               DBG_DEBUG("file %s existed. Retry.\n",
+                       smb_fname_str_dbg(fsp->fsp_name));
+       }
+
+       status = fd_open(conn, fsp, curr_flags, mode);
+
+       if (NT_STATUS_IS_OK(status) && (!file_existed)) {
+               *file_created = true;
+       }
+
        return status;
 }
 
@@ -901,6 +910,25 @@ static NTSTATUS open_file(files_struct *fsp,
                        return status;
                }
 
+               if (local_flags & O_NONBLOCK) {
+                       /*
+                        * GPFS can return ETIMEDOUT for pread on
+                        * nonblocking file descriptors when files
+                        * migrated to tape need to be recalled. I
+                        * could imagine this happens elsehwere
+                        * too. With blocking file descriptors this
+                        * does not happen.
+                        */
+                       ret = set_blocking(fsp->fh->fd, true);
+                       if (ret == -1) {
+                               status = map_nt_error_from_unix(errno);
+                               DBG_WARNING("Could not set fd to blocking: "
+                                           "%s\n", strerror(errno));
+                               fd_close(fsp);
+                               return status;
+                       }
+               }
+
                ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
                if (ret == -1) {
                        /* If we have an fd, this stat should succeed. */
@@ -930,7 +958,7 @@ static NTSTATUS open_file(files_struct *fsp,
                        }
 
                        /* Change the owner if required. */
-                       if (lp_inherit_owner(SNUM(conn))) {
+                       if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
                                change_file_owner_to_parent(conn, parent_dir,
                                                            fsp);
                                need_re_stat = true;
@@ -1452,7 +1480,7 @@ static bool delay_for_oplock(files_struct *fsp,
                        /*
                         * we'll decide about SMB2_LEASE_READ later.
                         *
-                        * Maybe the break will be defered
+                        * Maybe the break will be deferred
                         */
                        break_to &= ~SMB2_LEASE_HANDLE;
                }
@@ -2255,6 +2283,12 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
        uint32_t orig_access_mask = access_mask;
        uint32_t rejected_share_access;
 
+       if (access_mask & SEC_MASK_INVALID) {
+               DBG_DEBUG("access_mask [%8x] contains invalid bits\n",
+                         access_mask);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
        /*
         * Convert GENERIC bits to specific bits.
         */
@@ -2764,9 +2798,16 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
                struct deferred_open_record state;
+               bool delay;
 
                /*
-                * EWOULDBLOCK/EAGAIN maps to NETWORK_BUSY.
+                * This handles the kernel oplock case:
+                *
+                * the file has an active kernel oplock and the open() returned
+                * EWOULDBLOCK/EAGAIN which maps to NETWORK_BUSY.
+                *
+                * "Samba locking.tdb oplocks" are handled below after acquiring
+                * the sharemode lock with get_share_mode_lock().
                 */
                if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
                        DEBUG(10, ("FIFO busy\n"));
@@ -2797,8 +2838,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        smb_panic("validate_oplock_types failed");
                }
 
-               if (delay_for_oplock(fsp, 0, lease, lck, false,
-                                    create_disposition, first_open_attempt)) {
+               delay = delay_for_oplock(fsp, 0, lease, lck, false,
+                                        create_disposition,
+                                        first_open_attempt);
+               if (delay) {
                        schedule_defer_open(lck, fsp->file_id, request_time,
                                            req);
                        TALLOC_FREE(lck);
@@ -2919,15 +2962,27 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                file_existed = true;
        }
 
-       if ((req != NULL) &&
-           delay_for_oplock(
-                   fsp, oplock_request, lease, lck,
-                   NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
-                   create_disposition, first_open_attempt)) {
-               schedule_defer_open(lck, fsp->file_id, request_time, req);
-               TALLOC_FREE(lck);
-               fd_close(fsp);
-               return NT_STATUS_SHARING_VIOLATION;
+       if (req != NULL) {
+               /*
+                * Handle oplocks, deferring the request if delay_for_oplock()
+                * triggered a break message and we have to wait for the break
+                * response.
+                */
+               bool delay;
+               bool sharing_violation = NT_STATUS_EQUAL(
+                       status, NT_STATUS_SHARING_VIOLATION);
+
+               delay = delay_for_oplock(fsp, oplock_request, lease, lck,
+                                        sharing_violation,
+                                        create_disposition,
+                                        first_open_attempt);
+               if (delay) {
+                       schedule_defer_open(lck, fsp->file_id,
+                                           request_time, req);
+                       TALLOC_FREE(lck);
+                       fd_close(fsp);
+                       return NT_STATUS_SHARING_VIOLATION;
+               }
        }
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -3375,7 +3430,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        }
 
        /* Change the owner if required. */
-       if (lp_inherit_owner(SNUM(conn))) {
+       if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
                change_dir_owner_to_parent(conn, parent_dir,
                                           smb_dname->base_name,
                                           &smb_dname->st);
@@ -4017,7 +4072,8 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        const struct dom_sid *group_sid = NULL;
        uint32_t security_info_sent = (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL);
        struct security_token *token = fsp->conn->session_info->security_token;
-       bool inherit_owner = lp_inherit_owner(SNUM(fsp->conn));
+       bool inherit_owner =
+           (lp_inherit_owner(SNUM(fsp->conn)) == INHERIT_OWNER_WINDOWS_AND_UNIX);
        bool inheritable_components = false;
        bool try_builtin_administrators = false;
        const struct dom_sid *BA_U_sid = NULL;