summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2004-06-02 02:12:54 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 10:51:51 -0500
commitefc96df3d5b415ca9c19527a44d0619992712b6d (patch)
treee78e7fd75c7e8e08fe156f344e05cd43f7496ff0
parent03d4344432c6aa75d400afc501aec1a14070f35d (diff)
downloadsamba-efc96df3d5b415ca9c19527a44d0619992712b6d.tar.gz
samba-efc96df3d5b415ca9c19527a44d0619992712b6d.tar.bz2
samba-efc96df3d5b415ca9c19527a44d0619992712b6d.zip
r974: Fix open code to pass more torture tests. We now pass the Samba4
oplock test. We do this be changing the algorithm when breaking oplocks slightly. Previously we broke an oplock, then re-loaded the share modes and re-iterated. Now we break all oplocks, then re-load the share modes and check the share details - then iterate. This seems to match the way Win2k3 does it. Jeremy. (This used to be commit 5ec4f4e4e6596ea0f52aca5c9e1a75bf35612e37)
-rw-r--r--source3/smbd/open.c115
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;
}
}