#include "includes.h"
#include "system/filesys.h"
+#include "lib/util/server_id.h"
#include "printing.h"
#include "smbd/smbd.h"
#include "smbd/globals.h"
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)
{
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;
* 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;
}
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. */
}
/* 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;
/*
* 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;
}
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.
*/
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"));
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);
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)) {
}
/* 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);
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;