X-Git-Url: http://git.samba.org/samba.git/?p=kai%2Fsamba.git;a=blobdiff_plain;f=source3%2Fsmbd%2Fopen.c;h=d736f4f795bb05d935afff6060f326e9e7c5761d;hp=5714157970be7cce4089bae51435a7fadf734466;hb=b3eb78c4f7123ccad6af50379c29d0939590d1ff;hpb=93e10db3dde3616ab61bf28e0b9c44378d445ab5 diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 5714157970b..d736f4f795b 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -29,6 +29,7 @@ #include "../librpc/gen_ndr/ndr_security.h" #include "../librpc/gen_ndr/open_files.h" #include "auth.h" +#include "serverid.h" #include "messages.h" extern const struct generic_mapping file_generic_mapping; @@ -64,6 +65,7 @@ static bool parent_override_delete(connection_struct *conn, NTSTATUS smbd_check_access_rights(struct connection_struct *conn, const struct smb_filename *smb_fname, + bool use_privs, uint32_t access_mask) { /* Check if we have rights to open. */ @@ -83,7 +85,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn, return NT_STATUS_ACCESS_DENIED; } - if (get_current_uid(conn) == (uid_t)0) { + if (!use_privs && get_current_uid(conn) == (uid_t)0) { /* I'm sorry sir, I didn't know you were root... */ DEBUG(10,("smbd_check_access_rights: root override " "on %s. Granting 0x%x\n", @@ -113,7 +115,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn, status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name, (SECINFO_OWNER | SECINFO_GROUP | - SECINFO_DACL),&sd); + SECINFO_DACL), talloc_tos(), &sd); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("smbd_check_access_rights: Could not get acl " @@ -129,12 +131,18 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn, } /* - * Never test FILE_READ_ATTRIBUTES. se_file_access_check() also takes care of + * If we can access the path to this file, by + * default we have FILE_READ_ATTRIBUTES from the + * containing directory. See the section: + * "Algorithm to Check Access to an Existing File" + * in MS-FSA.pdf. + * + * se_file_access_check() also takes care of * owner WRITE_DAC and READ_CONTROL. */ status = se_file_access_check(sd, get_current_nttok(conn), - false, + use_privs, (access_mask & ~FILE_READ_ATTRIBUTES), &rejected_mask); @@ -235,6 +243,7 @@ static NTSTATUS check_parent_access(struct connection_struct *conn, status = SMB_VFS_GET_NT_ACL(conn, parent_dir, SECINFO_DACL, + talloc_tos(), &parent_sd); if (!NT_STATUS_IS_OK(status)) { @@ -246,7 +255,13 @@ static NTSTATUS check_parent_access(struct connection_struct *conn, } /* - * Never test FILE_READ_ATTRIBUTES. se_file_access_check() also takes care of + * If we can access the path to this file, by + * default we have FILE_READ_ATTRIBUTES from the + * containing directory. See the section: + * "Algorithm to Check Access to an Existing File" + * in MS-FSA.pdf. + * + * se_file_access_check() also takes care of * owner WRITE_DAC and READ_CONTROL. */ status = se_file_access_check(parent_sd, @@ -273,10 +288,10 @@ static NTSTATUS check_parent_access(struct connection_struct *conn, fd support routines - attempt to do a dos_open. ****************************************************************************/ -static NTSTATUS fd_open(struct connection_struct *conn, - files_struct *fsp, - int flags, - mode_t mode) +NTSTATUS fd_open(struct connection_struct *conn, + files_struct *fsp, + int flags, + mode_t mode) { struct smb_filename *smb_fname = fsp->fsp_name; NTSTATUS status = NT_STATUS_OK; @@ -563,14 +578,13 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn, * Fail if already exists, just pass through. */ status = fd_open(conn, fsp, flags, mode); - 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; - } + + /* + * Here we've opened with O_CREAT|O_EXCL. If that went + * NT_STATUS_OK, we *know* we created this file. + */ + *file_created = NT_STATUS_IS_OK(status); + return status; } @@ -677,7 +691,8 @@ static NTSTATUS open_file(files_struct *fsp, DEBUG(3,("Permission denied opening %s\n", smb_fname_str_dbg(smb_fname))); return NT_STATUS_ACCESS_DENIED; - } else if(flags & O_CREAT) { + } + if (flags & O_CREAT) { /* We don't want to write - but we must make sure that O_CREAT doesn't create the file if we have write access into the directory. @@ -711,13 +726,6 @@ static NTSTATUS open_file(files_struct *fsp, const char *wild; int ret; - /* - * We can't actually truncate here as the file may be locked. - * open_file_ntcreate will take care of the truncate later. JRA. - */ - - local_flags &= ~O_TRUNC; - #if defined(O_NONBLOCK) && defined(S_ISFIFO) /* * We would block on opening a FIFO with no one else on the @@ -726,6 +734,7 @@ static NTSTATUS open_file(files_struct *fsp, */ if (file_existed && S_ISFIFO(smb_fname->st.st_ex_mode)) { + local_flags &= ~O_TRUNC; /* Can't truncate a FIFO. */ local_flags |= O_NONBLOCK; } #endif @@ -751,6 +760,7 @@ static NTSTATUS open_file(files_struct *fsp, if (file_existed) { status = smbd_check_access_rights(conn, smb_fname, + false, access_mask); } else if (local_flags & O_CREAT){ status = check_parent_access(conn, @@ -842,6 +852,7 @@ static NTSTATUS open_file(files_struct *fsp, status = smbd_check_access_rights(conn, smb_fname, + false, access_mask); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && @@ -924,6 +935,14 @@ static bool share_conflict(struct share_mode_entry *entry, (unsigned int)entry->share_access, (unsigned int)entry->private_options)); + if (server_id_is_disconnected(&entry->pid)) { + /* + * note: cleanup should have been done by + * delay_for_batch_oplocks() + */ + return false; + } + DEBUG(10,("share_conflict: access_mask = 0x%x, share_access = 0x%x\n", (unsigned int)access_mask, (unsigned int)share_access)); @@ -1005,7 +1024,8 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn, } if (is_deferred_open_entry(share_entry) && - !open_was_deferred(sconn, share_entry->op_mid)) { + !open_was_deferred(sconn, share_entry->op_mid)) + { char *str = talloc_asprintf(talloc_tos(), "Got a deferred entry without a request: " "PANIC: %s\n", @@ -1031,7 +1051,8 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn, } if ((share_entry->op_type == NO_OPLOCK) && - (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) { + (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) + { /* Someone has already written to it, but I haven't yet * noticed */ return; @@ -1061,11 +1082,13 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn, bool is_stat_open(uint32 access_mask) { - return (access_mask && - ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES| - FILE_WRITE_ATTRIBUTES))==0) && - ((access_mask & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES| - FILE_WRITE_ATTRIBUTES)) != 0)); + const uint32_t stat_open_bits = + (SYNCHRONIZE_ACCESS| + FILE_READ_ATTRIBUTES| + FILE_WRITE_ATTRIBUTES); + + return (((access_mask & stat_open_bits) != 0) && + ((access_mask & ~stat_open_bits) == 0)); } /**************************************************************************** @@ -1153,11 +1176,6 @@ static NTSTATUS open_mode_check(connection_struct *conn, return NT_STATUS_OK; } -static bool is_delete_request(files_struct *fsp) { - return ((fsp->access_mask == DELETE_ACCESS) && - (fsp->oplock_type == NO_OPLOCK)); -} - /* * Send a break message to the oplock holder and delay the open for * our client. @@ -1305,6 +1323,19 @@ static bool delay_for_batch_oplocks(files_struct *fsp, return false; } + if (server_id_is_disconnected(&batch_entry->pid)) { + /* + * TODO: clean up. + * This could be achieved by sending a break message + * to ourselves. Special considerations for files + * with delete_on_close flag set! + * + * For now we keep it simple and do not + * allow delete on close for durable handles. + */ + return false; + } + /* Found a batch oplock */ send_break_message(fsp, batch_entry, mid, oplock_request); return true; @@ -1315,8 +1346,6 @@ static bool delay_for_exclusive_oplocks(files_struct *fsp, int oplock_request, struct share_mode_entry *ex_entry) { - bool delay_it; - if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { return false; } @@ -1324,12 +1353,12 @@ static bool delay_for_exclusive_oplocks(files_struct *fsp, return false; } - /* Found an exclusive or batch oplock */ - - delay_it = is_delete_request(fsp) ? - BATCH_OPLOCK_TYPE(ex_entry->op_type) : true; - - if (!delay_it) { + if (server_id_is_disconnected(&ex_entry->pid)) { + /* + * since only durable handles can get disconnected, + * and we can only get durable handles with batch oplocks, + * this should actually never be reached... + */ return false; } @@ -1453,6 +1482,7 @@ static void defer_open(struct share_mode_lock *lck, DEBUG(0, ("Trying to defer an already deferred " "request: mid=%llu, exiting\n", (unsigned long long)req->mid)); + TALLOC_FREE(lck); exit_server("attempt to defer a deferred request"); } } @@ -1468,6 +1498,7 @@ static void defer_open(struct share_mode_lock *lck, if (!push_deferred_open_message_smb(req, request_time, timeout, state->id, (char *)state, sizeof(*state))) { + TALLOC_FREE(lck); exit_server("push_deferred_open_message_smb failed"); } if (lck) { @@ -1555,7 +1586,8 @@ static NTSTATUS fcb_or_dos_open(struct smb_request *req, (unsigned int)fsp->fh->private_options, (unsigned int)fsp->access_mask )); - if (fsp->fh->fd != -1 && + if (fsp != fsp_to_dup_into && + fsp->fh->fd != -1 && fsp->vuid == vuid && fsp->file_pid == file_pid && (fsp->fh->private_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | @@ -1651,13 +1683,14 @@ static void schedule_async_open(struct timeval request_time, static NTSTATUS smbd_calculate_maximum_allowed_access( connection_struct *conn, const struct smb_filename *smb_fname, + bool use_privs, uint32_t *p_access_mask) { struct security_descriptor *sd; uint32_t access_granted; NTSTATUS status; - if (get_current_uid(conn) == (uid_t)0) { + if (!use_privs && (get_current_uid(conn) == (uid_t)0)) { *p_access_mask |= FILE_GENERIC_ALL; return NT_STATUS_OK; } @@ -1665,7 +1698,8 @@ static NTSTATUS smbd_calculate_maximum_allowed_access( status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name, (SECINFO_OWNER | SECINFO_GROUP | - SECINFO_DACL),&sd); + SECINFO_DACL), + talloc_tos(), &sd); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* @@ -1675,38 +1709,50 @@ static NTSTATUS smbd_calculate_maximum_allowed_access( return NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("smbd_calculate_access_mask: " - "Could not get acl on file %s: %s\n", + DEBUG(10,("Could not get acl on file %s: %s\n", smb_fname_str_dbg(smb_fname), nt_errstr(status))); return NT_STATUS_ACCESS_DENIED; } /* - * Never test FILE_READ_ATTRIBUTES. se_file_access_check() + * If we can access the path to this file, by + * default we have FILE_READ_ATTRIBUTES from the + * containing directory. See the section: + * "Algorithm to Check Access to an Existing File" + * in MS-FSA.pdf. + * + * se_file_access_check() * also takes care of owner WRITE_DAC and READ_CONTROL. */ status = se_file_access_check(sd, get_current_nttok(conn), - false, + use_privs, (*p_access_mask & ~FILE_READ_ATTRIBUTES), &access_granted); TALLOC_FREE(sd); if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("smbd_calculate_access_mask: " - "Access denied on file %s: " + DEBUG(10, ("Access denied on file %s: " "when calculating maximum access\n", smb_fname_str_dbg(smb_fname))); return NT_STATUS_ACCESS_DENIED; } *p_access_mask = (access_granted | FILE_READ_ATTRIBUTES); + + if (!(access_granted & DELETE_ACCESS)) { + if (can_delete_file_in_directory(conn, smb_fname)) { + *p_access_mask |= DELETE_ACCESS; + } + } + return NT_STATUS_OK; } NTSTATUS smbd_calculate_access_mask(connection_struct *conn, const struct smb_filename *smb_fname, + bool use_privs, uint32_t access_mask, uint32_t *access_mask_out) { @@ -1724,7 +1770,7 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn, if (access_mask & MAXIMUM_ALLOWED_ACCESS) { status = smbd_calculate_maximum_allowed_access( - conn, smb_fname, &access_mask); + conn, smb_fname, use_privs, &access_mask); if (!NT_STATUS_IS_OK(status)) { return status; @@ -1778,6 +1824,109 @@ bool is_deferred_open_async(const void *ptr) return state->async_open; } +static bool clear_ads(uint32_t create_disposition) +{ + bool ret = false; + + switch (create_disposition) { + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + case FILE_OVERWRITE: + ret = true; + break; + default: + break; + } + return ret; +} + +static int disposition_to_open_flags(uint32_t create_disposition) +{ + int ret = 0; + + /* + * Currently we're using FILE_SUPERSEDE as the same as + * FILE_OVERWRITE_IF but they really are + * different. FILE_SUPERSEDE deletes an existing file + * (requiring delete access) then recreates it. + */ + + switch (create_disposition) { + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + /* + * If file exists replace/overwrite. If file doesn't + * exist create. + */ + ret = O_CREAT|O_TRUNC; + break; + + case FILE_OPEN: + /* + * If file exists open. If file doesn't exist error. + */ + ret = 0; + break; + + case FILE_OVERWRITE: + /* + * If file exists overwrite. If file doesn't exist + * error. + */ + ret = O_TRUNC; + break; + + case FILE_CREATE: + /* + * If file exists error. If file doesn't exist create. + */ + ret = O_CREAT|O_EXCL; + break; + + case FILE_OPEN_IF: + /* + * If file exists open. If file doesn't exist create. + */ + ret = O_CREAT; + break; + } + return ret; +} + +static int calculate_open_access_flags(uint32_t access_mask, + int oplock_request, + uint32_t private_flags) +{ + bool need_write, need_read; + + /* + * Note that we ignore the append flag as append does not + * mean the same thing under DOS and Unix. + */ + + need_write = + ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) || + (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)); + + if (!need_write) { + return O_RDONLY; + } + + /* DENY_DOS opens are always underlying read-write on the + file handle, no matter what the requested access mask + says. */ + + need_read = + ((private_flags & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS) || + access_mask & (FILE_READ_ATTRIBUTES|FILE_READ_DATA| + FILE_READ_EA|FILE_EXECUTE)); + + if (!need_read) { + return O_WRONLY; + } + return O_RDWR; +} + /**************************************************************************** Open a file with a share mode. Passed in an already created files_struct *. ****************************************************************************/ @@ -1802,7 +1951,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, bool def_acl = False; bool posix_open = False; bool new_file_created = False; - bool clear_ads = false; + bool first_open_attempt = true; NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED; mode_t new_unx_mode = (mode_t)0; mode_t unx_mode = (mode_t)0; @@ -1814,6 +1963,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, NTSTATUS status; char *parent_dir; SMB_STRUCT_STAT saved_stat = smb_fname->st; + struct share_mode_entry *batch_entry = NULL; + struct share_mode_entry *exclusive_entry = NULL; + bool got_level2_oplock = false; + bool got_a_none_oplock = false; + struct timespec old_write_time; + struct file_id id; if (conn->printer) { /* @@ -1903,6 +2058,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, /* Ensure we don't reprocess this message. */ remove_deferred_open_message_smb(req->sconn, req->mid); + + first_open_attempt = false; } } @@ -1933,26 +2090,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } switch( create_disposition ) { - /* - * Currently we're using FILE_SUPERSEDE as the same as - * FILE_OVERWRITE_IF but they really are - * different. FILE_SUPERSEDE deletes an existing file - * (requiring delete access) then recreates it. - */ - case FILE_SUPERSEDE: - /* If file exists replace/overwrite. If file doesn't - * exist create. */ - flags2 |= (O_CREAT | O_TRUNC); - clear_ads = true; - break; - - case FILE_OVERWRITE_IF: - /* If file exists replace/overwrite. If file doesn't - * exist create. */ - flags2 |= (O_CREAT | O_TRUNC); - clear_ads = true; - break; - case FILE_OPEN: /* If file exists open. If file doesn't exist error. */ if (!file_existed) { @@ -1976,8 +2113,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, errno = ENOENT; return NT_STATUS_OBJECT_NAME_NOT_FOUND; } - flags2 |= O_TRUNC; - clear_ads = true; break; case FILE_CREATE: @@ -1995,19 +2130,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } return map_nt_error_from_unix(errno); } - flags2 |= (O_CREAT|O_EXCL); break; + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: case FILE_OPEN_IF: - /* If file exists open. If file doesn't exist - * create. */ - flags2 |= O_CREAT; break; - default: return NT_STATUS_INVALID_PARAMETER; } + flags2 = disposition_to_open_flags(create_disposition); + /* We only care about matching attributes on file exists and * overwrite. */ @@ -2031,6 +2165,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } status = smbd_calculate_access_mask(conn, smb_fname, + false, access_mask, &access_mask); if (!NT_STATUS_IS_OK(status)) { @@ -2055,20 +2190,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, * mean the same thing under DOS and Unix. */ - if ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) || - (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) { - /* DENY_DOS opens are always underlying read-write on the - file handle, no matter what the requested access mask - says. */ - if ((private_flags & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS) || - access_mask & (FILE_READ_ATTRIBUTES|FILE_READ_DATA|FILE_READ_EA|FILE_EXECUTE)) { - flags = O_RDWR; - } else { - flags = O_WRONLY; - } - } else { - flags = O_RDONLY; - } + flags = calculate_open_access_flags(access_mask, oplock_request, + private_flags); /* * Currently we only look at FILE_WRITE_THROUGH for create options. @@ -2093,6 +2216,24 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, flags2 &= ~(O_CREAT|O_TRUNC); } + if (first_open_attempt && lp_kernel_oplocks(SNUM(conn))) { + /* + * With kernel oplocks the open breaking an oplock + * blocks until the oplock holder has given up the + * oplock or closed the file. We prevent this by first + * trying to open the file with O_NONBLOCK (see "man + * fcntl" on Linux). For the second try, triggered by + * an oplock break response, we do not need this + * anymore. + * + * This is true under the assumption that only Samba + * requests kernel oplocks. Once someone else like + * NFSv4 starts to use that API, we will have to + * modify this by communicating with the NFSv4 server. + */ + flags2 |= O_NONBLOCK; + } + /* * Ensure we can't write on a read-only share or file. */ @@ -2122,207 +2263,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, request_time = fsp->open_time; } - if (file_existed) { - struct share_mode_entry *batch_entry = NULL; - struct share_mode_entry *exclusive_entry = NULL; - bool got_level2_oplock = false; - bool got_a_none_oplock = false; - struct file_id id; - - struct timespec old_write_time = smb_fname->st.st_ex_mtime; - id = vfs_file_id_from_sbuf(conn, &smb_fname->st); - - lck = get_share_mode_lock(talloc_tos(), id, - conn->connectpath, - smb_fname, &old_write_time); - if (lck == NULL) { - DEBUG(0, ("Could not get share mode lock\n")); - return NT_STATUS_SHARING_VIOLATION; - } - - /* Get the types we need to examine. */ - find_oplock_types(fsp, - oplock_request, - lck, - &batch_entry, - &exclusive_entry, - &got_level2_oplock, - &got_a_none_oplock); - - /* First pass - send break only on batch oplocks. */ - if ((req != NULL) && - delay_for_batch_oplocks(fsp, - req->mid, - oplock_request, - batch_entry)) { - schedule_defer_open(lck, request_time, req); - TALLOC_FREE(lck); - return NT_STATUS_SHARING_VIOLATION; - } - - /* Use the client requested access mask here, not the one we - * open with. */ - status = open_mode_check(conn, lck, fsp->name_hash, - access_mask, share_access, - create_options, &file_existed); - - if (NT_STATUS_IS_OK(status)) { - /* We might be going to allow this open. Check oplock - * status again. */ - /* Second pass - send break for both batch or - * exclusive oplocks. */ - if ((req != NULL) && - delay_for_exclusive_oplocks( - fsp, - req->mid, - oplock_request, - exclusive_entry)) { - schedule_defer_open(lck, request_time, req); - TALLOC_FREE(lck); - return NT_STATUS_SHARING_VIOLATION; - } - } - - if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { - /* DELETE_PENDING is not deferred for a second */ - TALLOC_FREE(lck); - return status; - } - - grant_fsp_oplock_type(fsp, - oplock_request, - got_level2_oplock, - got_a_none_oplock); - - if (!NT_STATUS_IS_OK(status)) { - uint32 can_access_mask; - bool can_access = True; - - SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)); - - /* Check if this can be done with the deny_dos and fcb - * calls. */ - if (private_flags & - (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| - NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { - if (req == NULL) { - DEBUG(0, ("DOS open without an SMB " - "request!\n")); - TALLOC_FREE(lck); - return NT_STATUS_INTERNAL_ERROR; - } - - /* Use the client requested access mask here, - * not the one we open with. */ - status = fcb_or_dos_open(req, - conn, - fsp, - smb_fname, - id, - req->smbpid, - req->vuid, - access_mask, - share_access, - create_options); - - if (NT_STATUS_IS_OK(status)) { - TALLOC_FREE(lck); - if (pinfo) { - *pinfo = FILE_WAS_OPENED; - } - return NT_STATUS_OK; - } - } - - /* - * This next line is a subtlety we need for - * MS-Access. If a file open will fail due to share - * permissions and also for security (access) reasons, - * we need to return the access failed error, not the - * share error. We can't open the file due to kernel - * oplock deadlock (it's possible we failed above on - * the open_mode_check()) so use a userspace check. - */ - - if (flags & O_RDWR) { - can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA; - } else if (flags & O_WRONLY) { - can_access_mask = FILE_WRITE_DATA; - } else { - can_access_mask = FILE_READ_DATA; - } - - if (((can_access_mask & FILE_WRITE_DATA) && - !CAN_WRITE(conn)) || - !NT_STATUS_IS_OK(smbd_check_access_rights(conn, - smb_fname, can_access_mask))) { - can_access = False; - } - - /* - * If we're returning a share violation, ensure we - * cope with the braindead 1 second delay. - */ - - if (!(oplock_request & INTERNAL_OPEN_ONLY) && - lp_defer_sharing_violations()) { - struct timeval timeout; - struct deferred_open_record state; - int timeout_usecs; - - /* this is a hack to speed up torture tests - in 'make test' */ - timeout_usecs = lp_parm_int(SNUM(conn), - "smbd","sharedelay", - SHARING_VIOLATION_USEC_WAIT); - - /* This is a relative time, added to the absolute - request_time value to get the absolute timeout time. - Note that if this is the second or greater time we enter - this codepath for this particular request mid then - request_time is left as the absolute time of the *first* - time this request mid was processed. This is what allows - the request to eventually time out. */ - - timeout = timeval_set(0, timeout_usecs); - - /* Nothing actually uses state.delayed_for_oplocks - but it's handy to differentiate in debug messages - between a 30 second delay due to oplock break, and - a 1 second delay for share mode conflicts. */ - - state.delayed_for_oplocks = False; - state.async_open = false; - state.id = id; - - if ((req != NULL) - && !request_timed_out(request_time, - timeout)) { - defer_open(lck, request_time, timeout, - req, &state); - } - } - - TALLOC_FREE(lck); - if (can_access) { - /* - * We have detected a sharing violation here - * so return the correct error code - */ - status = NT_STATUS_SHARING_VIOLATION; - } else { - status = NT_STATUS_ACCESS_DENIED; - } - return status; - } - - /* - * We exit this block with the share entry *locked*..... - */ - } - - SMB_ASSERT(!file_existed || (lck != NULL)); - /* * Ensure we pay attention to default ACLs on directories if required. */ @@ -2338,14 +2278,68 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, (unsigned int)unx_mode, (unsigned int)access_mask, (unsigned int)open_access_mask)); - /* - * open_file strips any O_TRUNC flags itself. - */ - fsp_open = open_file(fsp, conn, req, parent_dir, flags|flags2, unx_mode, access_mask, open_access_mask, &new_file_created); + if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) { + struct deferred_open_record state; + + /* + * EWOULDBLOCK/EAGAIN maps to 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; + } + + /* + * From here on we assume this is an oplock break triggered + */ + + lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); + if (lck == NULL) { + state.delayed_for_oplocks = false; + state.async_open = false; + state.id = fsp->file_id; + defer_open(NULL, request_time, timeval_set(0, 0), + req, &state); + DEBUG(10, ("No share mode lock found after " + "EWOULDBLOCK, retrying sync\n")); + return NT_STATUS_SHARING_VIOLATION; + } + + find_oplock_types(fsp, 0, lck, &batch_entry, &exclusive_entry, + &got_level2_oplock, &got_a_none_oplock); + + if (delay_for_batch_oplocks(fsp, req->mid, 0, batch_entry) || + delay_for_exclusive_oplocks(fsp, req->mid, 0, + exclusive_entry)) { + schedule_defer_open(lck, request_time, req); + TALLOC_FREE(lck); + DEBUG(10, ("Sent oplock break request to kernel " + "oplock holder\n")); + return NT_STATUS_SHARING_VIOLATION; + } + + /* + * No oplock from Samba around. Immediately retry with + * a blocking open. + */ + state.delayed_for_oplocks = false; + state.async_open = false; + state.id = lck->data->id; + defer_open(lck, request_time, timeval_set(0, 0), req, &state); + TALLOC_FREE(lck); + DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. " + "Retrying sync\n")); + return NT_STATUS_SHARING_VIOLATION; + } + if (!NT_STATUS_IS_OK(fsp_open)) { if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) { schedule_async_open(request_time, req); @@ -2378,123 +2372,224 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, return NT_STATUS_ACCESS_DENIED; } - if (!file_existed) { - struct share_mode_entry *batch_entry = NULL; - struct share_mode_entry *exclusive_entry = NULL; - bool got_level2_oplock = false; - bool got_a_none_oplock = false; - struct timespec old_write_time = smb_fname->st.st_ex_mtime; - struct file_id id; - /* - * Deal with the race condition where two smbd's detect the - * file doesn't exist and do the create at the same time. One - * of them will win and set a share mode, the other (ie. this - * one) should check if the requested share mode for this - * create is allowed. - */ + old_write_time = smb_fname->st.st_ex_mtime; - /* - * Now the file exists and fsp is successfully opened, - * fsp->dev and fsp->inode are valid and should replace the - * dev=0,inode=0 from a non existent file. Spotted by - * Nadav Danieli . JRA. - */ + /* + * Deal with the race condition where two smbd's detect the + * file doesn't exist and do the create at the same time. One + * of them will win and set a share mode, the other (ie. this + * one) should check if the requested share mode for this + * create is allowed. + */ - id = fsp->file_id; + /* + * Now the file exists and fsp is successfully opened, + * fsp->dev and fsp->inode are valid and should replace the + * dev=0,inode=0 from a non existent file. Spotted by + * Nadav Danieli . JRA. + */ - lck = get_share_mode_lock(talloc_tos(), id, - conn->connectpath, - smb_fname, &old_write_time); + id = fsp->file_id; - if (lck == NULL) { - DEBUG(0, ("open_file_ntcreate: Could not get share " - "mode lock for %s\n", - smb_fname_str_dbg(smb_fname))); - fd_close(fsp); - return NT_STATUS_SHARING_VIOLATION; - } + lck = get_share_mode_lock(talloc_tos(), id, + conn->connectpath, + smb_fname, &old_write_time); - /* Get the types we need to examine. */ - find_oplock_types(fsp, - oplock_request, - lck, - &batch_entry, - &exclusive_entry, - &got_level2_oplock, - &got_a_none_oplock); + if (lck == NULL) { + DEBUG(0, ("open_file_ntcreate: Could not get share " + "mode lock for %s\n", + smb_fname_str_dbg(smb_fname))); + fd_close(fsp); + return NT_STATUS_SHARING_VIOLATION; + } + + /* Get the types we need to examine. */ + find_oplock_types(fsp, + oplock_request, + lck, + &batch_entry, + &exclusive_entry, + &got_level2_oplock, + &got_a_none_oplock); + + /* First pass - send break only on batch oplocks. */ + if ((req != NULL) && + delay_for_batch_oplocks(fsp, + req->mid, + oplock_request, + batch_entry)) { + schedule_defer_open(lck, request_time, req); + TALLOC_FREE(lck); + fd_close(fsp); + return NT_STATUS_SHARING_VIOLATION; + } - /* First pass - send break only on batch oplocks. */ + status = open_mode_check(conn, lck, fsp->name_hash, + access_mask, share_access, + create_options, &file_existed); + + if (NT_STATUS_IS_OK(status)) { + /* We might be going to allow this open. Check oplock + * status again. */ + /* Second pass - send break for both batch or + * exclusive oplocks. */ if ((req != NULL) && - delay_for_batch_oplocks(fsp, - req->mid, - oplock_request, - batch_entry)) { + delay_for_exclusive_oplocks( + fsp, + req->mid, + oplock_request, + exclusive_entry)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); fd_close(fsp); return NT_STATUS_SHARING_VIOLATION; } + } - status = open_mode_check(conn, lck, fsp->name_hash, - access_mask, share_access, - create_options, &file_existed); + if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { + /* DELETE_PENDING is not deferred for a second */ + TALLOC_FREE(lck); + fd_close(fsp); + return status; + } - if (NT_STATUS_IS_OK(status)) { - /* We might be going to allow this open. Check oplock - * status again. */ - /* Second pass - send break for both batch or - * exclusive oplocks. */ - if ((req != NULL) && - delay_for_exclusive_oplocks( - fsp, - req->mid, - oplock_request, - exclusive_entry)) { - schedule_defer_open(lck, request_time, req); + if (!NT_STATUS_IS_OK(status)) { + uint32 can_access_mask; + bool can_access = True; + + SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)); + + /* Check if this can be done with the deny_dos and fcb + * calls. */ + if (private_flags & + (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| + NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { + if (req == NULL) { + DEBUG(0, ("DOS open without an SMB " + "request!\n")); TALLOC_FREE(lck); fd_close(fsp); - return NT_STATUS_SHARING_VIOLATION; + return NT_STATUS_INTERNAL_ERROR; + } + + /* Use the client requested access mask here, + * not the one we open with. */ + status = fcb_or_dos_open(req, + conn, + fsp, + smb_fname, + id, + req->smbpid, + req->vuid, + access_mask, + share_access, + create_options); + + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(lck); + if (pinfo) { + *pinfo = FILE_WAS_OPENED; + } + return NT_STATUS_OK; } } - if (!NT_STATUS_IS_OK(status)) { + /* + * This next line is a subtlety we need for + * MS-Access. If a file open will fail due to share + * permissions and also for security (access) reasons, + * we need to return the access failed error, not the + * share error. We can't open the file due to kernel + * oplock deadlock (it's possible we failed above on + * the open_mode_check()) so use a userspace check. + */ + + if (flags & O_RDWR) { + can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA; + } else if (flags & O_WRONLY) { + can_access_mask = FILE_WRITE_DATA; + } else { + can_access_mask = FILE_READ_DATA; + } + + if (((can_access_mask & FILE_WRITE_DATA) && + !CAN_WRITE(conn)) || + !NT_STATUS_IS_OK(smbd_check_access_rights(conn, + smb_fname, + false, + can_access_mask))) { + can_access = False; + } + + /* + * If we're returning a share violation, ensure we + * cope with the braindead 1 second delay. + */ + + if (!(oplock_request & INTERNAL_OPEN_ONLY) && + lp_defer_sharing_violations()) { + struct timeval timeout; struct deferred_open_record state; + int timeout_usecs; + + /* this is a hack to speed up torture tests + in 'make test' */ + timeout_usecs = lp_parm_int(SNUM(conn), + "smbd","sharedelay", + SHARING_VIOLATION_USEC_WAIT); + + /* This is a relative time, added to the absolute + request_time value to get the absolute timeout time. + Note that if this is the second or greater time we enter + this codepath for this particular request mid then + request_time is left as the absolute time of the *first* + time this request mid was processed. This is what allows + the request to eventually time out. */ + + timeout = timeval_set(0, timeout_usecs); + + /* Nothing actually uses state.delayed_for_oplocks + but it's handy to differentiate in debug messages + between a 30 second delay due to oplock break, and + a 1 second delay for share mode conflicts. */ state.delayed_for_oplocks = False; state.async_open = false; state.id = id; - /* Do it all over again immediately. In the second - * round we will find that the file existed and handle - * the DELETE_PENDING and FCB cases correctly. No need - * to duplicate the code here. Essentially this is a - * "goto top of this function", but don't tell - * anybody... */ - - if (req != NULL) { - defer_open(lck, request_time, timeval_zero(), + if ((req != NULL) + && !request_timed_out(request_time, + timeout)) { + defer_open(lck, request_time, timeout, req, &state); } - TALLOC_FREE(lck); - fd_close(fsp); - return status; } - grant_fsp_oplock_type(fsp, - oplock_request, - got_level2_oplock, - got_a_none_oplock); - - /* - * We exit this block with the share entry *locked*..... - */ - + TALLOC_FREE(lck); + fd_close(fsp); + if (can_access) { + /* + * We have detected a sharing violation here + * so return the correct error code + */ + status = NT_STATUS_SHARING_VIOLATION; + } else { + status = NT_STATUS_ACCESS_DENIED; + } + return status; } - SMB_ASSERT(lck != NULL); + grant_fsp_oplock_type(fsp, + oplock_request, + got_level2_oplock, + got_a_none_oplock); + + /* + * We have the share entry *locked*..... + */ /* Delete streams if create_disposition requires it */ - if (!new_file_created && clear_ads && + if (!new_file_created && clear_ads(create_disposition) && !is_ntfs_stream_smb_fname(smb_fname)) { status = delete_all_streams(conn, smb_fname->base_name); if (!NT_STATUS_IS_OK(status)) { @@ -2512,7 +2607,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, the kernel refuses the operations then the kernel is wrong. note that GPFS supports it as well - jmcd */ - if (fsp->fh->fd != -1) { + if (fsp->fh->fd != -1 && lp_kernel_share_modes(SNUM(conn))) { int ret_flock; ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access, access_mask); if(ret_flock == -1 ){ @@ -2525,29 +2620,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } /* - * At this point onwards, we can guarentee that the share entry + * At this point onwards, we can guarantee that the share entry * is locked, whether we created the file or not, and that the * deny mode is compatible with all current opens. */ - /* - * If requested, truncate the file. - */ - - if (!new_file_created && (flags2&O_TRUNC)) { - /* - * We are modifying the file after open - update the stat - * struct.. - */ - if ((SMB_VFS_FTRUNCATE(fsp, 0) == -1) || - (SMB_VFS_FSTAT(fsp, &smb_fname->st)==-1)) { - status = map_nt_error_from_unix(errno); - TALLOC_FREE(lck); - fd_close(fsp); - return status; - } - } - /* * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted, * but we don't have to store this - just ignore it on access check. @@ -2744,7 +2821,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn, bool need_re_stat = false; uint32_t access_mask = SEC_DIR_ADD_SUBDIR; - if(access_mask & ~(conn->share_access)) { + if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) { DEBUG(5,("mkdir_internal: failing share access " "%s\n", lp_servicename(talloc_tos(), SNUM(conn)))); return NT_STATUS_ACCESS_DENIED; @@ -2888,7 +2965,7 @@ static NTSTATUS open_directory(connection_struct *conn, (unsigned int)create_disposition, (unsigned int)file_attributes)); - status = smbd_calculate_access_mask(conn, smb_dname, + status = smbd_calculate_access_mask(conn, smb_dname, false, access_mask, &access_mask); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("open_directory: smbd_calculate_access_mask " @@ -2994,7 +3071,10 @@ static NTSTATUS open_directory(connection_struct *conn, } if (info == FILE_WAS_OPENED) { - status = smbd_check_access_rights(conn, smb_dname, access_mask); + status = smbd_check_access_rights(conn, + smb_dname, + false, + access_mask); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("open_directory: smbd_check_access_rights on " "file %s failed with %s\n", @@ -3367,7 +3447,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn, static NTSTATUS inherit_new_acl(files_struct *fsp) { - TALLOC_CTX *ctx = talloc_tos(); + TALLOC_CTX *frame = talloc_stackframe(); char *parent_name = NULL; struct security_descriptor *parent_desc = NULL; NTSTATUS status = NT_STATUS_OK; @@ -3379,15 +3459,18 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) bool inheritable_components = false; size_t size = 0; - if (!parent_dirname(ctx, fsp->fsp_name->base_name, &parent_name, NULL)) { + if (!parent_dirname(frame, fsp->fsp_name->base_name, &parent_name, NULL)) { + TALLOC_FREE(frame); return NT_STATUS_NO_MEMORY; } status = SMB_VFS_GET_NT_ACL(fsp->conn, - parent_name, - (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL), - &parent_desc); + parent_name, + (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL), + frame, + &parent_desc); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return status; } @@ -3395,6 +3478,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) fsp->is_directory); if (!inheritable_components && !inherit_owner) { + TALLOC_FREE(frame); /* Nothing to inherit and not setting owner. */ return NT_STATUS_OK; } @@ -3420,7 +3504,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) group_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX]; } - status = se_create_child_secdesc(ctx, + status = se_create_child_secdesc(frame, &psd, &size, parent_desc, @@ -3428,6 +3512,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) group_sid, fsp->is_directory); if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); return status; } @@ -3458,6 +3543,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) if (inherit_owner) { unbecome_root(); } + TALLOC_FREE(frame); return status; } @@ -3639,11 +3725,12 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, goto fail; } - /* - * We're opening the stream element of a base_fsp - * we already opened. Set up the base_fsp pointer. - */ if (base_fsp) { + /* + * We're opening the stream element of a + * base_fsp we already opened. Set up the + * base_fsp pointer. + */ fsp->base_fsp = base_fsp; }