From: Stefan Metzmacher Date: Tue, 7 Jul 2009 15:24:25 +0000 (+0200) Subject: s3:smbd: abstract the main locking logic from the LockingAndX parsing X-Git-Url: http://git.samba.org/metze/?p=metze%2Fsamba%2Fwip.git;a=commitdiff_plain;h=acfeaa411958ac4ee43d45d47802da74fea14eef s3:smbd: abstract the main locking logic from the LockingAndX parsing This prepares SMB2 Lock support. metze --- diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index c07ac3367937..7c72216b0215 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -7191,6 +7191,208 @@ uint64_t get_lock_offset(const uint8_t *data, int data_offset, return offset; } +struct smbd_lock_element { + uint32_t smbpid; + uint64_t offset; + uint64_t count; +}; + +static NTSTATUS smbd_do_locking(struct smb_request *req, + files_struct *fsp, + uint8_t type, + int32_t timeout, + uint16_t num_ulocks, + struct smbd_lock_element *ulocks, + uint16_t num_locks, + struct smbd_lock_element *locks, + bool *async) +{ + connection_struct *conn = req->conn; + int i; + NTSTATUS status = NT_STATUS_OK; + + *async = false; + + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + struct smbd_lock_element *e = &ulocks[i]; + + DEBUG(10,("smbd_do_locking: unlock start=%.0f, len=%.0f for " + "pid %u, file %s\n", + (double)e->offset, + (double)e->count, + (unsigned int)e->smbpid, + fsp->fsp_name)); + + status = do_unlock(smbd_messaging_context(), + fsp, + e->smbpid, + e->count, + e->offset, + WINDOWS_LOCK); + + DEBUG(10, ("smbd_do_locking: unlock returned %s\n", + nt_errstr(status))); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + /* Setup the timeout in seconds. */ + + if (!lp_blocking_locks(SNUM(conn))) { + timeout = 0; + } + + /* Data now points at the beginning of the list + of smb_lkrng structs */ + + for(i = 0; i < (int)num_locks; i++) { + struct smbd_lock_element *e = &locks[i]; + enum brl_type brltype = ((type & LOCKING_ANDX_SHARED_LOCK) ? + READ_LOCK:WRITE_LOCK); + + DEBUG(10,("smbd_do_locking: lock start=%.0f, len=%.0f for pid " + "%u, file %s timeout = %d\n", + (double)e->offset, + (double)e->count, + (unsigned int)e->smbpid, + fsp->fsp_name, + (int)timeout)); + + if (type & LOCKING_ANDX_CANCEL_LOCK) { + struct blocking_lock_record *blr = NULL; + + if (lp_blocking_locks(SNUM(conn))) { + + /* Schedule a message to ourselves to + remove the blocking lock record and + return the right error. */ + + blr = blocking_lock_cancel(fsp, + e->smbpid, + e->offset, + e->count, + WINDOWS_LOCK, + type, + NT_STATUS_FILE_LOCK_CONFLICT); + if (blr == NULL) { + return NT_STATUS_DOS( + ERRDOS, + ERRcancelviolation); + } + } + /* Remove a matching pending lock. */ + status = do_lock_cancel(fsp, + e->smbpid, + e->count, + e->offset, + WINDOWS_LOCK, + blr); + } else { + bool blocking_lock = timeout ? true : false; + bool defer_lock = false; + struct byte_range_lock *br_lck; + uint32_t block_smbpid; + + br_lck = do_lock(smbd_messaging_context(), + fsp, + e->smbpid, + e->count, + e->offset, + brltype, + WINDOWS_LOCK, + blocking_lock, + &status, + &block_smbpid, + NULL); + + if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) { + /* Windows internal resolution for blocking locks seems + to be about 200ms... Don't wait for less than that. JRA. */ + if (timeout != -1 && timeout < lp_lock_spin_time()) { + timeout = lp_lock_spin_time(); + } + 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; + timeout = lp_lock_spin_time(); + } + + if (br_lck && defer_lock) { + /* + * 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(br_lck, + req, + fsp, + timeout, + i, + e->smbpid, + brltype, + WINDOWS_LOCK, + e->offset, + e->count, + block_smbpid)) { + TALLOC_FREE(br_lck); + *async = true; + return NT_STATUS_OK; + } + } + + TALLOC_FREE(br_lck); + } + + if (!NT_STATUS_IS_OK(status)) { + break; + } + } + + /* If any of the above locks failed, then we must unlock + all of the previous locks (X/Open spec). */ + + if (num_locks != 0 && !NT_STATUS_IS_OK(status)) { + + if (type & LOCKING_ANDX_CANCEL_LOCK) { + i = -1; /* we want to skip the for loop */ + } + + /* + * Ensure we don't do a remove on the lock that just failed, + * as under POSIX rules, if we have a lock already there, we + * will delete it (and we shouldn't) ..... + */ + for(i--; i >= 0; i--) { + struct smbd_lock_element *e = &locks[i]; + + do_unlock(smbd_messaging_context(), + fsp, + e->smbpid, + e->count, + e->offset, + WINDOWS_LOCK); + } + return status; + } + + DEBUG(3, ("smbd_do_locking: fnum=%d type=%d num_locks=%d num_ulocks=%d\n", + fsp->fnum, (unsigned int)type, num_locks, num_ulocks)); + + return NT_STATUS_OK; +} + /**************************************************************************** Reply to a lockingX request. ****************************************************************************/ @@ -7203,14 +7405,15 @@ void reply_lockingX(struct smb_request *req) unsigned char oplocklevel; uint16 num_ulocks; uint16 num_locks; - uint64_t count = 0, offset = 0; - uint32 lock_pid; int32 lock_timeout; int i; const uint8_t *data; bool large_file_format; bool err; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + struct smbd_lock_element *ulocks; + struct smbd_lock_element *locks; + bool async = false; START_PROFILE(SMBlockingX); @@ -7323,12 +7526,26 @@ void reply_lockingX(struct smb_request *req) return; } + ulocks = talloc_array(req, struct smbd_lock_element, num_ulocks); + if (ulocks == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBlockingX); + return; + } + + locks = talloc_array(req, struct smbd_lock_element, num_locks); + if (locks == NULL) { + reply_nterror(req, NT_STATUS_NO_MEMORY); + END_PROFILE(SMBlockingX); + return; + } + /* Data now points at the beginning of the list of smb_unlkrng structs */ for(i = 0; i < (int)num_ulocks; i++) { - lock_pid = get_lock_pid( data, i, large_file_format); - count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, &err); + ulocks[i].smbpid = get_lock_pid(data, i, large_file_format); + ulocks[i].count = get_lock_count(data, i, large_file_format); + ulocks[i].offset = get_lock_offset(data, i, large_file_format, &err); /* * There is no error code marked "stupid client bug".... :-). @@ -7338,32 +7555,6 @@ void reply_lockingX(struct smb_request *req) reply_doserror(req, ERRDOS, ERRnoaccess); return; } - - DEBUG(10,("reply_lockingX: unlock start=%.0f, len=%.0f for " - "pid %u, file %s\n", (double)offset, (double)count, - (unsigned int)lock_pid, fsp->fsp_name )); - - status = do_unlock(smbd_messaging_context(), - fsp, - lock_pid, - count, - offset, - WINDOWS_LOCK); - - DEBUG(10, ("reply_lockingX: unlock returned %s\n", - nt_errstr(status))); - - if (NT_STATUS_V(status)) { - END_PROFILE(SMBlockingX); - reply_nterror(req, status); - return; - } - } - - /* Setup the timeout in seconds. */ - - if (!lp_blocking_locks(SNUM(conn))) { - lock_timeout = 0; } /* Now do any requested locks */ @@ -7373,11 +7564,9 @@ void reply_lockingX(struct smb_request *req) of smb_lkrng structs */ for(i = 0; i < (int)num_locks; i++) { - enum brl_type lock_type = ((locktype & LOCKING_ANDX_SHARED_LOCK) ? - READ_LOCK:WRITE_LOCK); - lock_pid = get_lock_pid( data, i, large_file_format); - count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, &err); + locks[i].smbpid = get_lock_pid(data, i, large_file_format); + locks[i].count = get_lock_count(data, i, large_file_format); + locks[i].offset = get_lock_offset(data, i, large_file_format, &err); /* * There is no error code marked "stupid client bug".... :-). @@ -7387,154 +7576,22 @@ void reply_lockingX(struct smb_request *req) reply_doserror(req, ERRDOS, ERRnoaccess); return; } - - DEBUG(10,("reply_lockingX: lock start=%.0f, len=%.0f for pid " - "%u, file %s timeout = %d\n", (double)offset, - (double)count, (unsigned int)lock_pid, - fsp->fsp_name, (int)lock_timeout )); - - if (locktype & LOCKING_ANDX_CANCEL_LOCK) { - struct blocking_lock_record *blr = NULL; - - if (lp_blocking_locks(SNUM(conn))) { - - /* Schedule a message to ourselves to - remove the blocking lock record and - return the right error. */ - - blr = blocking_lock_cancel(fsp, - lock_pid, - offset, - count, - WINDOWS_LOCK, - locktype, - NT_STATUS_FILE_LOCK_CONFLICT); - if (blr == NULL) { - END_PROFILE(SMBlockingX); - reply_nterror( - req, - NT_STATUS_DOS( - ERRDOS, - ERRcancelviolation)); - return; - } - } - /* Remove a matching pending lock. */ - status = do_lock_cancel(fsp, - lock_pid, - count, - offset, - WINDOWS_LOCK, - blr); - } else { - bool blocking_lock = lock_timeout ? True : False; - bool defer_lock = False; - struct byte_range_lock *br_lck; - uint32 block_smbpid; - - br_lck = do_lock(smbd_messaging_context(), - fsp, - lock_pid, - count, - offset, - lock_type, - WINDOWS_LOCK, - blocking_lock, - &status, - &block_smbpid, - NULL); - - if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) { - /* Windows internal resolution for blocking locks seems - to be about 200ms... Don't wait for less than that. JRA. */ - if (lock_timeout != -1 && lock_timeout < lp_lock_spin_time()) { - lock_timeout = lp_lock_spin_time(); - } - 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 = lp_lock_spin_time(); - } - - if (br_lck && defer_lock) { - /* - * 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(br_lck, - req, - fsp, - lock_timeout, - i, - lock_pid, - lock_type, - WINDOWS_LOCK, - offset, - count, - block_smbpid)) { - TALLOC_FREE(br_lck); - END_PROFILE(SMBlockingX); - return; - } - } - - TALLOC_FREE(br_lck); - } - - if (NT_STATUS_V(status)) { - break; - } } - /* If any of the above locks failed, then we must unlock - all of the previous locks (X/Open spec). */ - if (num_locks != 0 && !NT_STATUS_IS_OK(status)) { - - if (locktype & LOCKING_ANDX_CANCEL_LOCK) { - i = -1; /* we want to skip the for loop */ - } - - /* - * Ensure we don't do a remove on the lock that just failed, - * as under POSIX rules, if we have a lock already there, we - * will delete it (and we shouldn't) ..... - */ - for(i--; i >= 0; i--) { - lock_pid = get_lock_pid( data, i, large_file_format); - count = get_lock_count( data, i, large_file_format); - offset = get_lock_offset( data, i, large_file_format, - &err); - - /* - * There is no error code marked "stupid client - * bug".... :-). - */ - if(err) { - END_PROFILE(SMBlockingX); - reply_doserror(req, ERRDOS, ERRnoaccess); - return; - } - - do_unlock(smbd_messaging_context(), - fsp, - lock_pid, - count, - offset, - WINDOWS_LOCK); - } + status = smbd_do_locking(req, fsp, + locktype, lock_timeout, + num_ulocks, ulocks, + num_locks, locks, + &async); + if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBlockingX); reply_nterror(req, status); return; } + if (async) { + END_PROFILE(SMBlockingX); + return; + } reply_outbuf(req, 2, 0);