diff options
Diffstat (limited to 'source3/smbd/open.c')
-rw-r--r-- | source3/smbd/open.c | 879 |
1 files changed, 380 insertions, 499 deletions
diff --git a/source3/smbd/open.c b/source3/smbd/open.c index ed847826d52..56d31c69409 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -24,11 +24,11 @@ extern struct current_user current_user; extern userdom_struct current_user_info; -extern uint16 global_oplock_port; extern uint16 global_smbpid; extern BOOL global_client_failed_oplock_break; -struct dev_inode_bundle { +struct deferred_open_record { + BOOL delayed_for_oplocks; SMB_DEV_T dev; SMB_INO_T inode; }; @@ -208,7 +208,6 @@ static BOOL open_file(files_struct *fsp, BOOL file_existed = VALID_STAT(*psbuf); fsp->fh->fd = -1; - fsp->oplock_type = NO_OPLOCK; errno = EPERM; /* Check permissions */ @@ -283,8 +282,7 @@ static BOOL open_file(files_struct *fsp, /* Don't create files with Microsoft wildcard characters. */ if ((local_flags & O_CREAT) && !file_existed && ms_has_wild(fname)) { - set_saved_error_triple(ERRDOS, ERRinvalidname, - NT_STATUS_OBJECT_NAME_INVALID); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_INVALID); return False; } @@ -354,7 +352,6 @@ static BOOL open_file(files_struct *fsp, } fsp->print_file = False; fsp->modified = False; - fsp->oplock_type = NO_OPLOCK; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; fsp->is_stat = False; @@ -397,7 +394,7 @@ static BOOL is_executable(const char *fname) Returns True if conflict, False if not. ****************************************************************************/ -static BOOL share_conflict(share_mode_entry *entry, +static BOOL share_conflict(struct share_mode_entry *entry, uint32 access_mask, uint32 share_access) { @@ -445,7 +442,6 @@ static BOOL share_conflict(share_mode_entry *entry, DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \ (unsigned int)(share) )); \ - set_saved_error_triple(ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION); \ return True; \ } #else @@ -454,7 +450,6 @@ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (u DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \ (unsigned int)(share) )); \ - set_saved_error_triple(ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION); \ return True; \ } #endif @@ -480,11 +475,23 @@ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (u #if defined(DEVELOPER) static void validate_my_share_entries(int num, - share_mode_entry *share_entry) + struct share_mode_entry *share_entry) { files_struct *fsp; - if (share_entry->pid != sys_getpid()) { + if (!procid_is_me(&share_entry->pid)) { + return; + } + + if (is_deferred_open_entry(share_entry) && + !open_was_deferred(share_entry->op_mid)) { + pstring str; + DEBUG(0, ("Got a deferred entry without a request: " + "PANIC: %s\n", share_mode_str(num, share_entry))); + smb_panic(str); + } + + if (!is_valid_share_mode_entry(share_entry)) { return; } @@ -497,7 +504,26 @@ static void validate_my_share_entries(int num, "share entry with an open file\n"); } + if (is_deferred_open_entry(share_entry) || + is_unused_share_mode_entry(share_entry)) { + goto panic; + } + + if ((share_entry->op_type == NO_OPLOCK) && + (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) { + /* Someone has already written to it, but I haven't yet + * noticed */ + return; + } + if (((uint16)fsp->oplock_type) != share_entry->op_type) { + goto panic; + } + + return; + + panic: + { pstring str; DEBUG(0,("validate_my_share_entries: PANIC : %s\n", share_mode_str(num, share_entry) )); @@ -510,375 +536,217 @@ static void validate_my_share_entries(int num, } #endif -struct share_mode_entry_list { - struct share_mode_entry_list *next, *prev; - share_mode_entry entry; -}; - -static void free_broken_entry_list(struct share_mode_entry_list *broken_entry_list) +static BOOL is_stat_open(uint32 access_mask) { - while (broken_entry_list) { - struct share_mode_entry_list *broken_entry = broken_entry_list; - DLIST_REMOVE(broken_entry_list, broken_entry); - SAFE_FREE(broken_entry); - } -} - -static BOOL cause_oplock_break(int request, int existing, uint32 access_mask) -{ - if ((access_mask == DELETE_ACCESS) && - (request == NO_OPLOCK)) { - /* This is a delete request */ - return (BATCH_OPLOCK_TYPE(existing) != 0); - } - - if (EXCLUSIVE_OPLOCK_TYPE(existing) && (request != NO_OPLOCK)) { - return True; - } - - if ((existing != NO_OPLOCK) && (request == NO_OPLOCK)) { - return True; - } - - return False; + 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)); } /**************************************************************************** - Deal with open deny mode and oplock break processing. + Deal with share modes Invarient: Share mode must be locked on entry and exit. Returns -1 on error, or number of share modes on success (may be zero). ****************************************************************************/ -static int open_mode_check(connection_struct *conn, - const char *fname, - SMB_DEV_T dev, - SMB_INO_T inode, - uint32 access_mask, - uint32 share_access, - uint32 create_options, - int *p_oplock_request, - BOOL *p_all_current_opens_are_level_II) +static NTSTATUS open_mode_check(connection_struct *conn, + const char *fname, + struct share_mode_lock *lck, + uint32 access_mask, + uint32 share_access, + uint32 create_options, + BOOL *file_existed) { int i; - int num_share_modes; - int oplock_contention_count = 0; - share_mode_entry *old_shares = NULL; - BOOL broke_oplock; - BOOL delete_on_close; - num_share_modes = get_share_modes(dev, inode, &old_shares, &delete_on_close); - - if(num_share_modes == 0) { - SAFE_FREE(old_shares); - return 0; + if(lck->num_share_modes == 0) { + return NT_STATUS_OK; } + + *file_existed = True; - if (access_mask && - ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES| - FILE_WRITE_ATTRIBUTES))==0) && - ((access_mask & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES| - FILE_WRITE_ATTRIBUTES)) != 0)) { + if (is_stat_open(access_mask)) { /* Stat open that doesn't trigger oplock breaks or share mode * checks... ! JRA. */ - SAFE_FREE(old_shares); - return num_share_modes; + return NT_STATUS_OK; } /* A delete on close prohibits everything */ - if (delete_on_close) { - SAFE_FREE(old_shares); - errno = EACCES; - return -1; + if (lck->delete_on_close) { + return NT_STATUS_DELETE_PENDING; } /* * Check if the share modes will give us access. */ - do { - struct share_mode_entry_list *broken_entry_list = NULL; - struct share_mode_entry_list *broken_entry = NULL; - - broke_oplock = False; - *p_all_current_opens_are_level_II = True; - - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &old_shares[i]; - BOOL opb_ret; - #if defined(DEVELOPER) - validate_my_share_entries(i, share_entry); + for(i = 0; i < lck->num_share_modes; i++) { + validate_my_share_entries(i, &lck->share_modes[i]); + } #endif - /* - * By observation of NetBench, oplocks are broken - * *before* share modes are checked. This allows a - * file to be closed by the client if the share mode - * would deny access and the client has an oplock. - * Check if someone has an oplock on this file. If so - * we must break it before continuing. - */ + if (!lp_share_modes(SNUM(conn))) { + return NT_STATUS_OK; + } - if (!cause_oplock_break(*p_oplock_request, - share_entry->op_type, - access_mask)) { - if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { - *p_all_current_opens_are_level_II = False; - } - continue; - } + /* Now we check the share modes, after any oplock breaks. */ + for(i = 0; i < lck->num_share_modes; i++) { - /* This is an oplock break */ - - DEBUG(5,("open_mode_check: oplock_request = %d, " - "breaking oplock (%x) on file %s, " - "dev = %x, inode = %.0f\n", - *p_oplock_request, share_entry->op_type, - fname, (unsigned int)dev, (double)inode)); - - /* Ensure the reply for the open uses the correct - * sequence number. */ - /* This isn't a real deferred packet as it's response - * will also increment the sequence. - */ - srv_defer_sign_response(get_current_mid()); - - /* Oplock break - unlock to request it. */ - unlock_share_entry(conn, dev, inode); - - opb_ret = request_oplock_break(share_entry); - - /* Now relock. */ - lock_share_entry(conn, dev, inode); - - if (!opb_ret) { - DEBUG(0,("open_mode_check: FAILED when breaking " - "oplock (%x) on file %s, dev = %x, " - "inode = %.0f\n", - old_shares[i].op_type, fname, - (unsigned int)dev, (double)inode)); - SAFE_FREE(old_shares); - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); - return -1; - } - - broken_entry = SMB_MALLOC_P(struct share_mode_entry_list); - if (!broken_entry) { - smb_panic("open_mode_check: malloc fail.\n"); - } - broken_entry->entry = *share_entry; - DLIST_ADD(broken_entry_list, broken_entry); - broke_oplock = True; - - } /* end for */ - - if (broke_oplock) { - /* Update the current open table. */ - SAFE_FREE(old_shares); - num_share_modes = get_share_modes(dev, inode, - &old_shares, - &delete_on_close); + if (!is_valid_share_mode_entry(&lck->share_modes[i])) { + continue; } - if (lp_share_modes(SNUM(conn))) { - /* Now we check the share modes, after any oplock breaks. */ - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &old_shares[i]; - - /* someone else has a share lock on it, check to see - * if we can too */ - if (share_conflict(share_entry, access_mask, - share_access)) { - SAFE_FREE(old_shares); - free_broken_entry_list(broken_entry_list); - errno = EACCES; - return -1; - } - } + /* someone else has a share lock on it, check to see if we can + * too */ + if (share_conflict(&lck->share_modes[i], + access_mask, share_access)) { + return NT_STATUS_SHARING_VIOLATION; } - - for(broken_entry = broken_entry_list; broken_entry; - broken_entry = broken_entry->next) { - oplock_contention_count++; - - /* Paranoia check that this is no longer an exlusive entry. */ - for(i = 0; i < num_share_modes; i++) { - share_mode_entry *share_entry = &old_shares[i]; - - if (!(share_modes_identical(&broken_entry->entry, - share_entry) && - EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type))) { - continue; - } - - /* - * This should not happen. The target left this oplock - * as exlusive.... The process *must* be dead.... - */ - - DEBUG(0,("open_mode_check: exlusive oplock left by " - "process %d after break ! For file %s, " - "dev = %x, inode = %.0f. Deleting it to " - "continue...\n", - (int)broken_entry->entry.pid, fname, - (unsigned int)dev, (double)inode)); - - if (process_exists(broken_entry->entry.pid)) { - DEBUG(0,("open_mode_check: Existent process " - "%lu left active oplock.\n", - (unsigned long)broken_entry->entry.pid )); - } - - if (del_share_entry(dev, inode, &broken_entry->entry, - NULL, &delete_on_close) == -1) { - free_broken_entry_list(broken_entry_list); - errno = EACCES; - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); - return -1; - } - - /* - * We must reload the share modes after deleting the - * other process's entry. - */ - - SAFE_FREE(old_shares); - num_share_modes = get_share_modes(dev, inode, - &old_shares, - &delete_on_close); - break; - } /* end for paranoia... */ - } /* end for broken_entry */ - free_broken_entry_list(broken_entry_list); - } while(broke_oplock); - - /* - * Refuse to grant an oplock in case the contention limit is - * reached when going through the lock list multiple times. - */ - - if(oplock_contention_count >= lp_oplock_contention_limit(SNUM(conn))) { - *p_oplock_request = 0; - DEBUG(4,("open_mode_check: oplock contention = %d. Not granting oplock.\n", - oplock_contention_count )); } - SAFE_FREE(old_shares); - return num_share_modes; + return NT_STATUS_OK; } -/**************************************************************************** - Delete the record for a handled deferred open entry. -****************************************************************************/ +static BOOL is_delete_request(files_struct *fsp) { + return ((fsp->access_mask == DELETE_ACCESS) && + (fsp->oplock_type == NO_OPLOCK)); +} -static void delete_defered_open_entry_record(connection_struct *conn, - SMB_DEV_T dev, - SMB_INO_T inode) +/* + * 1) No files open at all: Grant whatever the client wants. + * + * 2) Exclusive (or batch) oplock around: If the requested access is a delete + * request, break if the oplock around is a batch oplock. If it's another + * requested access type, break. + * + * 3) Only level2 around: Grant level2 and do nothing else. + */ + +static BOOL delay_for_oplocks(struct share_mode_lock *lck, files_struct *fsp) { - uint16 mid = get_current_mid(); - pid_t mypid = sys_getpid(); - deferred_open_entry *de_array = NULL; - int num_de_entries, i; + int i, num_level2; + struct share_mode_entry *exclusive = NULL; + BOOL delay_it = False; + BOOL have_level2 = False; - if (!lp_defer_sharing_violations()) { - return; + if (is_stat_open(fsp->access_mask)) { + fsp->oplock_type = NO_OPLOCK; + return False; } - num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); - for (i = 0; i < num_de_entries; i++) { - deferred_open_entry *entry = &de_array[i]; - if (entry->pid == mypid && entry->mid == mid && entry->dev == dev && - entry->inode == inode) { + num_level2 = 0; - /* Remove the deferred open entry from the array. */ - delete_deferred_open_entry(entry); - SAFE_FREE(de_array); - return; + if (lck->num_share_modes == 0) { + /* No files open at all: Directly grant whatever the client + * wants. */ + + if (fsp->oplock_type == NO_OPLOCK) { + /* Store a level2 oplock, but don't tell the client */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } + return False; + } + + for (i=0; i<lck->num_share_modes; i++) { + + if (!is_valid_share_mode_entry(&lck->share_modes[i])) { + continue; + } + + if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) { + SMB_ASSERT(exclusive == NULL); + exclusive = &lck->share_modes[i]; + } + + if (lck->share_modes[i].op_type == LEVEL_II_OPLOCK) { + have_level2 = True; } } - SAFE_FREE(de_array); + + if (exclusive != NULL) { /* Found an exclusive oplock */ + SMB_ASSERT(!have_level2); + delay_it = is_delete_request(fsp) ? + BATCH_OPLOCK_TYPE(exclusive->op_type) : True; + } + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + /* We can at most grant level2 */ + fsp->oplock_type = LEVEL_II_OPLOCK; + } + + if ((fsp->oplock_type == NO_OPLOCK) && have_level2) { + /* Store a level2 oplock, but don't tell the client */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } + + if (delay_it) { + DEBUG(10, ("Sending break request to PID %s\n", + procid_str_static(&exclusive->pid))); + exclusive->op_mid = get_current_mid(); + if (!message_send_pid(exclusive->pid, MSG_SMB_BREAK_REQUEST, + exclusive, sizeof(*exclusive), True)) { + DEBUG(3, ("Could not send oplock break message\n")); + } + file_free(fsp); + } + + return delay_it; +} + +static BOOL request_timed_out(struct timeval request_time, + struct timeval timeout) +{ + struct timeval now, end_time; + GetTimeOfDay(&now); + end_time = timeval_sum(&request_time, &timeout); + return (timeval_compare(&end_time, &now) < 0); } /**************************************************************************** Handle the 1 second delay in returning a SHARING_VIOLATION error. ****************************************************************************/ -static void defer_open_sharing_error(connection_struct *conn, - struct timeval *ptv, - const char *fname, - SMB_DEV_T dev, - SMB_INO_T inode) +static void defer_open(struct share_mode_lock *lck, + struct timeval request_time, + struct timeval timeout, + struct deferred_open_record *state) { uint16 mid = get_current_mid(); - pid_t mypid = sys_getpid(); - deferred_open_entry *de_array = NULL; - int num_de_entries, i; - struct dev_inode_bundle dib; + int i; - if (!lp_defer_sharing_violations()) { - return; - } + /* Paranoia check */ - dib.dev = dev; - dib.inode = inode; + for (i=0; i<lck->num_share_modes; i++) { + struct share_mode_entry *e = &lck->share_modes[i]; - num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); - for (i = 0; i < num_de_entries; i++) { - deferred_open_entry *entry = &de_array[i]; - if (entry->pid == mypid && entry->mid == mid) { - /* - * Check if a 1 second timeout has expired. - */ - if (usec_time_diff(ptv, &entry->time) > - SHARING_VIOLATION_USEC_WAIT) { - DEBUG(10,("defer_open_sharing_error: Deleting " - "deferred open entry for mid %u, " - "file %s\n", - (unsigned int)mid, fname )); - - /* Expired, return a real error. */ - /* Remove the deferred open entry from the array. */ - - delete_deferred_open_entry(entry); - SAFE_FREE(de_array); - return; - } - /* - * If the timeout hasn't expired yet and we still have - * a sharing violation, just leave the entry in the - * deferred open array alone. We do need to reschedule - * this open call though (with the original created - * time). - */ - DEBUG(10,("defer_open_sharing_error: time [%u.%06u] " - "updating deferred open entry for mid %u, file %s\n", - (unsigned int)entry->time.tv_sec, - (unsigned int)entry->time.tv_usec, - (unsigned int)mid, fname )); - - push_sharing_violation_open_smb_message(&entry->time, - (char *)&dib, - sizeof(dib)); - SAFE_FREE(de_array); - return; + if (!is_deferred_open_entry(e)) { + continue; + } + + if (procid_is_me(&e->pid) && (e->op_mid == mid)) { + DEBUG(0, ("Trying to defer an already deferred " + "request: mid=%d, exiting\n", mid)); + exit_server("exiting"); } } + /* End paranoia check */ + DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred " - "open entry for mid %u, file %s\n", - (unsigned int)ptv->tv_sec, (unsigned int)ptv->tv_usec, - (unsigned int)mid, fname )); + "open entry for mid %u\n", + (unsigned int)request_time.tv_sec, + (unsigned int)request_time.tv_usec, + (unsigned int)mid)); - if (!push_sharing_violation_open_smb_message(ptv, (char *)&dib, sizeof(dib))) { - SAFE_FREE(de_array); - return; - } - if (!add_deferred_open(mid, ptv, dev, inode, global_oplock_port, fname)) { - remove_sharing_violation_open_smb_message(mid); + if (!push_deferred_smb_message(mid, request_time, timeout, + (char *)state, sizeof(*state))) { + exit_server("push_deferred_smb_message failed\n"); } + add_deferred_open(lck, mid, request_time, state->dev, state->inode); /* * Push the MID of this packet on the signing queue. @@ -888,8 +756,6 @@ static void defer_open_sharing_error(connection_struct *conn, */ srv_defer_sign_response(mid); - - SAFE_FREE(de_array); } /**************************************************************************** @@ -1196,8 +1062,6 @@ files_struct *open_file_ntcreate(connection_struct *conn, BOOL internal_only_open = False; SMB_DEV_T dev = 0; SMB_INO_T inode = 0; - int num_share_modes = 0; - BOOL all_current_opens_are_level_II = False; BOOL fsp_open = False; files_struct *fsp = NULL; mode_t new_unx_mode = (mode_t)0; @@ -1205,8 +1069,11 @@ files_struct *open_file_ntcreate(connection_struct *conn, int info; uint32 existing_dos_attributes = 0; struct pending_message_list *pml = NULL; - uint16 port = 0; uint16 mid = get_current_mid(); + BOOL delayed_for_oplocks = False; + struct timeval request_time = timeval_zero(); + struct share_mode_lock *lck = NULL; + NTSTATUS status; if (conn->printer) { /* @@ -1241,9 +1108,11 @@ files_struct *open_file_ntcreate(connection_struct *conn, } if ((pml = get_open_deferred_message(mid)) != NULL) { - struct dev_inode_bundle dib; + struct deferred_open_record *state = + (struct deferred_open_record *)pml->private_data.data; - memcpy(&dib, pml->private_data.data, sizeof(dib)); + request_time = pml->request_time; + delayed_for_oplocks = state->delayed_for_oplocks; /* There could be a race condition where the dev/inode pair has changed since we deferred the message. If so, just @@ -1255,24 +1124,18 @@ files_struct *open_file_ntcreate(connection_struct *conn, notified of a close and we don't want to trigger another spurious oplock break. */ - if (!file_existed || dib.dev != psbuf->st_dev || - dib.inode != psbuf->st_ino || pml->msg_time.tv_sec || - pml->msg_time.tv_usec) { - /* Ensure we don't reprocess this message. */ - remove_sharing_violation_open_smb_message(mid); - - /* Now remove the deferred open entry under lock. */ - lock_share_entry(conn, dib.dev, dib.inode); - delete_defered_open_entry_record(conn, dib.dev, - dib.inode); - unlock_share_entry(conn, dib.dev, dib.inode); - - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); - return NULL; + /* Now remove the deferred open entry under lock. */ + lck = get_share_mode_lock(NULL, state->dev, state->inode, + fname); + if (lck == NULL) { + DEBUG(0, ("could not get share mode lock\n")); + } else { + del_deferred_open_entry(lck, mid); + talloc_destroy(lck); } + /* Ensure we don't reprocess this message. */ - remove_sharing_violation_open_smb_message(mid); + remove_deferred_open_smb_message(mid); } if (!check_name(fname,conn)) { @@ -1285,7 +1148,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, } /* ignore any oplock requests if oplocks are disabled */ - if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break) { + if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break || + IS_VETO_OPLOCK_PATH(conn, fname)) { oplock_request = 0; } @@ -1325,7 +1189,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, DEBUG(5,("open_file_ntcreate: FILE_OPEN " "requested for file %s and file " "doesn't exist.\n", fname )); - set_saved_error_triple(ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_NOT_FOUND); errno = ENOENT; return NULL; } @@ -1338,7 +1202,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, DEBUG(5,("open_file_ntcreate: FILE_OVERWRITE " "requested for file %s and file " "doesn't exist.\n", fname )); - set_saved_error_triple(ERRDOS, ERRbadfile, NT_STATUS_OBJECT_NAME_NOT_FOUND); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_NOT_FOUND); errno = ENOENT; return NULL; } @@ -1369,8 +1233,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, break; default: - set_saved_error_triple(ERRDOS, ERRinvalidparam, - NT_STATUS_INVALID_PARAMETER); + set_saved_ntstatus(NT_STATUS_INVALID_PARAMETER); return NULL; } @@ -1447,8 +1310,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, DEBUG(5,("open_file_ntcreate: write access requested for " "file %s on read only %s\n", fname, !CAN_WRITE(conn) ? "share" : "file" )); - set_saved_error_triple(ERRDOS, ERRnoaccess, - NT_STATUS_ACCESS_DENIED); + set_saved_ntstatus(NT_STATUS_ACCESS_DENIED); errno = EACCES; return NULL; } @@ -1458,45 +1320,96 @@ files_struct *open_file_ntcreate(connection_struct *conn, return NULL; } + fsp->dev = psbuf->st_dev; + fsp->inode = psbuf->st_ino; + fsp->share_access = share_access; + fsp->fh->private_options = create_options; + fsp->access_mask = access_mask; + fsp->oplock_type = oplock_request; + + if (timeval_is_zero(&request_time)) { + request_time = fsp->open_time; + } + if (file_existed) { dev = psbuf->st_dev; inode = psbuf->st_ino; - lock_share_entry(conn, dev, inode); - - num_share_modes = open_mode_check(conn, fname, dev, inode, - access_mask, share_access, - create_options, - &oplock_request, - &all_current_opens_are_level_II); - if(num_share_modes == -1) { - - if (!internal_only_open) { - NTSTATUS status; - get_saved_error_triple(NULL, NULL, &status); - if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) { - /* Check if this can be done with the - * deny_dos and fcb calls. */ - if (create_options & - (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| - NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { - files_struct *fsp_dup; - fsp_dup = fcb_or_dos_open(conn, fname, dev, - inode, access_mask, - share_access, - create_options); - - if (fsp_dup) { - unlock_share_entry(conn, dev, inode); - file_free(fsp); - if (pinfo) { - *pinfo = FILE_WAS_OPENED; - } - conn->num_files_open++; - return fsp_dup; - } + lck = get_share_mode_lock(NULL, dev, inode, fname); + + if (lck == NULL) { + DEBUG(0, ("Could not get share mode lock\n")); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); + return NULL; + } + + if (delay_for_oplocks(lck, fsp)) { + struct deferred_open_record state; + struct timeval timeout; + + if (delayed_for_oplocks) { + DEBUG(0, ("Trying to delay for oplocks " + "twice\n")); + exit_server("exiting"); + } + + timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0); + + /* Normally the smbd we asked should respond within + * OPLOCK_BREAK_TIMEOUT seconds regardless of whether + * the client did, give twice the timeout as a safety + * measure here in case the other smbd is stuck + * somewhere else. */ + + state.delayed_for_oplocks = True; + state.dev = dev; + state.inode = inode; + + if (!request_timed_out(request_time, timeout)) { + defer_open(lck, request_time, timeout, + &state); + } + + talloc_free(lck); + return NULL; + } + + status = open_mode_check(conn, fname, lck, + 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 */ + set_saved_ntstatus(status); + talloc_free(lck); + file_free(fsp); + return NULL; + } + + if (!NT_STATUS_IS_OK(status)) { + + SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)); + + /* Check if this can be done with the deny_dos and fcb + * calls. */ + if (create_options & + (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| + NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { + files_struct *fsp_dup; + fsp_dup = fcb_or_dos_open(conn, fname, dev, + inode, access_mask, + share_access, + create_options); + + if (fsp_dup) { + talloc_free(lck); + file_free(fsp); + if (pinfo) { + *pinfo = FILE_WAS_OPENED; } + conn->num_files_open++; + return fsp_dup; } } @@ -1527,8 +1440,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, if (!fsp_open && errno) { /* Default error. */ - set_saved_error_triple(ERRDOS, ERRnoaccess, - NT_STATUS_ACCESS_DENIED); + set_saved_ntstatus(NT_STATUS_ACCESS_DENIED); } /* @@ -1536,27 +1448,32 @@ files_struct *open_file_ntcreate(connection_struct *conn, * cope with the braindead 1 second delay. */ - if (!internal_only_open) { - NTSTATUS status; - get_saved_error_triple(NULL, NULL, &status); - if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) { - /* The fsp->open_time here represents - * the current time of day. */ - defer_open_sharing_error(conn, - &fsp->open_time, - fname, dev, inode); + if (!internal_only_open && + lp_defer_sharing_violations()) { + struct timeval timeout; + struct deferred_open_record state; + + timeout = timeval_set(0, SHARING_VIOLATION_USEC_WAIT); + + state.delayed_for_oplocks = False; + state.dev = dev; + state.inode = inode; + + if (!request_timed_out(request_time, + timeout)) { + defer_open(lck, request_time, timeout, + &state); } } - unlock_share_entry(conn, dev, inode); + talloc_free(lck); if (fsp_open) { fd_close(conn, fsp); /* * We have detected a sharing violation here * so return the correct error code */ - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); } file_free(fsp); return NULL; @@ -1567,23 +1484,28 @@ files_struct *open_file_ntcreate(connection_struct *conn, */ } + SMB_ASSERT(!file_existed || (lck != NULL)); + /* * Ensure we pay attention to default ACLs on directories if required. */ if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) && - (def_acl = directory_has_default_acl(conn, parent_dirname(fname)))) { + (def_acl = directory_has_default_acl(conn, + parent_dirname(fname)))) { unx_mode = 0777; } DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n", - (unsigned int)flags,(unsigned int)flags2,(unsigned int)unx_mode)); + (unsigned int)flags, (unsigned int)flags2, + (unsigned int)unx_mode)); /* * open_file strips any O_TRUNC flags itself. */ - fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,unx_mode,access_mask); + fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,unx_mode, + access_mask); if (!fsp_open && (flags2 & O_EXCL) && (errno == EEXIST)) { /* @@ -1602,23 +1524,24 @@ files_struct *open_file_ntcreate(connection_struct *conn, } if (!fsp_open) { - if(file_existed) { - unlock_share_entry(conn, dev, inode); + if (lck != NULL) { + talloc_free(lck); } file_free(fsp); return NULL; } - /* - * 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. - */ - if (!file_existed) { /* + * 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. + */ + + /* * 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 @@ -1628,70 +1551,41 @@ files_struct *open_file_ntcreate(connection_struct *conn, dev = fsp->dev; inode = fsp->inode; - lock_share_entry_fsp(fsp); - - num_share_modes = open_mode_check(conn, fname, dev, inode, - access_mask, share_access, - create_options, - &oplock_request, - &all_current_opens_are_level_II); - - if(num_share_modes == -1) { - NTSTATUS status; - get_saved_error_triple(NULL, NULL, &status); - if (NT_STATUS_EQUAL(status,NT_STATUS_SHARING_VIOLATION)) { - /* Check if this can be done with the deny_dos - * and fcb calls. */ - if (create_options & - (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| - NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { - files_struct *fsp_dup; - fsp_dup = fcb_or_dos_open(conn, fname, dev, inode, - access_mask, share_access, - create_options); - if (fsp_dup) { - unlock_share_entry(conn, dev, inode); - fd_close(conn, fsp); - file_free(fsp); - if (pinfo) { - *pinfo = FILE_WAS_OPENED; - } - conn->num_files_open++; - return fsp_dup; - } - } - - /* - * If we're returning a share violation, - * ensure we cope with the braindead 1 second - * delay. - */ - - /* The fsp->open_time here represents the - * current time of day. */ - defer_open_sharing_error(conn, &fsp->open_time, - fname, dev, inode); - } + lck = get_share_mode_lock(NULL, dev, inode, fname); - unlock_share_entry_fsp(fsp); - fd_close(conn,fsp); + if (lck == NULL) { + DEBUG(0, ("Coult not get share mode lock\n")); + fd_close(conn, fsp); file_free(fsp); - /* - * We have detected a sharing violation here, so - * return the correct code. - */ - set_saved_error_triple(ERRDOS, ERRbadshare, - NT_STATUS_SHARING_VIOLATION); + set_saved_ntstatus(NT_STATUS_SHARING_VIOLATION); return NULL; } - /* - * If there are any share modes set then the file *did* - * exist. Ensure we return the correct value for action. - */ + status = open_mode_check(conn, fname, lck, + access_mask, share_access, + create_options, &file_existed); + + if (!NT_STATUS_IS_OK(status)) { + struct deferred_open_record state; - if (num_share_modes > 0) { - file_existed = True; + fd_close(conn, fsp); + file_free(fsp); + + state.delayed_for_oplocks = False; + state.dev = dev; + state.inode = inode; + + /* 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... */ + + defer_open(lck, request_time, timeval_zero(), + &state); + talloc_free(lck); + return NULL; } /* @@ -1699,6 +1593,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, */ } + SMB_ASSERT(lck != NULL); + /* note that we ignore failure for the following. It is basically a hack for NFS, and NFS will never set one of these only read them. Nobody but Samba can ever set a deny @@ -1725,7 +1621,7 @@ files_struct *open_file_ntcreate(connection_struct *conn, */ if ((SMB_VFS_FTRUNCATE(fsp,fsp->fh->fd,0) == -1) || (SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf)==-1)) { - unlock_share_entry_fsp(fsp); + talloc_free(lck); fd_close(conn,fsp); file_free(fsp); return NULL; @@ -1761,20 +1657,14 @@ files_struct *open_file_ntcreate(connection_struct *conn, * file structs. */ - if(oplock_request && (num_share_modes == 0) && - !IS_VETO_OPLOCK_PATH(conn,fname) && - set_file_oplock(fsp, oplock_request) ) { - port = global_oplock_port; - } else if (oplock_request && all_current_opens_are_level_II) { - port = global_oplock_port; - oplock_request = LEVEL_II_OPLOCK; - set_file_oplock(fsp, oplock_request); - } else { - port = 0; - oplock_request = 0; + if ((fsp->oplock_type != NO_OPLOCK) && + (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) { + if (!set_file_oplock(fsp, fsp->oplock_type)) { + /* Could not get the kernel oplock */ + fsp->oplock_type = NO_OPLOCK; + } } - - set_share_mode(fsp, port, oplock_request); + set_share_mode(lck, fsp, 0, fsp->oplock_type); if (create_options & FILE_DELETE_ON_CLOSE) { uint32 dosattr= existing_dos_attributes; @@ -1788,19 +1678,16 @@ files_struct *open_file_ntcreate(connection_struct *conn, result = can_set_delete_on_close(fsp, True, dosattr); if (!NT_STATUS_IS_OK(result)) { - uint8 u_e_c; - uint32 u_e_code; - BOOL dummy_del_on_close; /* Remember to delete the mode we just added. */ - del_share_mode(fsp, NULL, &dummy_del_on_close); - unlock_share_entry_fsp(fsp); + del_share_mode(lck, fsp); + talloc_free(lck); fd_close(conn,fsp); file_free(fsp); - ntstatus_to_dos(result, &u_e_c, &u_e_code); - set_saved_error_triple(u_e_c, u_e_code, result); + set_saved_ntstatus(result); return NULL; } - set_delete_on_close(fsp, True); + lck->delete_on_close = True; + lck->modified = True; } if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED || @@ -1860,8 +1747,8 @@ files_struct *open_file_ntcreate(connection_struct *conn, /* If this is a successful open, we must remove any deferred open * records. */ - delete_defered_open_entry_record(conn, fsp->dev, fsp->inode); - unlock_share_entry_fsp(fsp); + del_deferred_open_entry(lck, mid); + talloc_free(lck); conn->num_files_open++; @@ -1945,17 +1832,13 @@ files_struct *open_directory(connection_struct *conn, if (is_ntfs_stream_name(fname)) { DEBUG(0,("open_directory: %s is a stream name!\n", fname )); - /* NB. Is the DOS error ERRbadpath or ERRbaddirectory ? */ - set_saved_error_triple(ERRDOS, ERRbadpath, - NT_STATUS_NOT_A_DIRECTORY); + set_saved_ntstatus(NT_STATUS_NOT_A_DIRECTORY); return NULL; } if (dir_existed && !S_ISDIR(psbuf->st_mode)) { DEBUG(0,("open_directory: %s is not a directory !\n", fname )); - /* NB. Is the DOS error ERRbadpath or ERRbaddirectory ? */ - set_saved_error_triple(ERRDOS, ERRbadpath, - NT_STATUS_NOT_A_DIRECTORY); + set_saved_ntstatus(NT_STATUS_NOT_A_DIRECTORY); return NULL; } @@ -1967,8 +1850,7 @@ files_struct *open_directory(connection_struct *conn, DEBUG(5,("open_directory: FILE_OPEN requested " "for directory %s and it doesn't " "exist.\n", fname )); - set_saved_error_triple(ERRDOS, ERRbadfile, - NT_STATUS_OBJECT_NAME_NOT_FOUND); + set_saved_ntstatus(NT_STATUS_OBJECT_NAME_NOT_FOUND); return NULL; } info = FILE_WAS_OPENED; @@ -2008,8 +1890,7 @@ files_struct *open_directory(connection_struct *conn, "0x%x for directory %s\n", (unsigned int)create_disposition, fname)); file_free(fsp); - set_saved_error_triple(ERRDOS, ERRinvalidparam, - NT_STATUS_INVALID_PARAMETER); + set_saved_ntstatus(NT_STATUS_INVALID_PARAMETER); return NULL; } |