diff options
author | Jeremy Allison <jra@samba.org> | 2006-07-18 01:05:51 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:38:12 -0500 |
commit | b737f26764cce935d9482335ece11c71a96720f4 (patch) | |
tree | e2d21c8b53815c90092437b2c4dc14bf5b34e560 | |
parent | 94ad8543bd27c6cbdcdaf4acf84d91438dbef28b (diff) | |
download | samba-b737f26764cce935d9482335ece11c71a96720f4.tar.gz samba-b737f26764cce935d9482335ece11c71a96720f4.tar.bz2 samba-b737f26764cce935d9482335ece11c71a96720f4.zip |
r17105: Fix the race Volker found - we had a non-locked
region between detecting a pending lock was needed
and when we added the blocking lock record. Make
sure that we hold the lock over all this period.
Removed the old code for doing blocking locks on
SMB requests that never block (the old SMBlock
and friends).
Discovered something interesting about the strange
NT_STATUS_FILE_LOCK_CONFLICT return. If we asked
for a lock with zero timeout, and we got an error
of NT_STATUS_FILE_LOCK_CONFLICT, treat it as though
it was a blocking lock with a timeout of 150 - 300ms.
This only happens when timeout is sent as zero and
can be seen quite clearly in ethereal. This is the
real replacement for old do_lock_spin() code.
Re-worked the blocking lock select timeout to correctly
use milliseconds instead of the old second level
resolution (far too coarse for this work).
Jeremy.
(This used to be commit b81d6d1ae95a3d3e449dde629884b565eac289d9)
-rw-r--r-- | source3/locking/brlock.c | 14 | ||||
-rw-r--r-- | source3/locking/locking.c | 22 | ||||
-rw-r--r-- | source3/smbd/blocking.c | 261 | ||||
-rw-r--r-- | source3/smbd/process.c | 13 | ||||
-rw-r--r-- | source3/smbd/reply.c | 134 | ||||
-rw-r--r-- | source3/smbd/trans2.c | 32 |
6 files changed, 161 insertions, 315 deletions
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index f251ff57ec..20bb4314b6 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -216,14 +216,14 @@ static BOOL brl_conflict_other(const struct lock_struct *lck1, const struct lock app depends on this ? ****************************************************************************/ -static NTSTATUS brl_lock_failed(files_struct *fsp, const struct lock_struct *lock, int32 lock_timeout) +static NTSTATUS brl_lock_failed(files_struct *fsp, const struct lock_struct *lock, BOOL blocking_lock) { if (lock->start >= 0xEF000000 && (lock->start >> 63) == 0) { /* amazing the little things you learn with a test suite. Locks beyond this offset (as a 64 bit number!) always generate the conflict error code, unless the top bit is set */ - if (lock_timeout == 0) { + if (!blocking_lock) { fsp->last_lock_failure = *lock; } return NT_STATUS_FILE_LOCK_CONFLICT; @@ -236,7 +236,7 @@ static NTSTATUS brl_lock_failed(files_struct *fsp, const struct lock_struct *loc return NT_STATUS_FILE_LOCK_CONFLICT; } - if (lock_timeout == 0) { + if (!blocking_lock) { fsp->last_lock_failure = *lock; } return NT_STATUS_LOCK_NOT_GRANTED; @@ -297,7 +297,7 @@ static int lock_compare(const struct lock_struct *lck1, ****************************************************************************/ static NTSTATUS brl_lock_windows(struct byte_range_lock *br_lck, - const struct lock_struct *plock, int32 lock_timeout) + const struct lock_struct *plock, BOOL blocking_lock) { unsigned int i; files_struct *fsp = br_lck->fsp; @@ -306,7 +306,7 @@ static NTSTATUS brl_lock_windows(struct byte_range_lock *br_lck, for (i=0; i < br_lck->num_locks; i++) { /* Do any Windows or POSIX locks conflict ? */ if (brl_conflict(&locks[i], plock)) { - return brl_lock_failed(fsp,plock,lock_timeout); + return brl_lock_failed(fsp,plock,blocking_lock); } #if ZERO_ZERO if (plock->start == 0 && plock->size == 0 && @@ -676,7 +676,7 @@ NTSTATUS brl_lock(struct byte_range_lock *br_lck, br_off size, enum brl_type lock_type, enum brl_flavour lock_flav, - int32 lock_timeout) + BOOL blocking_lock) { NTSTATUS ret; struct lock_struct lock; @@ -697,7 +697,7 @@ NTSTATUS brl_lock(struct byte_range_lock *br_lck, lock.lock_flav = lock_flav; if (lock_flav == WINDOWS_LOCK) { - ret = brl_lock_windows(br_lck, &lock, lock_timeout); + ret = brl_lock_windows(br_lck, &lock, blocking_lock); } else { ret = brl_lock_posix(br_lck, &lock); } diff --git a/source3/locking/locking.c b/source3/locking/locking.c index cd1d9547f3..a7cadd3a40 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -179,23 +179,25 @@ NTSTATUS query_lock(files_struct *fsp, Utility function called by locking requests. ****************************************************************************/ -NTSTATUS do_lock(files_struct *fsp, +struct byte_range_lock *do_lock(files_struct *fsp, uint32 lock_pid, SMB_BIG_UINT count, SMB_BIG_UINT offset, enum brl_type lock_type, enum brl_flavour lock_flav, - int32 lock_timeout) + BOOL blocking_lock, + NTSTATUS *perr) { struct byte_range_lock *br_lck = NULL; - NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED; if (!fsp->can_lock) { - return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + *perr = fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + return NULL; } if (!lp_locking(SNUM(fsp->conn))) { - return NT_STATUS_OK; + *perr = NT_STATUS_OK; + return NULL; } /* NOTE! 0 byte long ranges ARE allowed and should be stored */ @@ -206,20 +208,20 @@ NTSTATUS do_lock(files_struct *fsp, br_lck = brl_get_locks(NULL, fsp); if (!br_lck) { - return NT_STATUS_NO_MEMORY; + *perr = NT_STATUS_NO_MEMORY; + return NULL; } - status = brl_lock(br_lck, + *perr = brl_lock(br_lck, lock_pid, procid_self(), offset, count, lock_type, lock_flav, - lock_timeout); + blocking_lock); - TALLOC_FREE(br_lck); - return status; + return br_lck; } /**************************************************************************** diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 941e87d3ad..a0b93f5032 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -32,7 +32,7 @@ typedef struct _blocking_lock_record { struct _blocking_lock_record *prev; int com_type; files_struct *fsp; - time_t expire_time; + struct timeval expire_time; int lock_num; SMB_BIG_UINT offset; SMB_BIG_UINT count; @@ -75,7 +75,8 @@ static void received_unlock_msg(int msg_type, struct process_id src, Function to push a blocking lock request onto the lock queue. ****************************************************************************/ -BOOL push_blocking_lock_request( char *inbuf, int length, +BOOL push_blocking_lock_request( struct byte_range_lock *br_lck, + char *inbuf, int length, files_struct *fsp, int lock_timeout, int lock_num, @@ -86,7 +87,6 @@ BOOL push_blocking_lock_request( char *inbuf, int length, { static BOOL set_lock_msg; blocking_lock_record *blr, *tmp; - struct byte_range_lock *br_lck = NULL; NTSTATUS status; if(in_chained_smb() ) { @@ -115,7 +115,13 @@ BOOL push_blocking_lock_request( char *inbuf, int length, blr->com_type = CVAL(inbuf,smb_com); blr->fsp = fsp; - blr->expire_time = (lock_timeout == -1) ? (time_t)-1 : time(NULL) + (time_t)lock_timeout; + if (lock_timeout == -1) { + blr->expire_time.tv_sec = 0; + blr->expire_time.tv_usec = 0; /* Never expire. */ + } else { + blr->expire_time = timeval_current_ofs(lock_timeout/1000, + (lock_timeout % 1000) * 1000); + } blr->lock_num = lock_num; blr->lock_pid = lock_pid; blr->lock_flav = lock_flav; @@ -125,13 +131,6 @@ BOOL push_blocking_lock_request( char *inbuf, int length, memcpy(blr->inbuf, inbuf, length); blr->length = length; - br_lck = brl_get_locks(NULL, blr->fsp); - if (!br_lck) { - DLIST_REMOVE(blocking_lock_queue, blr); - free_blocking_lock_record(blr); - return False; - } - /* Add a pending lock record for this. */ status = brl_lock(br_lck, lock_pid, @@ -140,8 +139,7 @@ BOOL push_blocking_lock_request( char *inbuf, int length, count, PENDING_LOCK, blr->lock_flav, - lock_timeout); - TALLOC_FREE(br_lck); + lock_timeout ? True : False); /* blocking_lock. */ if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n")); @@ -158,8 +156,10 @@ BOOL push_blocking_lock_request( char *inbuf, int length, set_lock_msg = True; } - DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with expiry time %d (+%d) \ -for fnum = %d, name = %s\n", length, (int)blr->expire_time, lock_timeout, + DEBUG(3,("push_blocking_lock_request: lock request length=%d blocked with " + "expiry time (%u sec. %u usec) (+%d msec) for fnum = %d, name = %s\n", + length, (unsigned int)blr->expire_time.tv_sec, + (unsigned int)blr->expire_time.tv_usec, lock_timeout, blr->fsp->fnum, blr->fsp->fsp_name )); /* Push the MID of this packet on the signing queue. */ @@ -305,13 +305,6 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status) static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status) { switch(blr->com_type) { -#if 0 - /* We no longer push blocking lock requests for anything but lockingX and trans2. */ - case SMBlock: - case SMBlockread: - generic_blocking_lock_error(blr, status); - break; -#endif case SMBlockingX: reply_lockingX_error(blr, status); break; @@ -337,146 +330,6 @@ static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status } } -#if 0 -/* We no longer push blocking lock requests for anything but lockingX and trans2. */ - -/**************************************************************************** - Attempt to finish off getting all pending blocking locks for a lockread call. - Returns True if we want to be removed from the list. -*****************************************************************************/ - -static BOOL process_lockread(blocking_lock_record *blr) -{ - char *outbuf = get_OutBuffer(); - char *inbuf = blr->inbuf; - ssize_t nread = -1; - char *data, *p; - int outsize = 0; - SMB_BIG_UINT startpos; - size_t numtoread; - NTSTATUS status; - files_struct *fsp = blr->fsp; - - numtoread = SVAL(inbuf,smb_vwv1); - startpos = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv2); - - numtoread = MIN(BUFFER_SIZE-outsize,numtoread); - data = smb_buf(outbuf) + 3; - - status = do_lock(fsp, - (uint32)SVAL(inbuf,smb_pid), - (SMB_BIG_UINT)numtoread, - startpos, - READ_LOCK, - WINDOWS_LOCK, - (int32)-1); - - if (NT_STATUS_V(status)) { - if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && - !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { - /* - * We have other than a "can't get lock" - * error. Send an error. - * Return True so we get dequeued. - */ - generic_blocking_lock_error(blr, status); - return True; - } - - /* - * Still waiting for lock.... - */ - - DEBUG(10,("process_lockread: failed to get lock for file = %s. Still waiting....\n", - fsp->fsp_name)); - return False; - } - - nread = read_file(fsp,data,startpos,numtoread); - - if (nread < 0) { - generic_blocking_lock_error(blr,NT_STATUS_ACCESS_DENIED); - return True; - } - - construct_reply_common(inbuf, outbuf); - outsize = set_message(outbuf,5,0,True); - - outsize += nread; - SSVAL(outbuf,smb_vwv0,nread); - SSVAL(outbuf,smb_vwv5,nread+3); - p = smb_buf(outbuf); - *p++ = 1; - SSVAL(p,0,nread); p += 2; - set_message_end(outbuf, p+nread); - - DEBUG(3, ( "process_lockread file = %s, fnum=%d num=%lu nread=%ld\n", - fsp->fsp_name, fsp->fnum, (unsigned long)numtoread, (long)nread ) ); - - send_blocking_reply(outbuf,outsize); - return True; -} - -/**************************************************************************** - Attempt to finish off getting all pending blocking locks for a lock call. - Returns True if we want to be removed from the list. -*****************************************************************************/ - -static BOOL process_lock(blocking_lock_record *blr) -{ - char *outbuf = get_OutBuffer(); - char *inbuf = blr->inbuf; - int outsize; - SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; - NTSTATUS status; - files_struct *fsp = blr->fsp; - - count = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1); - offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); - - errno = 0; - status = do_lock(fsp, - (uint32)SVAL(inbuf,smb_pid), - count, - offset, - WRITE_LOCK, - WINDOWS_LOCK, - (int32)-1); - - if (NT_STATUS_IS_ERR(status)) { - if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) && - !NT_STATUS_EQUAL(status,NT_STATUS_FILE_LOCK_CONFLICT)) { - /* - * We have other than a "can't get lock" - * error. Send an error. - * Return True so we get dequeued. - */ - - blocking_lock_reply_error(blr, status); - return True; - } - /* - * Still can't get the lock - keep waiting. - */ - DEBUG(10,("process_lock: failed to get lock for file = %s. Still waiting....\n", - fsp->fsp_name)); - return False; - } - - /* - * Success - we got the lock. - */ - - DEBUG(3,("process_lock : file=%s fnum=%d offset=%.0f count=%.0f\n", - fsp->fsp_name, fsp->fnum, (double)offset, (double)count)); - - construct_reply_common(inbuf, outbuf); - outsize = set_message(outbuf,0,0,True); - send_blocking_reply(outbuf,outsize); - return True; -} -#endif - /**************************************************************************** Attempt to finish off getting all pending blocking locks for a lockingX call. Returns True if we want to be removed from the list. @@ -501,8 +354,9 @@ static BOOL process_lockingX(blocking_lock_record *blr) * Data now points at the beginning of the list * of smb_lkrng structs. */ - + for(; blr->lock_num < num_locks; blr->lock_num++) { + struct byte_range_lock *br_lck = NULL; BOOL err; lock_pid = get_lock_pid( data, blr->lock_num, large_file_format); @@ -514,14 +368,17 @@ static BOOL process_lockingX(blocking_lock_record *blr) * request would never have been queued. JRA. */ errno = 0; - status = do_lock(fsp, + br_lck = do_lock(fsp, lock_pid, count, offset, ((locktype & LOCKING_ANDX_SHARED_LOCK) ? READ_LOCK : WRITE_LOCK), WINDOWS_LOCK, - (int32)-1); + True, + &status); + + TALLOC_FREE(br_lck); if (NT_STATUS_IS_ERR(status)) { break; @@ -573,14 +430,15 @@ static BOOL process_trans2(blocking_lock_record *blr) char *outbuf; char params[2]; NTSTATUS status; - - status = do_lock(blr->fsp, - blr->lock_pid, - blr->count, - blr->offset, - blr->lock_type, - blr->lock_flav, - (int32)-1); + struct byte_range_lock *br_lck = do_lock(blr->fsp, + blr->lock_pid, + blr->count, + blr->offset, + blr->lock_type, + blr->lock_flav, + True, + &status); + TALLOC_FREE(br_lck); if (!NT_STATUS_IS_OK(status)) { if (ERROR_WAS_LOCK_DENIED(status)) { @@ -613,13 +471,6 @@ static BOOL process_trans2(blocking_lock_record *blr) static BOOL blocking_lock_record_process(blocking_lock_record *blr) { switch(blr->com_type) { -#if 0 - /* We no longer push blocking lock requests for anything but lockingX and trans2. */ - case SMBlock: - return process_lock(blr); - case SMBlockread: - return process_lockread(blr); -#endif case SMBlockingX: return process_lockingX(blr); case SMBtrans2: @@ -714,44 +565,60 @@ static void received_unlock_msg(int msg_type, struct process_id src, void *buf, size_t len) { DEBUG(10,("received_unlock_msg\n")); - process_blocking_lock_queue(time(NULL)); + process_blocking_lock_queue(); } /**************************************************************************** - Return the number of seconds to the next blocking locks timeout, or default_timeout + Return the number of milliseconds to the next blocking locks timeout, or default_timeout *****************************************************************************/ -unsigned blocking_locks_timeout(unsigned default_timeout) +unsigned int blocking_locks_timeout_ms(unsigned int default_timeout_ms) { - unsigned timeout = default_timeout; - time_t t; + unsigned int timeout_ms = default_timeout_ms; + struct timeval tv_curr; + SMB_BIG_INT min_tv_dif_us = 0x7FFFFFFF; /* A large +ve number. */ blocking_lock_record *blr = blocking_lock_queue; - /* note that we avoid the time() syscall if there are no blocking locks */ - if (!blr) - return timeout; + /* note that we avoid the GetTimeOfDay() syscall if there are no blocking locks */ + if (!blr) { + return timeout_ms; + } - t = time(NULL); + tv_curr = timeval_current(); for (; blr; blr = blr->next) { - if ((blr->expire_time != (time_t)-1) && - (timeout > (blr->expire_time - t))) { - timeout = blr->expire_time - t; + SMB_BIG_INT tv_dif_us; + + if (timeval_is_zero(&blr->expire_time)) { + continue; /* Never timeout. */ } + + tv_dif_us = usec_time_diff(&blr->expire_time, &tv_curr); + min_tv_dif_us = MIN(min_tv_dif_us, tv_dif_us); + } + + if (min_tv_dif_us < 0) { + min_tv_dif_us = 0; + } + + timeout_ms = (unsigned int)(min_tv_dif_us / (SMB_BIG_INT)1000); + + if (timeout_ms < 1) { + timeout_ms = 1; } - if (timeout < 1) - timeout = 1; + DEBUG(10,("blocking_locks_timeout_ms: returning %u\n", timeout_ms)); - return timeout; + return timeout_ms; } /**************************************************************************** Process the blocking lock queue. Note that this is only called as root. *****************************************************************************/ -void process_blocking_lock_queue(time_t t) +void process_blocking_lock_queue(void) { + struct timeval tv_curr = timeval_current(); blocking_lock_record *blr, *next = NULL; /* @@ -780,7 +647,7 @@ void process_blocking_lock_queue(time_t t) DEBUG(5,("process_blocking_lock_queue: examining pending lock fnum = %d for file %s\n", fsp->fnum, fsp->fsp_name )); - if((blr->expire_time != -1) && (blr->expire_time <= t)) { + if (!timeval_is_zero(&blr->expire_time) && timeval_compare(&blr->expire_time, &tv_curr) <= 0) { struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); /* diff --git a/source3/smbd/process.c b/source3/smbd/process.c index ce352adfd7..f8c66d93ea 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -1276,7 +1276,7 @@ int chain_reply(char *inbuf,char *outbuf,int size,int bufsize) } /**************************************************************************** - Setup the needed select timeout. + Setup the needed select timeout in milliseconds. ****************************************************************************/ static int setup_select_timeout(void) @@ -1284,16 +1284,17 @@ static int setup_select_timeout(void) int select_timeout; int t; - select_timeout = blocking_locks_timeout(SMBD_SELECT_TIMEOUT); - select_timeout *= 1000; + select_timeout = blocking_locks_timeout_ms(SMBD_SELECT_TIMEOUT*1000); t = change_notify_timeout(); DEBUG(10, ("change_notify_timeout: %d\n", t)); - if (t != -1) + if (t != -1) { select_timeout = MIN(select_timeout, t*1000); + } - if (print_notify_messages_pending()) + if (print_notify_messages_pending()) { select_timeout = MIN(select_timeout, 1000); + } return select_timeout; } @@ -1482,7 +1483,7 @@ machine %s in domain %s.\n", global_myname(), lp_workgroup())); * Check to see if we have any blocking locks * outstanding on the queue. */ - process_blocking_lock_queue(t); + process_blocking_lock_queue(); /* update printer queue caches if necessary */ diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index ec618db3f8..6176edb52d 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -2371,6 +2371,7 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length size_t numtoread; NTSTATUS status; files_struct *fsp = file_fsp(inbuf,smb_vwv0); + struct byte_range_lock *br_lck = NULL; START_PROFILE(SMBlockread); CHECK_FSP(fsp,conn); @@ -2395,42 +2396,17 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length * Note that the requested lock size is unaffected by max_recv. */ - status = do_lock(fsp, + br_lck = do_lock(fsp, (uint32)SVAL(inbuf,smb_pid), (SMB_BIG_UINT)numtoread, (SMB_BIG_UINT)startpos, WRITE_LOCK, WINDOWS_LOCK, - 0 /* zero timeout. */); + False, /* Non-blocking lock. */ + &status); + TALLOC_FREE(br_lck); if (NT_STATUS_V(status)) { -#if 0 - /* - * We used to make lockread a blocking lock. It turns out - * that this isn't on W2k. Found by the Samba 4 RAW-READ torture - * tester. JRA. - */ - - if (lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) { - /* - * A blocking lock was requested. Package up - * this smb into a queued request and push it - * onto the blocking lock queue. - */ - if(push_blocking_lock_request(inbuf, length, - fsp, - -1, - 0, - SVAL(inbuf,smb_pid), - WRITE_LOCK, - WINDOWS_LOCK, - (SMB_BIG_UINT)startpos, - (SMB_BIG_UINT)numtoread)) { - END_PROFILE(SMBlockread); - return -1; - } - } -#endif END_PROFILE(SMBlockread); return ERROR_NT(status); } @@ -3417,6 +3393,7 @@ int reply_lock(connection_struct *conn, SMB_BIG_UINT count,offset; NTSTATUS status; files_struct *fsp = file_fsp(inbuf,smb_vwv0); + struct byte_range_lock *br_lck = NULL; START_PROFILE(SMBlock); @@ -3430,36 +3407,18 @@ int reply_lock(connection_struct *conn, DEBUG(3,("lock fd=%d fnum=%d offset=%.0f count=%.0f\n", fsp->fh->fd, fsp->fnum, (double)offset, (double)count)); - status = do_lock(fsp, + br_lck = do_lock(fsp, (uint32)SVAL(inbuf,smb_pid), count, offset, WRITE_LOCK, WINDOWS_LOCK, - 0 /* zero timeout. */); + False, /* Non-blocking lock. */ + &status); + + TALLOC_FREE(br_lck); if (NT_STATUS_V(status)) { -#if 0 - /* Tests using Samba4 against W2K show this call never creates a blocking lock. */ - if (lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) { - /* - * A blocking lock was requested. Package up - * this smb into a queued request and push it - * onto the blocking lock queue. - */ - if(push_blocking_lock_request(inbuf, length, - fsp, - -1, - 0, - SVAL(inbuf,smb_pid), - WRITE_LOCK, - WINDOWS_LOCK, - offset, count)) { - END_PROFILE(SMBlock); - return -1; - } - } -#endif END_PROFILE(SMBlock); return ERROR_NT(status); } @@ -5353,9 +5312,7 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, /* Setup the timeout in seconds. */ - if (lp_blocking_locks(SNUM(conn))) { - lock_timeout = ((lock_timeout == -1) ? -1 : (lock_timeout+999)/1000); - } else { + if (!lp_blocking_locks(SNUM(conn))) { lock_timeout = 0; } @@ -5410,42 +5367,57 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, offset, WINDOWS_LOCK); } else { - status = do_lock(fsp, + BOOL blocking_lock = lock_timeout ? True : False; + BOOL defer_lock = False; + struct byte_range_lock *br_lck; + + br_lck = do_lock(fsp, lock_pid, count, offset, lock_type, WINDOWS_LOCK, - lock_timeout); + blocking_lock, + &status); - if (NT_STATUS_V(status)) { + if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) { + defer_lock = True; + } + + /* This heuristic seems to match W2K3 very well. If a + lock sent with timeout of zero would fail with NT_STATUS_FILE_LOCK_CONFLICT + it pretends we asked for a timeout of between 150 - 300 milliseconds as + far as I can tell. Replacement for do_lock_spin(). JRA. */ + + if (br_lck && lp_blocking_locks(SNUM(conn)) && !blocking_lock && + NT_STATUS_EQUAL((status), NT_STATUS_FILE_LOCK_CONFLICT)) { + defer_lock = True; + lock_timeout = 200; + } + + if (br_lck && defer_lock) { /* - * Interesting fact found by IFSTEST /t - * LockOverlappedTest... Even if it's our own lock - * context, we need to wait here as there may be an - * unlock on the way. JRA. + * A blocking lock was requested. Package up + * this smb into a queued request and push it + * onto the blocking lock queue. */ - if ((lock_timeout != 0) && - ERROR_WAS_LOCK_DENIED(status)) { - /* - * A blocking lock was requested. Package up - * this smb into a queued request and push it - * onto the blocking lock queue. - */ - if(push_blocking_lock_request(inbuf, length, - fsp, - lock_timeout, - i, - lock_pid, - lock_type, - WINDOWS_LOCK, - offset, - count)) { - END_PROFILE(SMBlockingX); - return -1; - } + if(push_blocking_lock_request(br_lck, + inbuf, length, + fsp, + lock_timeout, + i, + lock_pid, + lock_type, + WINDOWS_LOCK, + offset, + count)) { + TALLOC_FREE(br_lck); + END_PROFILE(SMBlockingX); + return -1; } } + + TALLOC_FREE(br_lck); } if (NT_STATUS_V(status)) { diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 199204684f..5acce13e52 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -4503,7 +4503,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", SMB_BIG_UINT count; SMB_BIG_UINT offset; uint32 lock_pid; - BOOL lock_blocking = False; + BOOL blocking_lock = False; enum brl_type lock_type; if (fsp == NULL || fsp->fh->fd == -1) { @@ -4533,15 +4533,15 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", } if (SVAL(pdata,POSIX_LOCK_FLAGS_OFFSET) == POSIX_LOCK_FLAG_NOWAIT) { - lock_blocking = False; + blocking_lock = False; } else if (SVAL(pdata,POSIX_LOCK_FLAGS_OFFSET) == POSIX_LOCK_FLAG_WAIT) { - lock_blocking = True; + blocking_lock = True; } else { return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } if (!lp_blocking_locks(SNUM(conn))) { - lock_blocking = False; + blocking_lock = False; } lock_pid = IVAL(pdata, POSIX_LOCK_PID_OFFSET); @@ -4562,21 +4562,23 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", offset, POSIX_LOCK); } else { - status = do_lock(fsp, - lock_pid, - count, - offset, - lock_type, - lock_blocking ? -1 : 0, - POSIX_LOCK); - - if (lock_blocking && ERROR_WAS_LOCK_DENIED(status)) { + struct byte_range_lock *br_lck = do_lock(fsp, + lock_pid, + count, + offset, + lock_type, + blocking_lock, + POSIX_LOCK, + &status); + + if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) { /* * A blocking lock was requested. Package up * this smb into a queued request and push it * onto the blocking lock queue. */ - if(push_blocking_lock_request(inbuf, length, + if(push_blocking_lock_request(br_lck, + inbuf, length, fsp, -1, /* infinite timeout. */ 0, @@ -4585,9 +4587,11 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", POSIX_LOCK, offset, count)) { + TALLOC_FREE(br_lck); return -1; } } + TALLOC_FREE(br_lck); } if (!NT_STATUS_IS_OK(status)) { |