#include "../librpc/gen_ndr/ndr_security.h"
#include "../librpc/gen_ndr/open_files.h"
#include "../librpc/gen_ndr/idmap.h"
+#include "../librpc/gen_ndr/ioctl.h"
#include "passdb/lookup_sid.h"
#include "auth.h"
#include "serverid.h"
return NT_STATUS_OK;
}
+/****************************************************************************
+ Ensure when opening a base file for a stream open that we have permissions
+ to do so given the access mask on the base file.
+****************************************************************************/
+
+static NTSTATUS check_base_file_access(struct connection_struct *conn,
+ struct smb_filename *smb_fname,
+ uint32_t access_mask)
+{
+ NTSTATUS status;
+
+ status = smbd_calculate_access_mask(conn, smb_fname,
+ false,
+ access_mask,
+ &access_mask);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("smbd_calculate_access_mask "
+ "on file %s returned %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) {
+ uint32_t dosattrs;
+ if (!CAN_WRITE(conn)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ dosattrs = dos_mode(conn, smb_fname);
+ if (IS_DOS_READONLY(dosattrs)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return smbd_check_access_rights(conn,
+ smb_fname,
+ false,
+ access_mask);
+}
+
/****************************************************************************
fd support routines - attempt to do a dos_open.
****************************************************************************/
* client should be doing this.
*/
- if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
+ if (fsp->posix_open || !lp_follow_symlinks(SNUM(conn))) {
flags |= O_NOFOLLOW;
}
#endif
}
}
- /* Actually do the open */
- status = fd_open_atomic(conn, fsp, local_flags,
+ /*
+ * Actually do the open - if O_TRUNC is needed handle it
+ * below under the share mode lock.
+ */
+ status = fd_open_atomic(conn, fsp, local_flags & ~O_TRUNC,
unx_mode, p_file_created);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
in the stat struct in fsp->fsp_name. */
/* Inherit the ACL if required */
- if (lp_inherit_perms(SNUM(conn))) {
+ if (lp_inherit_permissions(SNUM(conn))) {
inherit_access_posix_acl(conn, parent_dir,
smb_fname->base_name,
unx_mode);
return;
}
+ if (share_entry->share_file_id == 0) {
+ /* INTERNAL_OPEN_ONLY */
+ return;
+ }
+
if (!is_valid_share_mode_entry(share_entry)) {
return;
}
/****************************************************************************
Deal with share modes
- Invarient: Share mode must be locked on entry and exit.
+ Invariant: Share mode must be locked on entry and exit.
Returns -1 on error, or number of share modes on success (may be zero).
****************************************************************************/
*/
static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
- struct share_mode_entry *exclusive,
- uint64_t mid)
+ const struct share_mode_entry *exclusive,
+ uint16_t break_to)
{
NTSTATUS status;
char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
DEBUG(10, ("Sending break request to PID %s\n",
procid_str_static(&exclusive->pid)));
- exclusive->op_mid = mid;
/* Create the message. */
share_mode_entry_to_message(msg, exclusive);
+ /* Overload entry->op_type */
+ SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
+
status = messaging_send_buf(msg_ctx, exclusive->pid,
MSG_SMB_BREAK_REQUEST,
(uint8 *)msg, sizeof(msg));
}
static bool delay_for_oplock(files_struct *fsp,
- uint64_t mid,
int oplock_request,
struct share_mode_lock *lck,
- bool have_sharing_violation)
+ bool have_sharing_violation,
+ uint32_t create_disposition)
{
struct share_mode_data *d = lck->data;
struct share_mode_entry *entry;
uint32_t num_non_stat_opens = 0;
uint32_t i;
+ uint16_t break_to;
if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
return false;
return false;
}
+ switch (create_disposition) {
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE_IF:
+ break_to = NO_OPLOCK;
+ break;
+ default:
+ break_to = LEVEL_II_OPLOCK;
+ break;
+ }
+
if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
if (share_mode_stale_pid(d, 0)) {
return false;
}
- send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
+ send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
return true;
}
if (have_sharing_violation) {
*/
return false;
}
+ if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
+ (break_to == NO_OPLOCK)) {
+ if (share_mode_stale_pid(d, 0)) {
+ return false;
+ }
+ DEBUG(10, ("Asynchronously breaking level2 oplock for "
+ "create_disposition=%u\n",
+ (unsigned)create_disposition));
+ send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
+ return false;
+ }
if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
/*
* No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
return false;
}
- send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
+ send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
return true;
}
struct smb_request *req,
struct deferred_open_record *state)
{
+ struct deferred_open_record *open_rec;
+
DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred "
"open entry for mid %llu\n",
(unsigned int)request_time.tv_sec,
(unsigned int)request_time.tv_usec,
(unsigned long long)req->mid));
- if (!push_deferred_open_message_smb(req, request_time, timeout,
- state->id, (char *)state, sizeof(*state))) {
+ open_rec = talloc(NULL, struct deferred_open_record);
+ if (open_rec == NULL) {
TALLOC_FREE(lck);
- exit_server("push_deferred_open_message_smb failed");
+ exit_server("talloc failed");
}
+
+ *open_rec = *state;
+
if (lck) {
struct defer_open_state *watch_state;
struct tevent_req *watch_req;
bool ret;
- watch_state = talloc(req->sconn, struct defer_open_state);
+ watch_state = talloc(open_rec, struct defer_open_state);
if (watch_state == NULL) {
exit_server("talloc failed");
}
timeval_sum(&request_time, &timeout));
SMB_ASSERT(ret);
}
+
+ if (!push_deferred_open_message_smb(req, request_time, timeout,
+ state->id, open_rec)) {
+ TALLOC_FREE(lck);
+ exit_server("push_deferred_open_message_smb failed");
+ }
}
static void defer_open_done(struct tevent_req *req)
}
static void schedule_defer_open(struct share_mode_lock *lck,
+ struct file_id id,
struct timeval request_time,
struct smb_request *req)
{
state.delayed_for_oplocks = True;
state.async_open = false;
- state.id = lck->data->id;
+ state.id = id;
if (!request_timed_out(request_time, timeout)) {
defer_open(lck, request_time, timeout, req, &state);
Return true if this is a state pointer to an asynchronous create.
****************************************************************************/
-bool is_deferred_open_async(const void *ptr)
+bool is_deferred_open_async(const struct deferred_open_record *rec)
{
- const struct deferred_open_record *state = (const struct deferred_open_record *)ptr;
-
- return state->async_open;
+ return rec->async_open;
}
static bool clear_ads(uint32_t create_disposition)
create_options, (unsigned int)unx_mode, oplock_request,
(unsigned int)private_flags));
- if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
- DEBUG(0, ("No smb request but not an internal only open!\n"));
- return NT_STATUS_INTERNAL_ERROR;
+ if (req == NULL) {
+ /* Ensure req == NULL means INTERNAL_OPEN_ONLY */
+ SMB_ASSERT(((oplock_request & INTERNAL_OPEN_ONLY) != 0));
+ } else {
+ /* And req != NULL means no INTERNAL_OPEN_ONLY */
+ SMB_ASSERT(((oplock_request & INTERNAL_OPEN_ONLY) == 0));
}
/*
*/
if (req) {
- void *ptr;
+ struct deferred_open_record *open_rec;
if (get_deferred_open_message_state(req,
&request_time,
- &ptr)) {
+ &open_rec)) {
/* Remember the absolute time of the original
request with this mid. We'll use it later to
see if this has timed out. */
/* If it was an async create retry, the file
didn't exist. */
- if (is_deferred_open_async(ptr)) {
+ if (is_deferred_open_async(open_rec)) {
SET_STAT_INVALID(smb_fname->st);
file_existed = false;
}
smb_panic("validate_oplock_types failed");
}
- if (delay_for_oplock(fsp, req->mid, 0, lck, false)) {
- schedule_defer_open(lck, request_time, req);
+ if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
+ schedule_defer_open(lck, fsp->file_id, request_time, req);
TALLOC_FREE(lck);
DEBUG(10, ("Sent oplock break request to kernel "
"oplock holder\n"));
*/
state.delayed_for_oplocks = false;
state.async_open = false;
- state.id = lck->data->id;
+ state.id = fsp->file_id;
defer_open(lck, request_time, timeval_set(0, 0), req, &state);
TALLOC_FREE(lck);
DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
if ((req != NULL) &&
delay_for_oplock(
- fsp, req->mid, oplock_request, lck,
- NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION))) {
- schedule_defer_open(lck, request_time, req);
+ fsp, oplock_request, lck,
+ NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
+ create_disposition)) {
+ schedule_defer_open(lck, fsp->file_id, request_time, req);
TALLOC_FREE(lck);
fd_close(fsp);
return NT_STATUS_SHARING_VIOLATION;
return status;
}
+ /* Should we atomically (to the client at least) truncate ? */
+ if ((!new_file_created) &&
+ (flags2 & O_TRUNC) &&
+ (!S_ISFIFO(fsp->fsp_name->st.st_ex_mode))) {
+ int ret;
+
+ ret = vfs_set_filelen(fsp, 0);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ TALLOC_FREE(lck);
+ fd_close(fsp);
+ return status;
+ }
+ }
+
grant_fsp_oplock_type(fsp, lck, oplock_request);
/*
* file structs.
*/
- status = set_file_oplock(fsp, fsp->oplock_type);
+ status = set_file_oplock(fsp);
if (!NT_STATUS_IS_OK(status)) {
/*
* Could not get the kernel oplock
(unsigned int)new_unx_mode));
}
- TALLOC_FREE(lck);
-
- return NT_STATUS_OK;
-}
-
-
-/****************************************************************************
- Open a file for for write to ensure that we can fchmod it.
-****************************************************************************/
+ {
+ /*
+ * Deal with other opens having a modified write time.
+ */
+ struct timespec write_time = get_share_mode_write_time(lck);
-NTSTATUS open_file_fchmod(connection_struct *conn,
- struct smb_filename *smb_fname,
- files_struct **result)
-{
- if (!VALID_STAT(smb_fname->st)) {
- return NT_STATUS_INVALID_PARAMETER;
+ if (!null_timespec(write_time)) {
+ update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
+ }
}
- return SMB_VFS_CREATE_FILE(
- conn, /* conn */
- NULL, /* req */
- 0, /* root_dir_fid */
- smb_fname, /* fname */
- FILE_WRITE_DATA, /* access_mask */
- (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
- FILE_SHARE_DELETE),
- FILE_OPEN, /* create_disposition*/
- 0, /* create_options */
- 0, /* file_attributes */
- INTERNAL_OPEN_ONLY, /* oplock_request */
- 0, /* allocation_size */
- 0, /* private_flags */
- NULL, /* sd */
- NULL, /* ea_list */
- result, /* result */
- NULL); /* pinfo */
+ TALLOC_FREE(lck);
+
+ return NT_STATUS_OK;
}
static NTSTATUS mkdir_internal(connection_struct *conn,
}
}
- if (lp_inherit_perms(SNUM(conn))) {
+ if (lp_inherit_permissions(SNUM(conn))) {
inherit_access_posix_acl(conn, parent_dir,
smb_dname->base_name, mode);
need_re_stat = true;
return status;
}
- mtimespec = smb_dname->st.st_ex_mtime;
-
+ /* 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
+ we only update timestamps on file writes.
+ See bug #9870.
+ */
+ ZERO_STRUCT(mtimespec);
+
+ if (access_mask & (FILE_LIST_DIRECTORY|
+ FILE_ADD_FILE|
+ FILE_ADD_SUBDIRECTORY|
+ FILE_TRAVERSE|
+ DELETE_ACCESS|
+ FILE_DELETE_CHILD)) {
#ifdef O_DIRECTORY
- status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
+ status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
#else
- /* POSIX allows us to open a directory with O_RDONLY. */
- status = fd_open(conn, fsp, O_RDONLY, 0);
+ /* POSIX allows us to open a directory with O_RDONLY. */
+ status = fd_open(conn, fsp, O_RDONLY, 0);
#endif
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(5, ("open_directory: Could not open fd for "
- "%s (%s)\n",
- smb_fname_str_dbg(smb_dname),
- nt_errstr(status)));
- file_free(req, fsp);
- return status;
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("open_directory: Could not open fd for "
+ "%s (%s)\n",
+ smb_fname_str_dbg(smb_dname),
+ nt_errstr(status)));
+ file_free(req, fsp);
+ return status;
+ }
+ } else {
+ fsp->fh->fd = -1;
+ DEBUG(10, ("Not opening Directory %s\n",
+ smb_fname_str_dbg(smb_dname)));
}
status = vfs_stat_fsp(fsp);
}
}
+ {
+ /*
+ * Deal with other opens having a modified write time. Is this
+ * possible for directories?
+ */
+ struct timespec write_time = get_share_mode_write_time(lck);
+
+ if (!null_timespec(write_time)) {
+ update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
+ }
+ }
+
TALLOC_FREE(lck);
if (pinfo) {
FILE_DIRECTORY_FILE, /* create_options */
FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
0, /* oplock_request */
+ NULL, /* lease */
0, /* allocation_size */
0, /* private_flags */
NULL, /* sd */
0, /* create_options */
FILE_ATTRIBUTE_NORMAL, /* file_attributes */
0, /* oplock_request */
+ NULL, /* lease */
0, /* allocation_size */
NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* private_flags */
NULL, /* sd */
if (SMB_VFS_STAT(conn, smb_fname_base) == -1) {
DEBUG(10, ("Unable to stat stream: %s\n",
smb_fname_str_dbg(smb_fname_base)));
+ } else {
+ /*
+ * https://bugzilla.samba.org/show_bug.cgi?id=10229
+ * We need to check if the requested access mask
+ * could be used to open the underlying file (if
+ * it existed), as we're passing in zero for the
+ * access mask to the base filename.
+ */
+ status = check_base_file_access(conn,
+ smb_fname_base,
+ access_mask);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Permission check "
+ "for base %s failed: "
+ "%s\n", smb_fname->base_name,
+ nt_errstr(status)));
+ goto fail;
+ }
}
/* Open the base file. */
}
}
+ if ((conn->fs_capabilities & FILE_FILE_COMPRESSION)
+ && (create_options & FILE_NO_COMPRESSION)
+ && (info == FILE_WAS_CREATED)) {
+ status = SMB_VFS_SET_COMPRESSION(conn, fsp, fsp,
+ COMPRESSION_FORMAT_NONE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to disable compression: %s\n",
+ nt_errstr(status)));
+ }
+ }
+
DEBUG(10, ("create_file_unixpath: info=%d\n", info));
*result = fsp;
uint32_t create_options,
uint32_t file_attributes,
uint32_t oplock_request,
+ struct smb2_lease *lease,
uint64_t allocation_size,
uint32_t private_flags,
struct security_descriptor *sd,