summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2004-06-08 16:14:31 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 10:51:54 -0500
commit2fc57c9a2ce3a266534dd20e6fed4883e052c557 (patch)
tree0b26f97c713129487ffcf34494119bc3dbe9176a /source3/smbd
parent386a11f49eff2ec60d75b9966ecdc5627b259f0d (diff)
downloadsamba-2fc57c9a2ce3a266534dd20e6fed4883e052c557.tar.gz
samba-2fc57c9a2ce3a266534dd20e6fed4883e052c557.tar.bz2
samba-2fc57c9a2ce3a266534dd20e6fed4883e052c557.zip
r1085: Now it's had some proper user testing, merge in the deferred open fix. I'm
still doing more testing, but it fixes a behaviour that we've been wrong on ever since the start of Samba. Jeremy. (This used to be commit 894cc6d16296b934c112786eec896846156aee5d)
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/close.c30
-rw-r--r--source3/smbd/dir.c3
-rw-r--r--source3/smbd/nttrans.c8
-rw-r--r--source3/smbd/open.c200
-rw-r--r--source3/smbd/oplock.c70
-rw-r--r--source3/smbd/process.c217
-rw-r--r--source3/smbd/reply.c32
-rw-r--r--source3/smbd/trans2.c8
8 files changed, 521 insertions, 47 deletions
diff --git a/source3/smbd/close.c b/source3/smbd/close.c
index 8b3010c1b2..305377dfc6 100644
--- a/source3/smbd/close.c
+++ b/source3/smbd/close.c
@@ -2,6 +2,7 @@
Unix SMB/CIFS implementation.
file closing
Copyright (C) Andrew Tridgell 1992-1998
+ Copyright (C) Jeremy Allison 1992-2004.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -105,6 +106,32 @@ static int close_filestruct(files_struct *fsp)
}
/****************************************************************************
+ If any deferred opens are waiting on this close, notify them.
+****************************************************************************/
+
+static void notify_deferred_opens(files_struct *fsp)
+{
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, i;
+ pid_t mypid = sys_getpid();
+
+ num_de_entries = get_deferred_opens(fsp->conn, fsp->dev, fsp->inode, &de_array);
+ for (i = 0; i < num_de_entries; i++) {
+ deferred_open_entry *entry = &de_array[i];
+ if (entry->pid == mypid) {
+ /*
+ * We need to notify ourself to retry the open.
+ * Do this by finding the queued SMB record, moving it
+ * to the head of the queue and changing the wait time to zero.
+ */
+ schedule_sharing_violation_open_smb_message(entry->mid);
+ } else {
+ send_deferred_open_retry_message(entry);
+ }
+ }
+}
+
+/****************************************************************************
Close a file.
If normal_close is 1 then this came from a normal SMBclose (or equivalent)
@@ -177,6 +204,9 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close)
SAFE_FREE(share_entry);
+ /* Notify any deferred opens waiting on this close. */
+ notify_deferred_opens(fsp);
+
/*
* NT can set delete_on_close of the last open
* reference to a file.
diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c
index 06ef23ab8c..b88f687766 100644
--- a/source3/smbd/dir.c
+++ b/source3/smbd/dir.c
@@ -763,7 +763,8 @@ static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_
return True;
else
fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action);
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY,
+ &access_mode, &smb_action);
if (!fsp)
return False;
diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index 26be4434fd..e540db234a 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -863,6 +863,10 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib
restore_case_semantics(conn, file_attributes);
END_PROFILE(SMBntcreateX);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
}
@@ -1347,6 +1351,10 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o
}
} else {
restore_case_semantics(conn, file_attributes);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index ab5ea236fa..04e074d56e 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2,7 +2,7 @@
Unix SMB/CIFS implementation.
file opening and share modes
Copyright (C) Andrew Tridgell 1992-1998
- Copyright (C) Jeremy Allison 2001
+ Copyright (C) Jeremy Allison 2001-2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,6 +26,11 @@ extern uint16 global_oplock_port;
extern uint16 global_smbpid;
extern BOOL global_client_failed_oplock_break;
+struct dev_inode_bundle {
+ SMB_DEV_T dev;
+ SMB_INO_T inode;
+};
+
/****************************************************************************
fd support routines - attempt to do a dos_open.
****************************************************************************/
@@ -243,7 +248,7 @@ static BOOL open_file(files_struct *fsp,connection_struct *conn,
}
/*******************************************************************
-return True if the filename is one of the special executable types
+ Return True if the filename is one of the special executable types.
********************************************************************/
static BOOL is_executable(const char *fname)
@@ -262,12 +267,13 @@ static BOOL is_executable(const char *fname)
enum {AFAIL,AREAD,AWRITE,AALL};
/*******************************************************************
-reproduce the share mode access table
-this is horrendoously complex, and really can't be justified on any
-rational grounds except that this is _exactly_ what NT does. See
-the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
-test routines.
+ Reproduce the share mode access table.
+ This is horrendoously complex, and really can't be justified on any
+ rational grounds except that this is _exactly_ what NT does. See
+ the DENY1 and DENY2 tests in smbtorture for a comprehensive set of
+ test routines.
********************************************************************/
+
static int access_table(int new_deny,int old_deny,int old_mode,
BOOL same_pid, BOOL isexe)
{
@@ -353,9 +359,8 @@ static int access_table(int new_deny,int old_deny,int old_mode,
return(AFAIL);
}
-
/****************************************************************************
-check if we can open a file with a share mode
+ Check if we can open a file with a share mode.
****************************************************************************/
static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access,
@@ -770,9 +775,101 @@ after break ! For file %s, dev = %x, inode = %.0f. Deleting it to continue...\n"
}
/****************************************************************************
-set a kernel flock on a file for NFS interoperability
-this requires a patch to Linux
+ Delete the record for a handled deferred open entry.
+****************************************************************************/
+
+static void delete_defered_open_entry_record(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode)
+{
+ uint16 mid = get_current_mid();
+ pid_t mypid = sys_getpid();
+ deferred_open_entry *de_array = NULL;
+ int num_de_entries, 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 && entry->dev == dev &&
+ entry->inode == inode) {
+
+ /* Remove the deferred open entry from the array. */
+ delete_deferred_open_entry(entry);
+ SAFE_FREE(de_array);
+ return;
+ }
+ }
+ SAFE_FREE(de_array);
+}
+
+/****************************************************************************
+ Handle the 1 second delay in returning a SHARING_VIOLATION error.
+****************************************************************************/
+
+void defer_open_sharing_error(connection_struct *conn, struct timeval *ptv,
+ char *fname, SMB_DEV_T dev, SMB_INO_T inode)
+{
+ 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;
+
+ dib.dev = dev;
+ dib.inode = inode;
+
+ 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;
+ }
+ }
+
+ 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 ));
+
+ 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);
+ }
+ SAFE_FREE(de_array);
+}
+
+/****************************************************************************
+ Set a kernel flock on a file for NFS interoperability.
+ This requires a patch to Linux.
****************************************************************************/
+
static void kernel_flock(files_struct *fsp, int deny_mode)
{
#if HAVE_KERNEL_SHARE_MODES
@@ -847,6 +944,7 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
BOOL fcbopen = False;
BOOL def_acl = False;
BOOL add_share_mode = True;
+ BOOL internal_only_open = False;
SMB_DEV_T dev = 0;
SMB_INO_T inode = 0;
int num_share_modes = 0;
@@ -858,9 +956,50 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_
mode_t new_mode = (mode_t)0;
int action;
uint32 existing_dos_mode = 0;
+ struct pending_message_list *pml = NULL;
+ uint16 mid = get_current_mid();
/* We add aARCH to this as this mode is only used if the file is created new. */
mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname);
+ if (oplock_request == INTERNAL_OPEN_ONLY) {
+ internal_only_open = True;
+ oplock_request = 0;
+ }
+
+ if ((pml = get_open_deferred_message(mid)) != NULL) {
+ struct dev_inode_bundle dib;
+
+ memcpy(&dib, pml->private_data.data, sizeof(dib));
+
+ /* There could be a race condition where the dev/inode pair
+ has changed since we deferred the message. If so, just
+ remove the deferred open entry and return sharing violation. */
+
+ /* If the timeout value is non-zero, we need to just
+ return sharing violation. Don't retry the open
+ as we were not 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);
+
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ return NULL;
+ }
+ /* Ensure we don't reprocess this message. */
+ remove_sharing_violation_open_smb_message(mid);
+
+ }
+
if (conn->printer) {
/* printers are handled completely differently. Most of the passed parameters are
ignored */
@@ -1043,17 +1182,28 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED;
}
+ /*
+ * If we're returning a share violation, ensure we cope with
+ * the braindead 1 second delay.
+ */
+
+ if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,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);
+ }
+
unlock_share_entry(conn, dev, inode);
- if (fsp_open)
+ if (fsp_open) {
fd_close(conn, fsp);
+ /*
+ * We have detected a sharing violation here
+ * so return the correct error code
+ */
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
+ }
file_free(fsp);
- /*
- * We have detected a sharing violation here
- * so return the correct error code
- */
- unix_ERR_class = ERRDOS;
- unix_ERR_code = ERRbadshare;
- unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION;
return NULL;
}
@@ -1118,6 +1268,16 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
&flags, &oplock_request, &all_current_opens_are_level_II);
if(num_share_modes == -1) {
+ /*
+ * If we're returning a share violation, ensure we cope with
+ * the braindead 1 second delay.
+ */
+
+ if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,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);
+ }
+
unlock_share_entry_fsp(fsp);
fd_close(conn,fsp);
file_free(fsp);
@@ -1286,6 +1446,8 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n",
fname, (int)new_mode));
}
+ /* 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);
conn->num_files_open++;
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 19e6956d9e..6739f5654f 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -388,6 +388,30 @@ pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
(int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
break;
+ case RETRY_DEFERRED_OPEN_CMD:
+
+ /* Request to retry and open that would return SHARING_VIOLATION. */
+ if (msg_len != DEFERRED_OPEN_MSG_LEN) {
+ DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n",
+ (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN));
+ return False;
+ }
+ {
+ uint16 mid;
+
+ memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid));
+ memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode));
+ memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev));
+ memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid));
+
+ DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \
+pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n",
+ (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid));
+
+ schedule_sharing_violation_open_smb_message(mid);
+ }
+ break;
+
/*
* Keep this as a debug case - eventually we can remove it.
*/
@@ -1215,7 +1239,51 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
}
/****************************************************************************
-setup oplocks for this process
+ Send a 'retry your open' message to a process with a deferred open entry.
+****************************************************************************/
+
+BOOL send_deferred_open_retry_message(deferred_open_entry *entry)
+{
+ char de_msg[DEFERRED_OPEN_MSG_LEN];
+ struct sockaddr_in addr_out;
+ pid_t pid = sys_getpid();
+
+ memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN);
+ SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD);
+ memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid));
+ memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev));
+ memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode));
+ memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid));
+
+ /* Set the address and port. */
+ memset((char *)&addr_out,'\0',sizeof(addr_out));
+ addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ addr_out.sin_port = htons( entry->port );
+ addr_out.sin_family = AF_INET;
+
+ if( DEBUGLVL( 3 ) ) {
+ dbgtext( "send_deferred_open_retry_message: sending a message to ");
+ dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+ dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+ (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+ }
+
+ if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0,
+ (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+ if( DEBUGLVL( 0 ) ) {
+ dbgtext( "send_deferred_open_retry_message: failed sending a message to ");
+ dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+ dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+ (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+ dbgtext( "Error was %s\n", strerror(errno) );
+ }
+ return False;
+ }
+ return True;
+}
+
+/****************************************************************************
+ Setup oplocks for this process.
****************************************************************************/
BOOL init_oplocks(void)
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index ccebd2b86c..698c8475f7 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -61,20 +61,28 @@ uint16 get_current_mid(void)
for processing.
****************************************************************************/
-struct pending_message_list {
- struct pending_message_list *next, *prev;
- char *msg_buf;
- int msg_len;
-};
-
static struct pending_message_list *smb_oplock_queue;
+static struct pending_message_list *smb_sharing_violation_queue;
+
+enum q_type { OPLOCK_QUEUE, SHARE_VIOLATION_QUEUE };
+
+/****************************************************************************
+ Free up a message.
+****************************************************************************/
+
+static void free_queued_message(struct pending_message_list *msg)
+{
+ data_blob_free(&msg->buf);
+ data_blob_free(&msg->private_data);
+ SAFE_FREE(msg);
+}
/****************************************************************************
Function to push a message onto the tail of a linked list of smb messages ready
for processing.
****************************************************************************/
-static BOOL push_message(char *buf, int msg_len)
+static BOOL push_queued_message(enum q_type qt, char *buf, int msg_len, struct timeval *ptv, char *private, size_t private_len)
{
struct pending_message_list *tmp_msg;
struct pending_message_list *msg = (struct pending_message_list *)
@@ -85,32 +93,159 @@ static BOOL push_message(char *buf, int msg_len)
return False;
}
- msg->msg_buf = (char *)malloc(msg_len);
- if(msg->msg_buf == NULL) {
+ memset(msg,'\0',sizeof(*msg));
+
+ msg->buf = data_blob(buf, msg_len);
+ if(msg->buf.data == NULL) {
DEBUG(0,("push_message: malloc fail (2)\n"));
SAFE_FREE(msg);
return False;
}
- memcpy(msg->msg_buf, buf, msg_len);
- msg->msg_len = msg_len;
+ if (ptv) {
+ msg->msg_time = *ptv;
+ }
- DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg);
+ if (private) {
+ msg->private_data = data_blob(private, private_len);
+ if (msg->private_data.data == NULL) {
+ DEBUG(0,("push_message: malloc fail (3)\n"));
+ data_blob_free(&msg->buf);
+ SAFE_FREE(msg);
+ }
+ }
+
+ if (qt == OPLOCK_QUEUE) {
+ DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg);
+ } else {
+ DLIST_ADD_END(smb_sharing_violation_queue, msg, tmp_msg);
+ }
/* Push the MID of this packet on the signing queue. */
srv_defer_sign_response(SVAL(buf,smb_mid));
+ DEBUG(10,("push_message: pushed message length %u on queue %s\n",
+ (unsigned int)msg_len,
+ qt == OPLOCK_QUEUE ? "smb_oplock_queue" : "smb_sharing_violation_queue" ));
+
return True;
}
/****************************************************************************
- Function to push a smb message onto a linked list of local smb messages ready
+ Function to push an oplock smb message onto a linked list of local smb messages ready
for processing.
****************************************************************************/
BOOL push_oplock_pending_smb_message(char *buf, int msg_len)
{
- return push_message(buf, msg_len);
+ return push_queued_message(OPLOCK_QUEUE, buf, msg_len, NULL, NULL, 0);
+}
+
+/****************************************************************************
+ Function to delete a sharing violation open message by mid.
+****************************************************************************/
+
+void remove_sharing_violation_open_smb_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (mid == SVAL(pml->buf.data,smb_mid)) {
+ DEBUG(10,("remove_sharing_violation_open_smb_message: deleting mid %u len %u\n",
+ (unsigned int)mid, (unsigned int)pml->buf.length ));
+ DLIST_REMOVE(smb_sharing_violation_queue, pml);
+ free_queued_message(pml);
+ return;
+ }
+ }
+}
+
+/****************************************************************************
+ Move a sharing violation open retry message to the front of the list and
+ schedule it for immediate processing.
+****************************************************************************/
+
+void schedule_sharing_violation_open_smb_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+ int i = 0;
+
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ uint16 msg_mid = SVAL(pml->buf.data,smb_mid);
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: [%d] msg_mid = %u\n", i++,
+ (unsigned int)msg_mid ));
+ if (mid == msg_mid) {
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: scheduling mid %u\n",
+ mid ));
+ pml->msg_time.tv_sec = 0;
+ pml->msg_time.tv_usec = 0;
+ DLIST_PROMOTE(smb_sharing_violation_queue, pml);
+ return;
+ }
+ }
+
+ DEBUG(10,("schedule_sharing_violation_open_smb_message: failed to find message mid %u\n",
+ mid ));
+}
+
+/****************************************************************************
+ Return true if this mid is on the deferred queue.
+****************************************************************************/
+
+BOOL open_was_deferred(uint16 mid)
+{
+ struct pending_message_list *pml;
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (SVAL(pml->buf.data,smb_mid) == mid) {
+ return True;
+ }
+ }
+ return False;
+}
+
+/****************************************************************************
+ Return the message queued by this mid.
+****************************************************************************/
+
+struct pending_message_list *get_open_deferred_message(uint16 mid)
+{
+ struct pending_message_list *pml;
+ for (pml = smb_sharing_violation_queue; pml; pml = pml->next) {
+ if (SVAL(pml->buf.data,smb_mid) == mid) {
+ return pml;
+ }
+ }
+ return NULL;
+}
+
+/****************************************************************************
+ Function to push a sharing violation open smb message onto a linked list of local smb messages ready
+ for processing.
+****************************************************************************/
+
+BOOL push_sharing_violation_open_smb_message(struct timeval *ptv, char *private, size_t priv_len)
+{
+ uint16 mid = SVAL(InBuffer,smb_mid);
+ struct timeval tv;
+ SMB_BIG_INT tdif;
+
+ tv = *ptv;
+ tdif = tv.tv_sec;
+ tdif *= 1000000;
+ tdif += tv.tv_usec;
+
+ /* Add on the timeout. */
+ tdif += SHARING_VIOLATION_USEC_WAIT;
+
+ tv.tv_sec = tdif / 1000000;
+ tv.tv_usec = tdif % 1000000;
+
+ DEBUG(10,("push_sharing_violation_open_smb_message: pushing message len %u mid %u\
+ timeout time [%u.%06u]\n", (unsigned int) smb_len(InBuffer)+4, (unsigned int)mid,
+ (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec));
+
+ return push_queued_message(SHARE_VIOLATION_QUEUE, InBuffer,
+ smb_len(InBuffer)+4, &tv, private, priv_len);
}
/****************************************************************************
@@ -169,12 +304,17 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
fd_set fds;
int selrtn;
struct timeval to;
+ struct timeval *pto;
int maxfd;
smb_read_error = 0;
again:
+ to.tv_sec = timeout / 1000;
+ to.tv_usec = (timeout % 1000) * 1000;
+ pto = timeout > 0 ? &to : NULL;
+
/*
* Note that this call must be before processing any SMB
* messages as we need to synchronously process any messages
@@ -188,17 +328,55 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
*/
if(smb_oplock_queue != NULL) {
struct pending_message_list *msg = smb_oplock_queue;
- memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len));
+ memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
/* Free the message we just copied. */
DLIST_REMOVE(smb_oplock_queue, msg);
- SAFE_FREE(msg->msg_buf);
- SAFE_FREE(msg);
+ free_queued_message(msg);
DEBUG(5,("receive_message_or_smb: returning queued smb message.\n"));
return True;
}
+ /*
+ * Check to see if we already have a message on the deferred open queue
+ * and it's time to schedule.
+ */
+ if(smb_sharing_violation_queue != NULL) {
+ BOOL pop_message = False;
+ struct pending_message_list *msg = smb_sharing_violation_queue;
+
+ if (msg->msg_time.tv_sec == 0 && msg->msg_time.tv_usec == 0) {
+ pop_message = True;
+ } else {
+ struct timeval tv;
+ SMB_BIG_INT tdif;
+
+ GetTimeOfDay(&tv);
+ tdif = usec_time_diff(&msg->msg_time, &tv);
+ if (tdif <= 0) {
+ /* Timed out. Schedule...*/
+ pop_message = True;
+ DEBUG(10,("receive_message_or_smb: queued message timed out.\n"));
+ } else {
+ /* Make a more accurate select timeout. */
+ to.tv_sec = tdif / 1000000;
+ to.tv_usec = tdif % 1000000;
+ pto = &to;
+ DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n",
+ (unsigned int)pto->tv_sec, (unsigned int)pto->tv_usec ));
+ }
+ }
+
+ if (pop_message) {
+ memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length));
+
+ /* We leave this message on the queue so the open code can
+ know this is a retry. */
+ DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n"));
+ return True;
+ }
+ }
/*
* Setup the select read fd set.
@@ -229,10 +407,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout)
FD_SET(smbd_server_fd(),&fds);
maxfd = setup_oplock_select_set(&fds);
- to.tv_sec = timeout / 1000;
- to.tv_usec = (timeout % 1000) * 1000;
-
- selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL);
+ selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,pto);
/* if we get EINTR then maybe we have received an oplock
signal - treat this as select returning 1. This is ugly, but
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index fff5385171..bf208b0fa4 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -1043,6 +1043,10 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBopen);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1132,6 +1136,10 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
if (!fsp) {
END_PROFILE(SMBopenX);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1257,6 +1265,10 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBcreate);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1332,6 +1344,10 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
if (!fsp) {
END_PROFILE(SMBctemp);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess);
}
@@ -1623,8 +1639,13 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
DEBUG(3,("reply_unlink : %s\n",name));
status = unlink_internals(conn, dirtype, name);
- if (!NT_STATUS_IS_OK(status))
+ if (!NT_STATUS_IS_OK(status)) {
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return ERROR_NT(status);
+ }
/*
* Win2k needs a changenotify request response before it will
@@ -3944,6 +3965,10 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size,
status = rename_internals(conn, name, newname, attrs, False);
if (!NT_STATUS_IS_OK(status)) {
END_PROFILE(SMBmv);
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return ERROR_NT(status);
}
@@ -3989,7 +4014,8 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
return(False);
fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY),
- (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action);
+ (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,INTERNAL_OPEN_ONLY,
+ &Access,&action);
if (!fsp1)
return(False);
@@ -4002,7 +4028,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun,
ZERO_STRUCTP(&sbuf2);
fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY),
- ofun,dosattrs,0,&Access,&action);
+ ofun,dosattrs,INTERNAL_OPEN_ONLY,&Access,&action);
if (!fsp2) {
close_file(fsp1,False);
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index 02a6bf6e4b..0ba26a9147 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -627,6 +627,10 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i
oplock_request, &rmode,&smb_action);
if (!fsp) {
+ if (open_was_deferred(SVAL(inbuf,smb_mid))) {
+ /* We have re-scheduled this call. */
+ return -1;
+ }
return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess);
}
@@ -3205,7 +3209,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn,
SET_OPEN_MODE(DOS_OPEN_RDWR),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
FILE_ATTRIBUTE_NORMAL,
- 0, &access_mode, &action);
+ INTERNAL_OPEN_ONLY, &access_mode, &action);
if (new_fsp == NULL)
return(UNIXERROR(ERRDOS,ERRbadpath));
@@ -3655,7 +3659,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n",
SET_OPEN_MODE(DOS_OPEN_RDWR),
(FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
FILE_ATTRIBUTE_NORMAL,
- 0, &access_mode, &action);
+ INTERNAL_OPEN_ONLY, &access_mode, &action);
if (new_fsp == NULL)
return(UNIXERROR(ERRDOS,ERRbadpath));