diff options
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/close.c | 30 | ||||
-rw-r--r-- | source3/smbd/dir.c | 3 | ||||
-rw-r--r-- | source3/smbd/nttrans.c | 8 | ||||
-rw-r--r-- | source3/smbd/open.c | 200 | ||||
-rw-r--r-- | source3/smbd/oplock.c | 70 | ||||
-rw-r--r-- | source3/smbd/process.c | 217 | ||||
-rw-r--r-- | source3/smbd/reply.c | 32 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 8 |
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)); |