uint32_t name_hash;
bool delete_on_close_set;
int ret;
+ TALLOC_CTX *frame = talloc_stackframe();
- if (!parent_dirname(talloc_tos(),
+ if (!parent_dirname(frame,
smb_fname->base_name,
&parent_dir,
NULL)) {
- return NT_STATUS_NO_MEMORY;
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
}
- parent_smb_fname = synthetic_smb_fname(talloc_tos(),
+ parent_smb_fname = synthetic_smb_fname(frame,
parent_dir,
NULL,
NULL,
smb_fname->flags);
if (parent_smb_fname == NULL) {
- return NT_STATUS_NO_MEMORY;
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
}
if (get_current_uid(conn) == (uid_t)0) {
"on %s. Granting 0x%x\n",
smb_fname_str_dbg(smb_fname),
(unsigned int)access_mask ));
- return NT_STATUS_OK;
+ status = NT_STATUS_OK;
+ goto out;
}
status = SMB_VFS_GET_NT_ACL(conn,
parent_smb_fname,
SECINFO_DACL,
- talloc_tos(),
+ frame,
&parent_sd);
if (!NT_STATUS_IS_OK(status)) {
"%s with error %s\n",
parent_dir,
nt_errstr(status)));
- return status;
+ goto out;
}
/*
access_mask,
access_granted,
nt_errstr(status) ));
- return status;
+ goto out;
}
if (!(access_mask & (SEC_DIR_ADD_FILE | SEC_DIR_ADD_SUBDIR))) {
- return NT_STATUS_OK;
+ status = NT_STATUS_OK;
+ goto out;
}
if (!lp_check_parent_directory_delete_on_close(SNUM(conn))) {
- return NT_STATUS_OK;
+ status = NT_STATUS_OK;
+ goto out;
}
/* Check if the directory has delete-on-close set */
goto out;
}
- lck = get_existing_share_mode_lock(talloc_tos(), id);
+ lck = get_existing_share_mode_lock(frame, id);
if (lck == NULL) {
status = NT_STATUS_OK;
goto out;
status = NT_STATUS_OK;
out:
- TALLOC_FREE(lck);
- TALLOC_FREE(parent_smb_fname);
+ TALLOC_FREE(frame);
return status;
}
for (i=0; i<d->num_share_modes; i++) {
struct share_mode_entry *e = &d->share_modes[i];
- struct share_mode_lease *l = NULL;
+ bool e_is_lease = (e->op_type == LEASE_OPLOCK);
uint32_t e_lease_type = get_lease_type(d, e);
uint32_t break_to;
uint32_t delay_mask = 0;
+ bool lease_is_breaking = false;
+
+ if (e_is_lease) {
+ NTSTATUS status;
- if (e->op_type == LEASE_OPLOCK) {
- l = &d->leases[e->lease_idx];
+ 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 (have_sharing_violation) {
(unsigned)i, (unsigned)e_lease_type,
(unsigned)will_overwrite));
- if (lease != NULL && l != NULL) {
+ if (e_is_lease && lease != NULL) {
bool ign;
ign = smb2_lease_equal(fsp_client_guid(fsp),
&lease->lease_key,
- &l->client_guid,
- &l->lease_key);
+ &e->client_guid,
+ &e->lease_key);
if (ign) {
continue;
}
}
if ((e_lease_type & ~break_to) == 0) {
- if (l != NULL && l->breaking) {
+ if (lease_is_breaking) {
delay = true;
}
continue;
break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
}
- if (e->op_type != LEASE_OPLOCK) {
+ if (!e_is_lease) {
/*
* Oplocks only support breaking to R or NONE.
*/
if (e_lease_type & delay_mask) {
delay = true;
}
- if (l != NULL && l->breaking && !first_open_attempt) {
+ if (lease_is_breaking && !first_open_attempt) {
delay = true;
}
continue;
return (brl_num_locks(br_lck) > 0);
}
-int find_share_mode_lease(struct share_mode_data *d,
- const struct GUID *client_guid,
- const struct smb2_lease_key *key)
+static int find_share_mode_lease(struct share_mode_data *d,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *key)
{
uint32_t i;
return -1;
}
+NTSTATUS update_share_mode_lease_from_db(
+ struct share_mode_data *d,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key)
+{
+ int idx;
+ struct share_mode_lease *l;
+ uint32_t current_state, breaking_to_requested, breaking_to_required;
+ bool breaking;
+ uint16_t lease_version, epoch;
+ NTSTATUS status;
+
+ idx = find_share_mode_lease(d, client_guid, lease_key);
+ if (idx == -1) {
+ DBG_WARNING("find_share_mode_lease failed\n");
+ return NT_STATUS_NOT_FOUND;
+ }
+ l = &d->leases[idx];
+
+ status = leases_db_get(client_guid,
+ lease_key,
+ &d->id,
+ ¤t_state,
+ &breaking,
+ &breaking_to_requested,
+ &breaking_to_required,
+ &lease_version,
+ &epoch);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("leases_db_get returned %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ if ((l->current_state == current_state) &&
+ (l->breaking == breaking) &&
+ (l->breaking_to_requested == breaking_to_requested) &&
+ (l->breaking_to_required == breaking_to_required) &&
+ (l->lease_version == lease_version) &&
+ (l->epoch == epoch)) {
+ return NT_STATUS_OK;
+ }
+
+ l->current_state = current_state;
+ l->breaking = breaking;
+ l->breaking_to_requested = breaking_to_requested;
+ l->breaking_to_required = breaking_to_required;
+ l->lease_version = lease_version;
+ l->epoch = epoch;
+
+ d->modified = true;
+
+ return NT_STATUS_OK;
+}
+
struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
const struct smb2_lease_key *key,
- const struct share_mode_lease *l)
+ uint32_t current_state,
+ uint16_t lease_version,
+ uint16_t lease_epoch)
{
struct files_struct *fsp;
new_fsp->lease->ref_count = 1;
new_fsp->lease->sconn = new_fsp->conn->sconn;
new_fsp->lease->lease.lease_key = *key;
- new_fsp->lease->lease.lease_state = l->current_state;
+ new_fsp->lease->lease.lease_state = current_state;
/*
* We internally treat all leases as V2 and update
* the epoch, but when sending breaks it matters if
* the requesting lease was v1 or v2.
*/
- new_fsp->lease->lease.lease_version = l->lease_version;
- new_fsp->lease->lease.lease_epoch = l->epoch;
+ new_fsp->lease->lease.lease_version = lease_version;
+ new_fsp->lease->lease.lease_epoch = lease_epoch;
return new_fsp->lease;
}
-static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
- struct share_mode_lock *lck,
- const struct smb2_lease *lease,
- uint32_t *p_lease_idx,
- uint32_t granted)
+static NTSTATUS try_lease_upgrade(struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ const struct GUID *client_guid,
+ const struct smb2_lease *lease,
+ uint32_t granted)
{
struct share_mode_data *d = lck->data;
- const struct GUID *client_guid = fsp_client_guid(fsp);
- struct share_mode_lease *tmp;
+ bool do_upgrade;
+ uint32_t current_state, breaking_to_requested, breaking_to_required;
+ bool breaking;
+ uint16_t lease_version, epoch;
+ uint32_t existing, requested;
NTSTATUS status;
- int idx;
- idx = find_share_mode_lease(d, client_guid, &lease->lease_key);
+ status = leases_db_get(
+ client_guid,
+ &lease->lease_key,
+ &fsp->file_id,
+ ¤t_state,
+ &breaking,
+ &breaking_to_requested,
+ &breaking_to_required,
+ &lease_version,
+ &epoch);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
- if (idx != -1) {
- struct share_mode_lease *l = &d->leases[idx];
- bool do_upgrade;
- uint32_t existing, requested;
+ fsp->lease = find_fsp_lease(
+ fsp,
+ &lease->lease_key,
+ current_state,
+ lease_version,
+ epoch);
+ if (fsp->lease == NULL) {
+ DEBUG(1, ("Did not find existing lease for file %s\n",
+ fsp_str_dbg(fsp)));
+ return NT_STATUS_NO_MEMORY;
+ }
- fsp->lease = find_fsp_lease(fsp, &lease->lease_key, l);
- if (fsp->lease == NULL) {
- DEBUG(1, ("Did not find existing lease for file %s\n",
- fsp_str_dbg(fsp)));
- return NT_STATUS_NO_MEMORY;
- }
+ /*
+ * Upgrade only if the requested lease is a strict upgrade.
+ */
+ existing = current_state;
+ requested = lease->lease_state;
- *p_lease_idx = idx;
+ /*
+ * Tricky: This test makes sure that "requested" is a
+ * strict bitwise superset of "existing".
+ */
+ do_upgrade = ((existing & requested) == existing);
- /*
- * Upgrade only if the requested lease is a strict upgrade.
- */
- existing = l->current_state;
- requested = lease->lease_state;
+ /*
+ * Upgrade only if there's a change.
+ */
+ do_upgrade &= (granted != existing);
- /*
- * Tricky: This test makes sure that "requested" is a
- * strict bitwise superset of "existing".
- */
- do_upgrade = ((existing & requested) == existing);
+ /*
+ * Upgrade only if other leases don't prevent what was asked
+ * for.
+ */
+ do_upgrade &= (granted == requested);
- /*
- * Upgrade only if there's a change.
- */
- do_upgrade &= (granted != existing);
+ /*
+ * only upgrade if we are not in breaking state
+ */
+ do_upgrade &= !breaking;
- /*
- * Upgrade only if other leases don't prevent what was asked
- * for.
- */
- do_upgrade &= (granted == requested);
+ DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+ "granted=%"PRIu32", do_upgrade=%d\n",
+ existing, requested, granted, (int)do_upgrade));
- /*
- * only upgrade if we are not in breaking state
- */
- do_upgrade &= !l->breaking;
+ if (do_upgrade) {
+ NTSTATUS set_status;
- DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
- "granted=%"PRIu32", do_upgrade=%d\n",
- existing, requested, granted, (int)do_upgrade));
+ current_state = granted;
+ epoch += 1;
- if (do_upgrade) {
- l->current_state = granted;
- l->epoch += 1;
+ set_status = leases_db_set(
+ client_guid,
+ &lease->lease_key,
+ current_state,
+ breaking,
+ breaking_to_requested,
+ breaking_to_required,
+ lease_version,
+ epoch);
+
+ if (!NT_STATUS_IS_OK(set_status)) {
+ DBG_DEBUG("leases_db_set failed: %s\n",
+ nt_errstr(set_status));
+ return set_status;
}
+ }
- /* Ensure we're in sync with current lease state. */
- fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
- return NT_STATUS_OK;
+ status = update_share_mode_lease_from_db(
+ d, client_guid, &lease->lease_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("update_share_mode_lease_from_db failed: %s\n",
+ nt_errstr(status));
+ return status;
}
- /*
- * Create new lease
- */
+ fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS grant_new_fsp_lease(struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ const struct GUID *client_guid,
+ const struct smb2_lease *lease,
+ uint32_t granted)
+{
+ struct share_mode_data *d = lck->data;
+ struct share_mode_lease *tmp;
+ NTSTATUS status;
tmp = talloc_realloc(d, d->leases, struct share_mode_lease,
d->num_leases+1);
fsp->lease->lease.lease_state = granted;
fsp->lease->lease.lease_epoch = lease->lease_epoch + 1;
- *p_lease_idx = d->num_leases;
-
d->leases[d->num_leases] = (struct share_mode_lease) {
.client_guid = *client_guid,
.lease_key = fsp->lease->lease.lease_key,
status = leases_db_add(client_guid,
&lease->lease_key,
&fsp->file_id,
+ fsp->lease->lease.lease_state,
+ fsp->lease->lease.lease_version,
+ fsp->lease->lease.lease_epoch,
fsp->conn->connectpath,
fsp->fsp_name->base_name,
fsp->fsp_name->stream_name);
return NT_STATUS_OK;
}
+static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ const struct smb2_lease *lease,
+ uint32_t granted)
+{
+ const struct GUID *client_guid = fsp_client_guid(fsp);
+ NTSTATUS status;
+
+ status = try_lease_upgrade(fsp, lck, client_guid, lease, granted);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = grant_new_fsp_lease(
+ fsp, lck, client_guid, lease, granted);
+ }
+
+ return status;
+}
+
static bool is_same_lease(const files_struct *fsp,
const struct share_mode_data *d,
const struct share_mode_entry *e,
return smb2_lease_equal(fsp_client_guid(fsp),
&lease->lease_key,
- &d->leases[e->lease_idx].client_guid,
- &d->leases[e->lease_idx].lease_key);
+ &e->client_guid,
+ &e->lease_key);
+}
+
+static int map_lease_type_to_oplock(uint32_t lease_type)
+{
+ int result = NO_OPLOCK;
+
+ switch (lease_type) {
+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+ result = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+ break;
+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+ result = EXCLUSIVE_OPLOCK;
+ break;
+ case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+ case SMB2_LEASE_READ:
+ result = LEVEL_II_OPLOCK;
+ break;
+ }
+
+ return result;
}
static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
bool got_oplock = false;
uint32_t i;
uint32_t granted;
- uint32_t lease_idx = UINT32_MAX;
+ const struct GUID *client_guid = NULL;
+ const struct smb2_lease_key *lease_key = NULL;
bool ok;
NTSTATUS status;
fsp->oplock_type = LEASE_OPLOCK;
- status = grant_fsp_lease(fsp, lck, lease, &lease_idx,
- granted);
+ status = grant_fsp_lease(fsp, lck, lease, granted);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
*lease = fsp->lease->lease;
+
+ lease_key = &fsp->lease->lease.lease_key;
+ client_guid = fsp_client_guid(fsp);
+
DEBUG(10, ("lease_state=%d\n", lease->lease_state));
} else {
if (got_handle_lease) {
granted = SMB2_LEASE_NONE;
}
- switch (granted) {
- case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
- fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
- break;
- case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
- fsp->oplock_type = EXCLUSIVE_OPLOCK;
- break;
- case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
- case SMB2_LEASE_READ:
- fsp->oplock_type = LEVEL_II_OPLOCK;
- break;
- default:
- fsp->oplock_type = NO_OPLOCK;
- break;
- }
+ fsp->oplock_type = map_lease_type_to_oplock(granted);
status = set_file_oplock(fsp);
if (!NT_STATUS_IS_OK(status)) {
}
}
- ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
- req ? req->mid : 0,
- fsp->oplock_type,
- lease_idx);
+ ok = set_share_mode(
+ lck,
+ fsp,
+ get_current_uid(fsp->conn),
+ req ? req->mid : 0,
+ fsp->oplock_type,
+ client_guid,
+ lease_key);
if (!ok) {
return NT_STATUS_NO_MEMORY;
}
DBG_DEBUG("defering mid %" PRIu64 "\n", req->mid);
watch_req = dbwrap_watched_watch_send(watch_state,
- req->ev_ctx,
+ req->sconn->ev_ctx,
lck->data->record,
(struct server_id){0});
if (watch_req == NULL) {
}
tevent_req_set_callback(watch_req, defer_open_done, watch_state);
- ok = tevent_req_set_endtime(watch_req, req->ev_ctx, abs_timeout);
+ ok = tevent_req_set_endtime(watch_req, req->sconn->ev_ctx, abs_timeout);
if (!ok) {
exit_server("tevent_req_set_endtime failed");
}
* As this timer event is owned by req, it will
* disappear if req it talloc_freed.
*/
- open_rec->te = tevent_add_timer(req->ev_ctx,
+ open_rec->te = tevent_add_timer(req->sconn->ev_ctx,
req,
timeval_current_ofs(1, 0),
kernel_oplock_poll_open_timer,
exit_server("push_deferred_open_message_smb failed");
}
- open_rec->te = tevent_add_timer(req->ev_ctx,
+ open_rec->te = tevent_add_timer(req->sconn->ev_ctx,
req,
timeval_current_ofs(20, 0),
schedule_async_open_timer,
request_time = fsp->open_time;
}
+ if ((create_options & FILE_DELETE_ON_CLOSE) &&
+ (flags2 & O_CREAT) &&
+ !file_existed) {
+ /* Delete on close semantics for new files. */
+ status = can_set_delete_on_close(fsp,
+ new_dos_attributes);
+ if (!NT_STATUS_IS_OK(status)) {
+ fd_close(fsp);
+ return status;
+ }
+ }
+
/*
* Ensure we pay attention to default ACLs on directories if required.
*/
* in the open file db having the wrong dev/ino key.
*/
fd_close(fsp);
- DEBUG(1,("open_file_ntcreate: file %s - dev/ino mismatch. "
- "Old (dev=0x%llu, ino =0x%llu). "
- "New (dev=0x%llu, ino=0x%llu). Failing open "
- " with NT_STATUS_ACCESS_DENIED.\n",
- smb_fname_str_dbg(smb_fname),
- (unsigned long long)saved_stat.st_ex_dev,
- (unsigned long long)saved_stat.st_ex_ino,
- (unsigned long long)smb_fname->st.st_ex_dev,
- (unsigned long long)smb_fname->st.st_ex_ino));
+ DBG_WARNING("file %s - dev/ino mismatch. "
+ "Old (dev=%ju, ino=%ju). "
+ "New (dev=%ju, ino=%ju). Failing open "
+ "with NT_STATUS_ACCESS_DENIED.\n",
+ smb_fname_str_dbg(smb_fname),
+ (uintmax_t)saved_stat.st_ex_dev,
+ (uintmax_t)saved_stat.st_ex_ino,
+ (uintmax_t)smb_fname->st.st_ex_dev,
+ (uintmax_t)smb_fname->st.st_ex_ino);
return NT_STATUS_ACCESS_DENIED;
}
/* Handle strange delete on close create semantics. */
if (create_options & FILE_DELETE_ON_CLOSE) {
+ if (!new_file_created) {
+ status = can_set_delete_on_close(fsp,
+ existing_dos_attributes);
- status = can_set_delete_on_close(fsp, new_dos_attributes);
-
- if (!NT_STATUS_IS_OK(status)) {
- /* Remember to delete the mode we just added. */
- del_share_mode(lck, fsp);
- TALLOC_FREE(lck);
- fd_close(fsp);
- return status;
+ if (!NT_STATUS_IS_OK(status)) {
+ /* Remember to delete the mode we just added. */
+ del_share_mode(lck, fsp);
+ TALLOC_FREE(lck);
+ fd_close(fsp);
+ return status;
+ }
}
- /* Note that here we set the *inital* delete on close flag,
+ /* Note that here we set the *initial* delete on close flag,
not the regular one. The magic gets handled in close. */
fsp->initial_delete_on_close = True;
}
return status;
}
- ok = set_share_mode(lck, fsp, get_current_uid(conn),
- req ? req->mid : 0, NO_OPLOCK,
- UINT32_MAX);
+ ok = set_share_mode(
+ lck,
+ fsp,
+ get_current_uid(conn),
+ req ? req->mid : 0,
+ NO_OPLOCK,
+ NULL,
+ NULL);
if (!ok) {
TALLOC_FREE(lck);
fd_close(fsp);
}
if (NT_STATUS_IS_OK(status)) {
- /* Note that here we set the *inital* delete on close flag,
+ /* Note that here we set the *initial* delete on close flag,
not the regular one. The magic gets handled in close. */
fsp->initial_delete_on_close = True;
}
state.file_existed = VALID_STAT(fname->st);
if (state.file_existed) {
state.id = vfs_file_id_from_sbuf(conn, &fname->st);
- } else {
- memset(&state.id, '\0', sizeof(state.id));
}
status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
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);
- struct share_mode_lease *l = NULL;
if (share_mode_stale_pid(d, j)) {
continue;
}
if (e->op_type == LEASE_OPLOCK) {
+ struct share_mode_lease *l = NULL;
l = &lck->data->leases[e->lease_idx];
- if (!smb2_lease_key_equal(&l->lease_key,
+ if (!smb2_lease_key_equal(&e->lease_key,
lease_key)) {
continue;
}
new_base_name,
ucf_flags,
NULL,
+ NULL,
smb_fname_out);
if (!NT_STATUS_IS_OK(status)) {
goto out;