char *parent_dir = NULL;
struct smb_filename parent_dir_fname = {0};
const char *final_component = NULL;
- bool is_directory = false;
bool ok;
-#ifdef O_DIRECTORY
- if (flags & O_DIRECTORY) {
- is_directory = true;
- }
-#endif
-
- if (is_directory) {
+ if (fsp->is_directory) {
parent_dir = talloc_strdup(talloc_tos(), smb_fname->base_name);
if (parent_dir == NULL) {
saved_errno = errno;
* too. With blocking file descriptors this
* does not happen.
*/
- ret = set_blocking(fsp->fh->fd, true);
+ ret = vfs_set_blocking(fsp, true);
if (ret == -1) {
status = map_nt_error_from_unix(errno);
DBG_WARNING("Could not set fd to blocking: "
fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
fsp->file_pid = req ? req->smbpid : 0;
- fsp->can_lock = True;
- fsp->can_read = ((access_mask & FILE_READ_DATA) != 0);
- fsp->can_write =
+ fsp->fsp_flags.can_lock = true;
+ fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) != 0);
+ fsp->fsp_flags.can_write =
CAN_WRITE(conn) &&
((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
fsp->print_file = NULL;
fsp->aio_write_behind = True;
}
- fsp->wcp = NULL; /* Write cache pointer. */
-
DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
conn->session_info->unix_info->unix_name,
smb_fname_str_dbg(smb_fname),
- BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+ BOOLSTR(fsp->fsp_flags.can_read),
+ BOOLSTR(fsp->fsp_flags.can_write),
conn->num_files_open));
errno = 0;
struct validate_my_share_entries_state *state = private_data;
files_struct *fsp;
- if (!serverid_equal(&state->self, &e->pid)) {
+ if (!server_id_equal(&state->self, &e->pid)) {
return false;
}
return state.ret;
}
+static void share_mode_flags_get(
+ uint16_t flags,
+ uint32_t *access_mask,
+ uint32_t *share_mode,
+ uint32_t *lease_type)
+{
+ if (access_mask != NULL) {
+ *access_mask =
+ ((flags & SHARE_MODE_ACCESS_READ) ?
+ FILE_READ_DATA : 0) |
+ ((flags & SHARE_MODE_ACCESS_WRITE) ?
+ FILE_WRITE_DATA : 0) |
+ ((flags & SHARE_MODE_ACCESS_DELETE) ?
+ DELETE_ACCESS : 0);
+ }
+ if (share_mode != NULL) {
+ *share_mode =
+ ((flags & SHARE_MODE_SHARE_READ) ?
+ FILE_SHARE_READ : 0) |
+ ((flags & SHARE_MODE_SHARE_WRITE) ?
+ FILE_SHARE_WRITE : 0) |
+ ((flags & SHARE_MODE_SHARE_DELETE) ?
+ FILE_SHARE_DELETE : 0);
+ }
+ if (lease_type != NULL) {
+ *lease_type =
+ ((flags & SHARE_MODE_LEASE_READ) ?
+ SMB2_LEASE_READ : 0) |
+ ((flags & SHARE_MODE_LEASE_WRITE) ?
+ SMB2_LEASE_WRITE : 0) |
+ ((flags & SHARE_MODE_LEASE_HANDLE) ?
+ SMB2_LEASE_HANDLE : 0);
+ }
+}
+
+static uint16_t share_mode_flags_set(
+ uint16_t flags,
+ uint32_t access_mask,
+ uint32_t share_mode,
+ uint32_t lease_type)
+{
+ if (access_mask != UINT32_MAX) {
+ flags &= ~(SHARE_MODE_ACCESS_READ|
+ SHARE_MODE_ACCESS_WRITE|
+ SHARE_MODE_ACCESS_DELETE);
+ flags |= (access_mask & (FILE_READ_DATA | FILE_EXECUTE)) ?
+ SHARE_MODE_ACCESS_READ : 0;
+ flags |= (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ?
+ SHARE_MODE_ACCESS_WRITE : 0;
+ flags |= (access_mask & (DELETE_ACCESS)) ?
+ SHARE_MODE_ACCESS_DELETE : 0;
+ }
+ if (share_mode != UINT32_MAX) {
+ flags &= ~(SHARE_MODE_SHARE_READ|
+ SHARE_MODE_SHARE_WRITE|
+ SHARE_MODE_SHARE_DELETE);
+ flags |= (share_mode & FILE_SHARE_READ) ?
+ SHARE_MODE_SHARE_READ : 0;
+ flags |= (share_mode & FILE_SHARE_WRITE) ?
+ SHARE_MODE_SHARE_WRITE : 0;
+ flags |= (share_mode & FILE_SHARE_DELETE) ?
+ SHARE_MODE_SHARE_DELETE : 0;
+ }
+ if (lease_type != UINT32_MAX) {
+ flags &= ~(SHARE_MODE_LEASE_READ|
+ SHARE_MODE_LEASE_WRITE|
+ SHARE_MODE_LEASE_HANDLE);
+ flags |= (lease_type & SMB2_LEASE_READ) ?
+ SHARE_MODE_LEASE_READ : 0;
+ flags |= (lease_type & SMB2_LEASE_WRITE) ?
+ SHARE_MODE_LEASE_WRITE : 0;
+ flags |= (lease_type & SMB2_LEASE_HANDLE) ?
+ SHARE_MODE_LEASE_HANDLE : 0;
+ }
+
+ return flags;
+}
+
+static uint16_t share_mode_flags_restrict(
+ uint16_t flags,
+ uint32_t access_mask,
+ uint32_t share_mode,
+ uint32_t lease_type)
+{
+ uint32_t existing_access_mask, existing_share_mode;
+ uint32_t existing_lease_type;
+ uint16_t ret;
+
+ share_mode_flags_get(
+ flags,
+ &existing_access_mask,
+ &existing_share_mode,
+ &existing_lease_type);
+
+ existing_access_mask |= access_mask;
+ existing_share_mode &= share_mode;
+ existing_lease_type |= lease_type;
+
+ ret = share_mode_flags_set(
+ flags,
+ existing_access_mask,
+ existing_share_mode,
+ existing_lease_type);
+ return ret;
+}
+
/****************************************************************************
Deal with share modes
Invariant: Share mode must be locked on entry and exit.
****************************************************************************/
struct open_mode_check_state {
+ struct file_id fid;
uint32_t access_mask;
uint32_t share_access;
- bool conflict;
+ uint32_t lease_type;
};
static bool open_mode_check_fn(
void *private_data)
{
struct open_mode_check_state *state = private_data;
- bool disconnected, conflict, stale;
+ bool disconnected, stale;
+ uint32_t access_mask, share_access, lease_type;
disconnected = server_id_is_disconnected(&e->pid);
if (disconnected) {
return false;
}
- conflict = share_conflict(
- e->access_mask,
- e->share_access,
- state->access_mask,
- state->share_access);
- if (!conflict) {
+ access_mask = state->access_mask | e->access_mask;
+ share_access = state->share_access & e->share_access;
+ lease_type = state->lease_type | get_lease_type(e, state->fid);
+
+ if ((access_mask == state->access_mask) &&
+ (share_access == state->share_access) &&
+ (lease_type == state->lease_type)) {
return false;
}
+
stale = share_entry_stale_pid(e);
if (stale) {
return false;
}
- state->conflict = true;
- return true;
+ state->access_mask = access_mask;
+ state->share_access = share_access;
+ state->lease_type = lease_type;
+
+ return false;
}
static NTSTATUS open_mode_check(connection_struct *conn,
uint32_t access_mask,
uint32_t share_access)
{
- struct open_mode_check_state state = {
- .access_mask = access_mask, .share_access = share_access,
- };
- bool ok;
+ struct share_mode_data *d = lck->data;
+ struct open_mode_check_state state;
+ uint16_t new_flags;
+ bool ok, conflict, have_share_entries;
if (is_stat_open(access_mask)) {
/* Stat open that doesn't trigger oplock breaks or share mode
{
struct validate_my_share_entries_state validate_state = {
.sconn = conn->sconn,
- .fid = lck->data->id,
+ .fid = d->id,
.self = messaging_server_id(conn->sconn->msg_ctx),
};
ok = share_mode_forall_entries(
}
#endif
+ have_share_entries = share_mode_have_entries(lck);
+ if (!have_share_entries) {
+ /*
+ * This is a fresh share mode lock where no conflicts
+ * can happen.
+ */
+ return NT_STATUS_OK;
+ }
+
+ share_mode_flags_get(
+ d->flags, &state.access_mask, &state.share_access, NULL);
+
+ conflict = share_conflict(
+ state.access_mask,
+ state.share_access,
+ access_mask,
+ share_access);
+ if (!conflict) {
+ DBG_DEBUG("No conflict due to share_mode_flags access\n");
+ return NT_STATUS_OK;
+ }
+
+ state = (struct open_mode_check_state) {
+ .fid = d->id,
+ .share_access = (FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE),
+ };
+
+ /*
+ * Walk the share mode array to recalculate d->flags
+ */
+
ok = share_mode_forall_entries(lck, open_mode_check_fn, &state);
if (!ok) {
DBG_DEBUG("share_mode_forall_entries failed\n");
return NT_STATUS_INTERNAL_ERROR;
}
- if (state.conflict) {
+
+ new_flags = share_mode_flags_set(
+ 0, state.access_mask, state.share_access, state.lease_type);
+ if (new_flags == d->flags) {
+ /*
+ * We only end up here if we had a sharing violation
+ * from d->flags and have recalculated it.
+ */
return NT_STATUS_SHARING_VIOLATION;
}
- return NT_STATUS_OK;
+ d->flags = new_flags;
+ d->modified = true;
+
+ conflict = share_conflict(
+ state.access_mask,
+ state.share_access,
+ access_mask,
+ share_access);
+ if (!conflict) {
+ DBG_DEBUG("No conflict due to share_mode_flags access\n");
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_SHARING_VIOLATION;
}
/*
return false;
}
- remove_stale_share_mode_entries(lck->data);
-
if ((state.batch || state.ex_or_batch) &&
(state.num_non_stat_opens != 1)) {
DBG_WARNING("got batch (%d) or ex (%d) non-exclusively "
return result;
}
+struct delay_for_oplock_state {
+ struct files_struct *fsp;
+ const struct smb2_lease *lease;
+ bool will_overwrite;
+ uint32_t delay_mask;
+ bool first_open_attempt;
+ bool got_handle_lease;
+ bool got_oplock;
+ bool have_other_lease;
+ bool delay;
+};
+
+static bool delay_for_oplock_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct delay_for_oplock_state *state = private_data;
+ struct files_struct *fsp = state->fsp;
+ const struct smb2_lease *lease = state->lease;
+ bool e_is_lease = (e->op_type == LEASE_OPLOCK);
+ uint32_t e_lease_type = get_lease_type(e, fsp->file_id);
+ uint32_t break_to;
+ bool lease_is_breaking = false;
+
+ if (e_is_lease) {
+ NTSTATUS status;
+
+ if (lease != NULL) {
+ bool our_lease = is_same_lease(fsp, e, lease);
+ if (our_lease) {
+ DBG_DEBUG("Ignoring our own lease\n");
+ return false;
+ }
+ }
+
+ status = leases_db_get(
+ &e->client_guid,
+ &e->lease_key,
+ &fsp->file_id,
+ NULL, /* current_state */
+ &lease_is_breaking,
+ NULL, /* breaking_to_requested */
+ NULL, /* breaking_to_required */
+ NULL, /* lease_version */
+ NULL); /* epoch */
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+ }
+
+ if (!state->got_handle_lease &&
+ ((e_lease_type & SMB2_LEASE_HANDLE) != 0) &&
+ !share_entry_stale_pid(e)) {
+ state->got_handle_lease = true;
+ }
+
+ if (!state->got_oplock &&
+ (e->op_type != LEASE_OPLOCK) &&
+ !share_entry_stale_pid(e)) {
+ state->got_oplock = true;
+ }
+
+ if (!state->have_other_lease &&
+ !is_same_lease(fsp, e, lease) &&
+ !share_entry_stale_pid(e)) {
+ state->have_other_lease = true;
+ }
+
+ break_to = e_lease_type & ~state->delay_mask;
+
+ if (state->will_overwrite) {
+ break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_READ);
+ }
+
+ DBG_DEBUG("e_lease_type %u, will_overwrite: %u\n",
+ (unsigned)e_lease_type,
+ (unsigned)state->will_overwrite);
+
+ if ((e_lease_type & ~break_to) == 0) {
+ if (lease_is_breaking) {
+ state->delay = true;
+ }
+ return false;
+ }
+
+ if (share_entry_stale_pid(e)) {
+ return false;
+ }
+
+ if (state->will_overwrite) {
+ /*
+ * If we break anyway break to NONE directly.
+ * Otherwise vfs_set_filelen() will trigger the
+ * break.
+ */
+ break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
+ }
+
+ if (!e_is_lease) {
+ /*
+ * Oplocks only support breaking to R or NONE.
+ */
+ break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+ }
+
+ DBG_DEBUG("breaking from %d to %d\n",
+ (int)e_lease_type,
+ (int)break_to);
+ send_break_message(
+ fsp->conn->sconn->msg_ctx, &fsp->file_id, e, break_to);
+ if (e_lease_type & state->delay_mask) {
+ state->delay = true;
+ }
+ if (lease_is_breaking && !state->first_open_attempt) {
+ state->delay = true;
+ }
+
+ return false;
+};
+
static NTSTATUS delay_for_oplock(files_struct *fsp,
int oplock_request,
const struct smb2_lease *lease,
uint32_t create_disposition,
bool first_open_attempt)
{
- struct share_mode_data *d = lck->data;
- uint32_t i;
- bool delay = false;
- bool will_overwrite;
- const uint32_t delay_mask = have_sharing_violation ?
- SMB2_LEASE_HANDLE : SMB2_LEASE_WRITE;
- bool got_handle_lease = false;
- bool got_oplock = false;
- bool have_other_lease = false;
+ struct delay_for_oplock_state state = {
+ .fsp = fsp,
+ .lease = lease,
+ .first_open_attempt = first_open_attempt,
+ };
uint32_t granted;
NTSTATUS status;
+ bool ok;
if (is_stat_open(fsp->access_mask)) {
goto grant;
}
+ state.delay_mask = have_sharing_violation ?
+ SMB2_LEASE_HANDLE : SMB2_LEASE_WRITE;
+
switch (create_disposition) {
case FILE_SUPERSEDE:
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
- will_overwrite = true;
+ state.will_overwrite = true;
break;
default:
- will_overwrite = false;
+ state.will_overwrite = false;
break;
}
- for (i=0; i<d->num_share_modes; i++) {
- struct share_mode_entry *e = &d->share_modes[i];
- bool e_is_lease = (e->op_type == LEASE_OPLOCK);
- uint32_t e_lease_type = get_lease_type(d, e);
- uint32_t break_to;
- bool lease_is_breaking = false;
-
- if (e_is_lease) {
- if (lease != NULL) {
- bool our_lease = is_same_lease(fsp, e, lease);
- if (our_lease) {
- DBG_DEBUG("Ignoring our own lease\n");
- continue;
- }
- }
-
- status = leases_db_get(
- &e->client_guid,
- &e->lease_key,
- &fsp->file_id,
- NULL, /* current_state */
- &lease_is_breaking,
- NULL, /* breaking_to_requested */
- NULL, /* breaking_to_required */
- NULL, /* lease_version */
- NULL); /* epoch */
- SMB_ASSERT(NT_STATUS_IS_OK(status));
- }
-
- if (!got_handle_lease &&
- ((e_lease_type & SMB2_LEASE_HANDLE) != 0) &&
- !share_mode_stale_pid(d, i)) {
- got_handle_lease = true;
- }
-
- if (!got_oplock &&
- (e->op_type != LEASE_OPLOCK) &&
- !share_mode_stale_pid(d, i)) {
- got_oplock = true;
- }
-
- if (!have_other_lease &&
- !is_same_lease(fsp, e, lease) &&
- !share_mode_stale_pid(d, i)) {
- have_other_lease = true;
- }
-
- break_to = e_lease_type & ~delay_mask;
-
- if (will_overwrite) {
- break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_READ);
- }
-
- DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
- (unsigned)i, (unsigned)e_lease_type,
- (unsigned)will_overwrite));
-
- if ((e_lease_type & ~break_to) == 0) {
- if (lease_is_breaking) {
- delay = true;
- }
- continue;
- }
-
- if (share_mode_stale_pid(d, i)) {
- continue;
- }
-
- if (will_overwrite) {
- /*
- * If we break anyway break to NONE directly.
- * Otherwise vfs_set_filelen() will trigger the
- * break.
- */
- break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
- }
-
- if (!e_is_lease) {
- /*
- * Oplocks only support breaking to R or NONE.
- */
- break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
- }
-
- DEBUG(10, ("breaking from %d to %d\n",
- (int)e_lease_type, (int)break_to));
- send_break_message(fsp->conn->sconn->msg_ctx, &fsp->file_id,
- e, break_to);
- if (e_lease_type & delay_mask) {
- delay = true;
- }
- if (lease_is_breaking && !first_open_attempt) {
- delay = true;
- }
+ ok = share_mode_forall_entries(lck, delay_for_oplock_fn, &state);
+ if (!ok) {
+ return NT_STATUS_INTERNAL_ERROR;
}
- if (delay) {
+ if (state.delay) {
return NT_STATUS_RETRY;
}
granted &= ~SMB2_LEASE_READ;
}
- if (have_other_lease) {
+ if (state.have_other_lease) {
/*
* Can grant only one writer
*/
}
if (oplock_request == LEASE_OPLOCK) {
- if (got_oplock) {
+ if (state.got_oplock) {
granted &= ~SMB2_LEASE_HANDLE;
}
DBG_DEBUG("lease_state=%d\n", fsp->lease->lease.lease_state);
} else {
- if (got_handle_lease) {
+ if (state.got_handle_lease) {
granted = SMB2_LEASE_NONE;
}
}
}
- if (granted & SMB2_LEASE_READ) {
- lck->data->flags |= SHARE_MODE_HAS_READ_LEASE;
+ if ((granted & SMB2_LEASE_READ) &&
+ ((lck->data->flags & SHARE_MODE_LEASE_READ) == 0)) {
+ lck->data->flags |= SHARE_MODE_LEASE_READ;
+ lck->data->modified = true;
}
DBG_DEBUG("oplock type 0x%x on file %s\n",
static void defer_open(struct share_mode_lock *lck,
struct timeval timeout,
struct smb_request *req,
- bool delayed_for_oplocks,
struct file_id id)
{
struct deferred_open_record *open_rec = NULL;
abs_timeout = timeval_sum(&req->request_time, &timeout);
DBG_DEBUG("request time [%s] timeout [%s] mid [%" PRIu64 "] "
- "delayed_for_oplocks [%s] file_id [%s]\n",
+ "file_id [%s]\n",
timeval_str_buf(&req->request_time, false, true, &tvbuf1),
timeval_str_buf(&abs_timeout, false, true, &tvbuf2),
req->mid,
- delayed_for_oplocks ? "yes" : "no",
file_id_str_buf(id, &fbuf));
open_rec = talloc_zero(NULL, struct deferred_open_record);
DBG_DEBUG("defering mid %" PRIu64 "\n", req->mid);
- watch_req = dbwrap_watched_watch_send(watch_state,
- req->sconn->ev_ctx,
- lck->data->record,
- (struct server_id){0});
+ watch_req = share_mode_watch_send(
+ watch_state,
+ req->sconn->ev_ctx,
+ lck->data->id,
+ (struct server_id){0});
if (watch_req == NULL) {
exit_server("Could not watch share mode record");
}
NTSTATUS status;
bool ret;
- status = dbwrap_watched_watch_recv(req, NULL, NULL);
+ status = share_mode_watch_recv(req, NULL, NULL);
TALLOC_FREE(req);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
}
if (lck != NULL) {
- open_rec->watch_req = dbwrap_watched_watch_send(
+ open_rec->watch_req = share_mode_watch_send(
open_rec,
req->sconn->ev_ctx,
- lck->data->record,
+ lck->data->id,
(struct server_id) {0});
if (open_rec->watch_req == NULL) {
- DBG_WARNING("dbwrap_watched_watch_send failed\n");
+ DBG_WARNING("share_mode_watch_send failed\n");
TALLOC_FREE(open_rec);
return false;
}
NTSTATUS status;
bool ok;
- status = dbwrap_watched_watch_recv(subreq, NULL, NULL);
+ status = share_mode_watch_recv(subreq, NULL, NULL);
TALLOC_FREE(subreq);
DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
nt_errstr(status));
return;
}
- defer_open(lck, timeout, req, true, id);
+ defer_open(lck, timeout, req, id);
}
/****************************************************************************
SMB_STRUCT_STAT saved_stat = smb_fname->st;
struct timespec old_write_time;
struct file_id id;
+ bool setup_poll = false;
bool ok;
if (conn->printer) {
return NT_STATUS_ACCESS_DENIED;
}
- fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+ if (VALID_STAT(smb_fname->st)) {
+ /*
+ * Only try and create a file id before open
+ * for an existing file. For a file being created
+ * this won't do anything useful until the file
+ * exists and has a valid stat struct.
+ */
+ fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+ }
fsp->fh->private_options = private_flags;
fsp->access_mask = open_access_mask; /* We change this to the
* requested access_mask after
open_access_mask, &new_file_created);
if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
+ if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
+ DEBUG(10, ("FIFO busy\n"));
+ return NT_STATUS_NETWORK_BUSY;
+ }
+ if (req == NULL) {
+ DEBUG(10, ("Internal open busy\n"));
+ return NT_STATUS_NETWORK_BUSY;
+ }
/*
* This handles the kernel oplock case:
*
* "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"));
- return NT_STATUS_NETWORK_BUSY;
- }
- if (req == NULL) {
- DEBUG(10, ("Internal open busy\n"));
- return NT_STATUS_NETWORK_BUSY;
- }
+ setup_poll = true;
+ }
+ if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
+ /*
+ * EINTR from the open(2) syscall. Just setup a retry
+ * in a bit. We can't use the sys_write() tight retry
+ * loop here, as we might have to actually deal with
+ * lease-break signals to avoid a deadlock.
+ */
+ setup_poll = true;
+ }
+
+ if (setup_poll) {
/*
* From here on we assume this is an oplock break triggered
*/
}
if (!NT_STATUS_IS_OK(fsp_open)) {
- if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
+ bool wait_for_aio = NT_STATUS_EQUAL(
+ fsp_open, NT_STATUS_MORE_PROCESSING_REQUIRED);
+ if (wait_for_aio) {
schedule_async_open(req);
}
return fsp_open;
return status;
}
+ {
+ struct share_mode_data *d = lck->data;
+ uint16_t new_flags = share_mode_flags_restrict(
+ d->flags, access_mask, share_access, UINT32_MAX);
+
+ if (new_flags != d->flags) {
+ d->flags = new_flags;
+ d->modified = true;
+ }
+ }
+
ok = set_share_mode(
lck,
fsp,
if (!ok) {
if (fsp->oplock_type == LEASE_OPLOCK) {
status = remove_lease_if_stale(
- lck->data,
+ lck,
fsp_client_guid(fsp),
&fsp->lease->lease.lease_key);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_SHARING_VIOLATION;
}
- fsp->kernel_share_modes_taken = true;
+ fsp->fsp_flags.kernel_share_modes_taken = true;
}
/*
fsp->initial_delete_on_close = True;
}
- if (info == FILE_WAS_CREATED) {
+ /*
+ * If we created a file and it's not a stream, this is the point where
+ * we set the itime (aka invented time) that get's stored in the DOS
+ * attribute xattr. The value is going to be either what the filesystem
+ * provided or a copy of the creation date.
+ *
+ * Either way, we turn the itime into a File-ID, unless the filesystem
+ * provided one (unlikely).
+ */
+ if (info == FILE_WAS_CREATED && !is_named_stream(smb_fname)) {
smb_fname->st.st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_ITIME;
if (lp_store_dos_attributes(SNUM(conn)) &&
/* Overwritten files should be initially set as archive */
if ((info == FILE_WAS_OVERWRITTEN && lp_map_archive(SNUM(conn))) ||
lp_store_dos_attributes(SNUM(conn))) {
+ (void)dos_mode(conn, smb_fname);
if (!posix_open) {
if (file_set_dosmode(conn, smb_fname,
new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE,
*/
struct timespec write_time = get_share_mode_write_time(lck);
- if (!null_timespec(write_time)) {
+ if (!is_omit_timespec(&write_time)) {
update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
}
}
struct smb_filename *smb_dname,
uint32_t file_attributes)
{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
mode_t mode;
char *parent_dir = NULL;
NTSTATUS status;
if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) {
DEBUG(5,("mkdir_internal: failing share access "
- "%s\n", lp_servicename(talloc_tos(), SNUM(conn))));
+ "%s\n", lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
return NT_STATUS_ACCESS_DENIED;
}
files_struct **result)
{
files_struct *fsp = NULL;
- bool dir_existed = VALID_STAT(smb_dname->st) ? True : False;
+ bool dir_existed = VALID_STAT(smb_dname->st);
struct share_mode_lock *lck = NULL;
NTSTATUS status;
struct timespec mtimespec;
file_attributes |= FILE_ATTRIBUTE_DIRECTORY;
}
- DEBUG(5,("open_directory: opening directory %s, access_mask = 0x%x, "
- "share_access = 0x%x create_options = 0x%x, "
- "create_disposition = 0x%x, file_attributes = 0x%x\n",
+ DBG_INFO("opening directory %s, access_mask = 0x%"PRIx32", "
+ "share_access = 0x%"PRIx32" create_options = 0x%"PRIx32", "
+ "create_disposition = 0x%"PRIx32", "
+ "file_attributes = 0x%"PRIx32"\n",
smb_fname_str_dbg(smb_dname),
- (unsigned int)access_mask,
- (unsigned int)share_access,
- (unsigned int)create_options,
- (unsigned int)create_disposition,
- (unsigned int)file_attributes));
+ access_mask,
+ share_access,
+ create_options,
+ create_disposition,
+ file_attributes);
status = smbd_calculate_access_mask(conn, smb_dname, false,
access_mask, &access_mask);
fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_dname->st);
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
fsp->file_pid = req ? req->smbpid : 0;
- fsp->can_lock = False;
- fsp->can_read = False;
- fsp->can_write = False;
+ fsp->fsp_flags.can_lock = false;
+ fsp->fsp_flags.can_read = false;
+ fsp->fsp_flags.can_write = false;
fsp->fh->private_options = 0;
/*
/* Don't store old timestamps for directory
handles in the internal database. We don't
update them in there if new objects
- are creaded in the directory. Currently
+ are created in the directory. Currently
we only update timestamps on file writes.
See bug #9870.
*/
- ZERO_STRUCT(mtimespec);
+ mtimespec = make_omit_timespec();
#ifdef O_DIRECTORY
status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
return status;
}
+ {
+ struct share_mode_data *d = lck->data;
+ uint16_t new_flags = share_mode_flags_restrict(
+ d->flags, access_mask, share_access, UINT32_MAX);
+
+ if (new_flags != d->flags) {
+ d->flags = new_flags;
+ d->modified = true;
+ }
+ }
+
ok = set_share_mode(
lck,
fsp,
*/
struct timespec write_time = get_share_mode_write_time(lck);
- if (!null_timespec(write_time)) {
+ if (!is_omit_timespec(&write_time)) {
update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
}
}
return;
}
+struct lease_match_break_state {
+ struct messaging_context *msg_ctx;
+ const struct smb2_lease_key *lease_key;
+ struct file_id id;
+
+ bool found_lease;
+ uint16_t version;
+ uint16_t epoch;
+};
+
+static bool lease_match_break_fn(
+ struct share_mode_entry *e,
+ void *private_data)
+{
+ struct lease_match_break_state *state = private_data;
+ bool stale, equal;
+ uint32_t e_lease_type;
+ NTSTATUS status;
+
+ stale = share_entry_stale_pid(e);
+ if (stale) {
+ return false;
+ }
+
+ equal = smb2_lease_key_equal(&e->lease_key, state->lease_key);
+ if (!equal) {
+ return false;
+ }
+
+ status = leases_db_get(
+ &e->client_guid,
+ &e->lease_key,
+ &state->id,
+ NULL, /* current_state */
+ NULL, /* breaking */
+ NULL, /* breaking_to_requested */
+ NULL, /* breaking_to_required */
+ &state->version, /* lease_version */
+ &state->epoch); /* epoch */
+ if (NT_STATUS_IS_OK(status)) {
+ state->found_lease = true;
+ } else {
+ DBG_WARNING("Could not find version/epoch: %s\n",
+ nt_errstr(status));
+ }
+
+ e_lease_type = get_lease_type(e, state->id);
+ if (e_lease_type == SMB2_LEASE_NONE) {
+ return false;
+ }
+ send_break_message(state->msg_ctx, &state->id, e, SMB2_LEASE_NONE);
+
+ /*
+ * Windows 7 and 8 lease clients are broken in that they will
+ * not respond to lease break requests whilst waiting for an
+ * outstanding open request on that lease handle on the same
+ * TCP connection, due to holding an internal inode lock.
+ *
+ * This means we can't reschedule ourselves here, but must
+ * return from the create.
+ *
+ * Work around:
+ *
+ * Send the breaks and then return SMB2_LEASE_NONE in the
+ * lease handle to cause them to acknowledge the lease
+ * break. Consultation with Microsoft engineering confirmed
+ * this approach is safe.
+ */
+
+ return false;
+}
+
static NTSTATUS lease_match(connection_struct *conn,
struct smb_request *req,
const struct smb2_lease_key *lease_key,
/* We have to break all existing leases. */
for (i = 0; i < state.num_file_ids; i++) {
+ struct lease_match_break_state break_state = {
+ .msg_ctx = conn->sconn->msg_ctx,
+ .lease_key = lease_key,
+ };
struct share_mode_lock *lck;
- struct share_mode_data *d;
- struct share_mode_entry *lease_entry = NULL;
- uint32_t j;
+ bool ok;
if (file_id_equal(&state.ids[i], &state.id)) {
/* Don't need to break our own file. */
continue;
}
- lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]);
+ break_state.id = state.ids[i];
+
+ lck = get_existing_share_mode_lock(
+ talloc_tos(), break_state.id);
if (lck == NULL) {
/* Race condition - file already closed. */
continue;
}
- d = lck->data;
- for (j=0; j<d->num_share_modes; j++) {
- struct share_mode_entry *e = &d->share_modes[j];
- uint32_t e_lease_type = get_lease_type(d, e);
- if (share_mode_stale_pid(d, j)) {
- continue;
- }
-
- if (e->op_type == LEASE_OPLOCK) {
- if (!smb2_lease_key_equal(&e->lease_key,
- lease_key)) {
- continue;
- }
- lease_entry = e;
- }
-
- if (e_lease_type == SMB2_LEASE_NONE) {
- continue;
- }
-
- send_break_message(conn->sconn->msg_ctx, &d->id, e,
- SMB2_LEASE_NONE);
-
- /*
- * Windows 7 and 8 lease clients
- * are broken in that they will not
- * respond to lease break requests
- * whilst waiting for an outstanding
- * open request on that lease handle
- * on the same TCP connection, due
- * to holding an internal inode lock.
- *
- * This means we can't reschedule
- * ourselves here, but must return
- * from the create.
- *
- * Work around:
- *
- * Send the breaks and then return
- * SMB2_LEASE_NONE in the lease handle
- * to cause them to acknowledge the
- * lease break. Consultation with
- * Microsoft engineering confirmed
- * this approach is safe.
- */
-
- }
-
- if (lease_entry != NULL) {
- status = leases_db_get(
- &lease_entry->client_guid,
- &lease_entry->lease_key,
- &d->id,
- NULL, /* current_state */
- NULL, /* breaking */
- NULL, /* breaking_to_requested */
- NULL, /* breaking_to_required */
- p_version, /* lease_version */
- p_epoch); /* epoch */
- if (!NT_STATUS_IS_OK(status)) {
- DBG_WARNING("Could not find version/epoch: "
- "%s\n",
- nt_errstr(status));
- }
+ ok = share_mode_forall_leases(
+ lck, lease_match_break_fn, &break_state);
+ if (!ok) {
+ DBG_DEBUG("share_mode_forall_leases failed\n");
+ continue;
}
TALLOC_FREE(lck);
+
+ if (break_state.found_lease) {
+ *p_version = break_state.version;
+ *p_epoch = break_state.epoch;
+ }
}
/*
* Ensure we don't grant anything more so we
files_struct *fsp = NULL;
NTSTATUS status;
bool stream_name = false;
+ struct smb2_create_blob *posx = NULL;
DBG_DEBUG("create_file: access_mask = 0x%x "
"file_attributes = 0x%x, share_access = 0x%x, "
}
}
+ posx = smb2_create_blob_find(
+ in_context_blobs, SMB2_CREATE_TAG_POSIX);
+ if (posx != NULL) {
+ uint32_t wire_mode_bits = 0;
+ mode_t mode_bits = 0;
+ SMB_STRUCT_STAT sbuf = { 0 };
+ enum perm_type ptype =
+ (create_options & FILE_DIRECTORY_FILE) ?
+ PERM_NEW_DIR : PERM_NEW_FILE;
+
+ if (posx->data.length != 4) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ wire_mode_bits = IVAL(posx->data.data, 0);
+ status = unix_perms_from_wire(
+ conn, &sbuf, wire_mode_bits, ptype, &mode_bits);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ /*
+ * Remove type info from mode, leaving only the
+ * permissions and setuid/gid bits.
+ */
+ mode_bits &= ~S_IFMT;
+
+ file_attributes = (FILE_FLAG_POSIX_SEMANTICS | mode_bits);
+ }
+
status = create_file_unixpath(
conn, req, smb_fname, access_mask, share_access,
create_disposition, create_options, file_attributes,