diff options
Diffstat (limited to 'source3')
-rw-r--r-- | source3/smbd/open.c | 115 |
1 files changed, 87 insertions, 28 deletions
diff --git a/source3/smbd/open.c b/source3/smbd/open.c index cc6f014ceb..ab5ea236fa 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -364,6 +364,8 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i int deny_mode = GET_DENY_MODE(share_mode); int old_open_mode = GET_OPEN_MODE(share->share_mode); int old_deny_mode = GET_DENY_MODE(share->share_mode); + BOOL non_io_open_request; + BOOL non_io_open_existing; /* * share modes = false means don't bother to check for @@ -373,6 +375,18 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i if(!lp_share_modes(SNUM(conn))) return True; + if (desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) { + non_io_open_request = False; + } else { + non_io_open_request = True; + } + + if (share->desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) { + non_io_open_existing = False; + } else { + non_io_open_existing = True; + } + /* * Don't allow any opens once the delete on close flag has been * set. @@ -411,8 +425,7 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i * and the existing desired_acces then share modes don't conflict. */ - if ( !(desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) && - !(share->desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ) { + if (non_io_open_request && non_io_open_existing) { /* * Wrinkle discovered by smbtorture.... @@ -436,6 +449,13 @@ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, i and existing desired access (0x%x) are non-data opens\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access )); return True; + } else if (non_io_open_request || non_io_open_existing) { + /* + * If either are non-io opens then share modes don't conflict. + */ + DEBUG(5,("check_share_mode: One non-io open. Allowing open on file %s as desired access (0x%x) doesn't conflict with\ +existing desired access (0x%x).\n", fname, (unsigned int)desired_access, (unsigned int)share->desired_access )); + return True; } /* @@ -537,6 +557,20 @@ static void validate_my_share_entries(int num, share_mode_entry *share_entry) } #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) +{ + 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); + } +} + /**************************************************************************** Deal with open deny mode and oplock break processing. Invarient: Share mode must be locked on entry and exit. @@ -554,7 +588,7 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T int oplock_contention_count = 0; share_mode_entry *old_shares = 0; BOOL fcbopen = False; - BOOL broke_oplock; + BOOL broke_oplock; if(GET_OPEN_MODE(share_mode) == DOS_OPEN_FCB) fcbopen = True; @@ -567,7 +601,6 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) && ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) { /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */ - *p_oplock_request = 0; return num_share_modes; } @@ -576,12 +609,14 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T */ do { - share_mode_entry broken_entry; - + 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++) { + BOOL cause_oplock_break = False; share_mode_entry *share_entry = &old_shares[i]; #if defined(DEVELOPER) @@ -596,9 +631,17 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T * it before continuing. */ - if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) || + /* Was this a delete this file request ? */ + if (!*p_oplock_request && desired_access == DELETE_ACCESS && + !BATCH_OPLOCK_TYPE(share_entry->op_type)) { + /* Don't break the oplock in this case. */ + cause_oplock_break = False; + } else if((*p_oplock_request && EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) || (!*p_oplock_request && (share_entry->op_type != NO_OPLOCK))) { - + cause_oplock_break = True; + } + + if(cause_oplock_break) { BOOL opb_ret; DEBUG(5,("open_mode_check: oplock_request = %d, breaking oplock (%x) on file %s, \ @@ -629,50 +672,65 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou return -1; } + broken_entry = malloc(sizeof(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; - broken_entry = *share_entry; - break; } else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) { *p_all_current_opens_are_level_II = False; } - + } /* end for */ + + if (broke_oplock) { + /* Update the current open table. */ + SAFE_FREE(old_shares); + num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + } + + /* 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 (!check_share_mode(conn, share_entry, share_mode, desired_access, fname, fcbopen, p_flags)) { SAFE_FREE(old_shares); + free_broken_entry_list(broken_entry_list); errno = EACCES; return -1; } - - } /* end for */ - - if(broke_oplock) { - SAFE_FREE(old_shares); - num_share_modes = get_share_modes(conn, dev, inode, &old_shares); + } + + 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, share_entry) && - EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) { + if (share_modes_identical(&broken_entry->entry, share_entry) && + EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type) ) { /* * 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.pid, fname, (unsigned int)dev, (double)inode)); + 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.pid)) { + if (process_exists(broken_entry->entry.pid)) { DEBUG(0,("open_mode_check: Existent process %lu left active oplock.\n", - (unsigned long)broken_entry.pid )); + (unsigned long)broken_entry->entry.pid )); } - if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) { + if (del_share_entry(dev, inode, &broken_entry->entry, NULL) == -1) { + free_broken_entry_list(broken_entry_list); errno = EACCES; unix_ERR_class = ERRDOS; unix_ERR_code = ERRbadshare; @@ -690,8 +748,8 @@ dev = %x, inode = %.0f. Deleting it to continue...\n", (int)broken_entry.pid, fn break; } } /* end for paranoia... */ - } /* end if broke_oplock */ - + } /* end for broken_entry */ + free_broken_entry_list(broken_entry_list); } while(broke_oplock); if(old_shares != 0) @@ -939,9 +997,10 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ if (desired_access && ((desired_access & ~(SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES))==0) && ((desired_access & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES)) != 0)) { /* Stat open that doesn't trigger oplock breaks or share mode checks... ! JRA. */ - oplock_request = 0; - add_share_mode = False; + deny_mode = DENY_NONE; if (file_existed) { + oplock_request = 0; + add_share_mode = False; flags2 &= ~O_CREAT; } } |