From e1da1fcf12164f50f3462c90f0bb785d18c59b0b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 17 Jul 2006 21:09:02 +0000 Subject: r17098: Samba3 now cleanly passes Samba4 RAW-LOCK torture test. Phew - that was painful :-). But what it means is that we now implement lock cancels and I can add lock cancels into POSIX lock handling which will fix the fast/slow system call issue with cifsfs ! Jeremy. (This used to be commit f1a9cf075b87c76c032d19da0168424c90f6cb3c) --- source3/include/includes.h | 1 + source3/include/locking.h | 4 +- source3/include/messages.h | 1 + source3/include/smb.h | 3 +- source3/lib/dummysmbd.c | 4 + source3/locking/brlock.c | 57 ++++++------- source3/locking/locking.c | 102 ++++++++++------------- source3/smbd/blocking.c | 204 ++++++++++++++++++++++++++++++++++++--------- source3/smbd/close.c | 2 - source3/smbd/reply.c | 123 ++++++++++++++++----------- source3/smbd/trans2.c | 4 +- 11 files changed, 323 insertions(+), 182 deletions(-) (limited to 'source3') diff --git a/source3/include/includes.h b/source3/include/includes.h index ab2f6a9641..86d7f069cd 100644 --- a/source3/include/includes.h +++ b/source3/include/includes.h @@ -936,6 +936,7 @@ extern int errno; #include "debugparse.h" #include "version.h" #include "privileges.h" +#include "locking.h" #include "smb.h" #include "ads_cldap.h" #include "nameserv.h" diff --git a/source3/include/locking.h b/source3/include/locking.h index 983d59a853..8eabb305f7 100644 --- a/source3/include/locking.h +++ b/source3/include/locking.h @@ -45,8 +45,10 @@ struct lock_key { SMB_INO_T inode; }; +struct files_struct; + struct byte_range_lock { - files_struct *fsp; + struct files_struct *fsp; unsigned int num_locks; BOOL modified; struct lock_key key; diff --git a/source3/include/messages.h b/source3/include/messages.h index e246123ea9..b0305373c0 100644 --- a/source3/include/messages.h +++ b/source3/include/messages.h @@ -70,6 +70,7 @@ #define MSG_SMB_KERNEL_BREAK 3010 #define MSG_SMB_FILE_RENAME 3011 #define MSG_SMB_INJECT_FAULT 3012 +#define MSG_SMB_BLOCKING_LOCK_CANCEL 3013 /* winbind messages */ #define MSG_WINBIND_FINISHED 4001 diff --git a/source3/include/smb.h b/source3/include/smb.h index 39a7897581..1a55bef428 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -440,6 +440,7 @@ typedef struct files_struct { int oplock_type; int sent_oplock_break; struct timed_event *oplock_timeout; + struct lock_struct last_lock_failure; struct share_mode_entry *pending_break_messages; int num_pending_break_messages; @@ -861,8 +862,6 @@ struct parm_struct { #define FLAG_HIDE 0x2000 /* options that should be hidden in SWAT */ #define FLAG_DOS_STRING 0x4000 /* convert from UNIX to DOS codepage when reading this string. */ -#include "locking.h" - struct bitmap { uint32 *b; unsigned int n; diff --git a/source3/lib/dummysmbd.c b/source3/lib/dummysmbd.c index 9b587224e3..087de2fe25 100644 --- a/source3/lib/dummysmbd.c +++ b/source3/lib/dummysmbd.c @@ -38,3 +38,7 @@ BOOL conn_snum_used(int snum) { return False; } + +void cancel_pending_lock_requests_by_fid(files_struct *fsp, struct byte_range_lock *br_lck) +{ +} diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 9edac7203b..f251ff57ec 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -211,30 +211,34 @@ static BOOL brl_conflict_other(const struct lock_struct *lck1, const struct lock } /**************************************************************************** - Amazingly enough, w2k3 "remembers" whether the last lock failure + Amazingly enough, w2k3 "remembers" whether the last lock failure on a fnum is the same as this one and changes its error code. I wonder if any app depends on this ? ****************************************************************************/ -static NTSTATUS brl_lock_failed(const struct lock_struct *lock) +static NTSTATUS brl_lock_failed(files_struct *fsp, const struct lock_struct *lock, int32 lock_timeout) { - static struct lock_struct last_lock_failure; - - if (brl_same_context(&lock->context, &last_lock_failure.context) && - lock->fnum == last_lock_failure.fnum && - lock->start == last_lock_failure.start && - lock->size == last_lock_failure.size) { - return NT_STATUS_FILE_LOCK_CONFLICT; - } - last_lock_failure = *lock; - if (lock->start >= 0xEF000000 && - (lock->start >> 63) == 0) { + 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) { + fsp->last_lock_failure = *lock; + } + return NT_STATUS_FILE_LOCK_CONFLICT; + } + + if (procid_equal(&lock->context.pid, &fsp->last_lock_failure.context.pid) && + lock->context.tid == fsp->last_lock_failure.context.tid && + lock->fnum == fsp->last_lock_failure.fnum && + lock->start == fsp->last_lock_failure.start) { return NT_STATUS_FILE_LOCK_CONFLICT; } + + if (lock_timeout == 0) { + fsp->last_lock_failure = *lock; + } return NT_STATUS_LOCK_NOT_GRANTED; } @@ -293,8 +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, - BOOL *my_lock_ctx) + const struct lock_struct *plock, int32 lock_timeout) { unsigned int i; files_struct *fsp = br_lck->fsp; @@ -303,12 +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)) { - NTSTATUS status = brl_lock_failed(plock);; - /* Did we block ourselves ? */ - if (brl_same_context(&locks[i].context, &plock->context)) { - *my_lock_ctx = True; - } - return status; + return brl_lock_failed(fsp,plock,lock_timeout); } #if ZERO_ZERO if (plock->start == 0 && plock->size == 0 && @@ -571,8 +569,7 @@ OR ****************************************************************************/ static NTSTATUS brl_lock_posix(struct byte_range_lock *br_lck, - const struct lock_struct *plock, - BOOL *my_lock_ctx) + const struct lock_struct *plock) { unsigned int i, count; struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; @@ -604,10 +601,6 @@ static NTSTATUS brl_lock_posix(struct byte_range_lock *br_lck, if (locks[i].lock_flav == WINDOWS_LOCK) { /* Do any Windows flavour locks conflict ? */ if (brl_conflict(&locks[i], plock)) { - /* Did we block ourselves ? */ - if (brl_same_context(&locks[i].context, &plock->context)) { - *my_lock_ctx = True; - } /* No games with error messages. */ SAFE_FREE(tp); return NT_STATUS_FILE_LOCK_CONFLICT; @@ -683,13 +676,11 @@ NTSTATUS brl_lock(struct byte_range_lock *br_lck, br_off size, enum brl_type lock_type, enum brl_flavour lock_flav, - BOOL *my_lock_ctx) + int32 lock_timeout) { NTSTATUS ret; struct lock_struct lock; - *my_lock_ctx = False; - #if !ZERO_ZERO if (start == 0 && size == 0) { DEBUG(0,("client sent 0/0 lock - please report this\n")); @@ -706,9 +697,9 @@ 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, my_lock_ctx); + ret = brl_lock_windows(br_lck, &lock, lock_timeout); } else { - ret = brl_lock_posix(br_lck, &lock, my_lock_ctx); + ret = brl_lock_posix(br_lck, &lock); } #if ZERO_ZERO @@ -1165,7 +1156,7 @@ NTSTATUS brl_lockquery(struct byte_range_lock *br_lck, Remove a particular pending lock. ****************************************************************************/ -BOOL brl_remove_pending_lock(struct byte_range_lock *br_lck, +BOOL brl_lock_cancel(struct byte_range_lock *br_lck, uint32 smbpid, struct process_id pid, br_off start, diff --git a/source3/locking/locking.c b/source3/locking/locking.c index ac50c9b648..cd1d9547f3 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -185,7 +185,7 @@ NTSTATUS do_lock(files_struct *fsp, SMB_BIG_UINT offset, enum brl_type lock_type, enum brl_flavour lock_flav, - BOOL *my_lock_ctx) + int32 lock_timeout) { struct byte_range_lock *br_lck = NULL; NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED; @@ -216,76 +216,63 @@ NTSTATUS do_lock(files_struct *fsp, count, lock_type, lock_flav, - my_lock_ctx); + lock_timeout); TALLOC_FREE(br_lck); return status; } /**************************************************************************** - Utility function called by locking requests. This is *DISGUSTING*. It also - appears to be "What Windows Does" (tm). Andrew, ever wonder why Windows 2000 - is so slow on the locking tests...... ? This is the reason. Much though I hate - it, we need this. JRA. + Utility function called by unlocking requests. ****************************************************************************/ -NTSTATUS do_lock_spin(files_struct *fsp, +NTSTATUS do_unlock(files_struct *fsp, uint32 lock_pid, SMB_BIG_UINT count, SMB_BIG_UINT offset, - enum brl_type lock_type, - enum brl_flavour lock_flav, - BOOL *my_lock_ctx) + enum brl_flavour lock_flav) { - int j, maxj = lp_lock_spin_count(); - int sleeptime = lp_lock_sleep_time(); - NTSTATUS status, ret; - - if (maxj <= 0) { - maxj = 1; + BOOL ok = False; + struct byte_range_lock *br_lck = NULL; + + if (!fsp->can_lock) { + return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; } + + if (!lp_locking(SNUM(fsp->conn))) { + return NT_STATUS_OK; + } + + DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for fnum %d file %s\n", + (double)offset, (double)count, fsp->fnum, fsp->fsp_name )); - ret = NT_STATUS_OK; /* to keep dumb compilers happy */ - - for (j = 0; j < maxj; j++) { - status = do_lock(fsp, - lock_pid, - count, - offset, - lock_type, - lock_flav, - my_lock_ctx); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED) && - !NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) { - return status; - } - /* if we do fail then return the first error code we got */ - if (j == 0) { - ret = status; - /* Don't spin if we blocked ourselves. */ - if (*my_lock_ctx) { - return ret; - } + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } - /* Only spin for Windows locks. */ - if (lock_flav == POSIX_LOCK) { - return ret; - } - } + ok = brl_unlock(br_lck, + lock_pid, + procid_self(), + offset, + count, + lock_flav); + + TALLOC_FREE(br_lck); - if (sleeptime) { - sys_usleep(sleeptime); - } + if (!ok) { + DEBUG(10,("do_unlock: returning ERRlock.\n" )); + return NT_STATUS_RANGE_NOT_LOCKED; } - return ret; + + return NT_STATUS_OK; } /**************************************************************************** - Utility function called by unlocking requests. + Cancel any pending blocked locks. ****************************************************************************/ -NTSTATUS do_unlock(files_struct *fsp, +NTSTATUS do_lock_cancel(files_struct *fsp, uint32 lock_pid, SMB_BIG_UINT count, SMB_BIG_UINT offset, @@ -295,14 +282,15 @@ NTSTATUS do_unlock(files_struct *fsp, struct byte_range_lock *br_lck = NULL; if (!fsp->can_lock) { - return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + return fsp->is_directory ? + NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; } if (!lp_locking(SNUM(fsp->conn))) { - return NT_STATUS_OK; + return NT_STATUS_DOS(ERRDOS, ERRcancelviolation); } - - DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for fnum %d file %s\n", + + DEBUG(10,("do_lock_cancel: cancel start=%.0f len=%.0f requested for fnum %d file %s\n", (double)offset, (double)count, fsp->fnum, fsp->fsp_name )); br_lck = brl_get_locks(NULL, fsp); @@ -310,7 +298,7 @@ NTSTATUS do_unlock(files_struct *fsp, return NT_STATUS_NO_MEMORY; } - ok = brl_unlock(br_lck, + ok = brl_lock_cancel(br_lck, lock_pid, procid_self(), offset, @@ -320,8 +308,8 @@ NTSTATUS do_unlock(files_struct *fsp, TALLOC_FREE(br_lck); if (!ok) { - DEBUG(10,("do_unlock: returning ERRlock.\n" )); - return NT_STATUS_RANGE_NOT_LOCKED; + DEBUG(10,("do_lock_cancel: returning ERRcancelviolation.\n" )); + return NT_STATUS_DOS(ERRDOS, ERRcancelviolation); } return NT_STATUS_OK; @@ -340,7 +328,9 @@ void locking_close_file(files_struct *fsp) } br_lck = brl_get_locks(NULL,fsp); + if (br_lck) { + cancel_pending_lock_requests_by_fid(fsp, br_lck); brl_close_fnum(br_lck); TALLOC_FREE(br_lck); } diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c index 04ab01eb66..941e87d3ad 100644 --- a/source3/smbd/blocking.c +++ b/source3/smbd/blocking.c @@ -19,6 +19,8 @@ */ #include "includes.h" +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_LOCKING /**************************************************************************** This is the structure to queue to implement blocking locks. @@ -41,15 +43,18 @@ typedef struct _blocking_lock_record { int length; } blocking_lock_record; +/* dlink list we store pending lock records on. */ static blocking_lock_record *blocking_lock_queue; +/* dlink list we move cancelled lock records onto. */ +static blocking_lock_record *blocking_lock_cancelled_queue; + /**************************************************************************** Destructor for the above structure. ****************************************************************************/ static void free_blocking_lock_record(blocking_lock_record *blr) { - DLIST_REMOVE(blocking_lock_queue, blr); SAFE_FREE(blr->inbuf); SAFE_FREE(blr); } @@ -81,7 +86,6 @@ BOOL push_blocking_lock_request( char *inbuf, int length, { static BOOL set_lock_msg; blocking_lock_record *blr, *tmp; - BOOL my_lock_ctx = False; struct byte_range_lock *br_lck = NULL; NTSTATUS status; @@ -123,6 +127,7 @@ BOOL push_blocking_lock_request( char *inbuf, int 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; } @@ -135,11 +140,12 @@ BOOL push_blocking_lock_request( char *inbuf, int length, count, PENDING_LOCK, blr->lock_flav, - &my_lock_ctx); + lock_timeout); TALLOC_FREE(br_lck); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n")); + DLIST_REMOVE(blocking_lock_queue, blr); free_blocking_lock_record(blr); return False; } @@ -220,9 +226,24 @@ static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS stat status = NT_STATUS_FILE_LOCK_CONFLICT; } + if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) { + /* Store the last lock error. */ + files_struct *fsp = blr->fsp; + + fsp->last_lock_failure.context.smbpid = blr->lock_pid; + fsp->last_lock_failure.context.tid = fsp->conn->cnum; + fsp->last_lock_failure.context.pid = procid_self(); + fsp->last_lock_failure.start = blr->offset; + fsp->last_lock_failure.size = blr->count; + fsp->last_lock_failure.fnum = fsp->fnum; + fsp->last_lock_failure.lock_type = READ_LOCK; /* Don't care. */ + fsp->last_lock_failure.lock_flav = blr->lock_flav; + } + ERROR_NT(status); - if (!send_smb(smbd_server_fd(),outbuf)) + if (!send_smb(smbd_server_fd(),outbuf)) { exit_server("generic_blocking_lock_error: send_smb failed."); + } } /**************************************************************************** @@ -335,7 +356,6 @@ static BOOL process_lockread(blocking_lock_record *blr) size_t numtoread; NTSTATUS status; files_struct *fsp = blr->fsp; - BOOL my_lock_ctx = False; numtoread = SVAL(inbuf,smb_vwv1); startpos = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv2); @@ -343,13 +363,13 @@ static BOOL process_lockread(blocking_lock_record *blr) numtoread = MIN(BUFFER_SIZE-outsize,numtoread); data = smb_buf(outbuf) + 3; - status = do_lock_spin(fsp, - (uint32)SVAL(inbuf,smb_pid), - (SMB_BIG_UINT)numtoread, - startpos, - READ_LOCK, - WINDOWS_LOCK, - &my_lock_ctx); + 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) && @@ -410,19 +430,18 @@ static BOOL process_lock(blocking_lock_record *blr) SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0; NTSTATUS status; files_struct *fsp = blr->fsp; - BOOL my_lock_ctx = False; count = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1); offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3); errno = 0; - status = do_lock_spin(fsp, - (uint32)SVAL(inbuf,smb_pid), - count, - offset, - WRITE_LOCK, - WINDOWS_LOCK, - &my_lock_ctx); + 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) && @@ -474,7 +493,6 @@ static BOOL process_lockingX(blocking_lock_record *blr) uint32 lock_pid; BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES); char *data; - BOOL my_lock_ctx = False; NTSTATUS status = NT_STATUS_OK; data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks); @@ -496,13 +514,14 @@ static BOOL process_lockingX(blocking_lock_record *blr) * request would never have been queued. JRA. */ errno = 0; - status = do_lock_spin(fsp, + status = do_lock(fsp, lock_pid, count, offset, - ((locktype & 1) ? READ_LOCK : WRITE_LOCK), + ((locktype & LOCKING_ANDX_SHARED_LOCK) ? + READ_LOCK : WRITE_LOCK), WINDOWS_LOCK, - &my_lock_ctx); + (int32)-1); if (NT_STATUS_IS_ERR(status)) { break; @@ -552,7 +571,6 @@ static BOOL process_trans2(blocking_lock_record *blr) extern int max_send; char *inbuf = blr->inbuf; char *outbuf; - BOOL my_lock_ctx = False; char params[2]; NTSTATUS status; @@ -562,7 +580,7 @@ static BOOL process_trans2(blocking_lock_record *blr) blr->offset, blr->lock_type, blr->lock_flav, - &my_lock_ctx); + (int32)-1); if (!NT_STATUS_IS_OK(status)) { if (ERROR_WAS_LOCK_DENIED(status)) { @@ -615,33 +633,41 @@ static BOOL blocking_lock_record_process(blocking_lock_record *blr) } /**************************************************************************** - Delete entries by fnum from the blocking lock pending queue. + Cancel entries by fnum from the blocking lock pending queue. *****************************************************************************/ -void remove_pending_lock_requests_by_fid(files_struct *fsp) +void cancel_pending_lock_requests_by_fid(files_struct *fsp, struct byte_range_lock *br_lck) { blocking_lock_record *blr, *next = NULL; for(blr = blocking_lock_queue; blr; blr = next) { next = blr->next; if(blr->fsp->fnum == fsp->fnum) { - struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); + unsigned char locktype = 0; + + if (blr->com_type == SMBlockingX) { + locktype = CVAL(blr->inbuf,smb_vwv3); + } if (br_lck) { DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); - brl_remove_pending_lock(br_lck, + brl_lock_cancel(br_lck, blr->lock_pid, procid_self(), blr->offset, blr->count, blr->lock_flav); - TALLOC_FREE(br_lck); + blocking_lock_cancel(fsp, + blr->lock_pid, + blr->offset, + blr->count, + blr->lock_flav, + locktype, + NT_STATUS_RANGE_NOT_LOCKED); } - - free_blocking_lock_record(blr); } } } @@ -664,7 +690,7 @@ void remove_pending_lock_requests_by_mid(int mid) DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); - brl_remove_pending_lock(br_lck, + brl_lock_cancel(br_lck, blr->lock_pid, procid_self(), blr->offset, @@ -674,6 +700,7 @@ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum )); } blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); + DLIST_REMOVE(blocking_lock_queue, blr); free_blocking_lock_record(blr); } } @@ -765,7 +792,7 @@ void process_blocking_lock_queue(time_t t) DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n", fsp->fnum, fsp->fsp_name )); - brl_remove_pending_lock(br_lck, + brl_lock_cancel(br_lck, blr->lock_pid, procid_self(), blr->offset, @@ -775,6 +802,7 @@ void process_blocking_lock_queue(time_t t) } blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT); + DLIST_REMOVE(blocking_lock_queue, blr); free_blocking_lock_record(blr); continue; } @@ -787,7 +815,7 @@ void process_blocking_lock_queue(time_t t) */ if (br_lck) { - brl_remove_pending_lock(br_lck, + brl_lock_cancel(br_lck, blr->lock_pid, procid_self(), blr->offset, @@ -799,6 +827,7 @@ void process_blocking_lock_queue(time_t t) DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n", vuid )); blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); + DLIST_REMOVE(blocking_lock_queue, blr); free_blocking_lock_record(blr); continue; } @@ -811,7 +840,7 @@ void process_blocking_lock_queue(time_t t) */ if (br_lck) { - brl_remove_pending_lock(br_lck, + brl_lock_cancel(br_lck, blr->lock_pid, procid_self(), blr->offset, @@ -822,6 +851,7 @@ void process_blocking_lock_queue(time_t t) DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) )); blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED); + DLIST_REMOVE(blocking_lock_queue, blr); free_blocking_lock_record(blr); change_to_root_user(); continue; @@ -837,7 +867,7 @@ void process_blocking_lock_queue(time_t t) struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp); if (br_lck) { - brl_remove_pending_lock(br_lck, + brl_lock_cancel(br_lck, blr->lock_pid, procid_self(), blr->offset, @@ -846,8 +876,106 @@ void process_blocking_lock_queue(time_t t) TALLOC_FREE(br_lck); } + DLIST_REMOVE(blocking_lock_queue, blr); free_blocking_lock_record(blr); } change_to_root_user(); } } + +/**************************************************************************** + Handle a cancel message. Lock already moved onto the cancel queue. +*****************************************************************************/ + +#define MSG_BLOCKING_LOCK_CANCEL_SIZE (sizeof(blocking_lock_record *) + sizeof(NTSTATUS)) + +static void process_blocking_lock_cancel_message(int msg_type, struct process_id src, + void *buf, size_t len) +{ + NTSTATUS err; + const char *msg = (const char *)buf; + blocking_lock_record *blr; + + if (buf == NULL) { + smb_panic("process_blocking_lock_cancel_message: null msg\n"); + } + + if (len != MSG_BLOCKING_LOCK_CANCEL_SIZE) { + DEBUG(0, ("process_blocking_lock_cancel_message: " + "Got invalid msg len %d\n", (int)len)); + smb_panic("process_blocking_lock_cancel_message: bad msg\n"); + } + + memcpy(&blr, msg, sizeof(blr)); + memcpy(&err, &msg[sizeof(blr)], sizeof(NTSTATUS)); + + DEBUG(10,("process_blocking_lock_cancel_message: returning error %s\n", + nt_errstr(err) )); + + blocking_lock_reply_error(blr, err); + DLIST_REMOVE(blocking_lock_cancelled_queue, blr); + free_blocking_lock_record(blr); +} + +/**************************************************************************** + Send ourselves a blocking lock cancelled message. Handled asynchronously above. +*****************************************************************************/ + +BOOL blocking_lock_cancel(files_struct *fsp, + uint32 lock_pid, + SMB_BIG_UINT offset, + SMB_BIG_UINT count, + enum brl_flavour lock_flav, + unsigned char locktype, + NTSTATUS err) +{ + static BOOL initialized; + char msg[MSG_BLOCKING_LOCK_CANCEL_SIZE]; + blocking_lock_record *blr; + + if (!initialized) { + /* Register our message. */ + message_register(MSG_SMB_BLOCKING_LOCK_CANCEL, + process_blocking_lock_cancel_message); + + initialized = True; + } + + for (blr = blocking_lock_queue; blr; blr = blr->next) { + if (fsp == blr->fsp && + lock_pid == blr->lock_pid && + offset == blr->offset && + count == blr->count && + lock_flav == blr->lock_flav) { + break; + } + } + + if (!blr) { + return False; + } + + /* Check the flags are right. */ + if (blr->com_type == SMBlockingX && + (locktype & LOCKING_ANDX_LARGE_FILES) != + (CVAL(blr->inbuf,smb_vwv3) & LOCKING_ANDX_LARGE_FILES)) { + return False; + } + + /* Move to cancelled queue. */ + DLIST_REMOVE(blocking_lock_queue, blr); + DLIST_ADD(blocking_lock_cancelled_queue, blr); + + /* Create the message. */ + memcpy(msg, &blr, sizeof(blr)); + memcpy(&msg[sizeof(blr)], &err, sizeof(NTSTATUS)); + + /* Don't need to be root here as we're only ever + sending to ourselves. */ + + message_send_pid(pid_to_procid(sys_getpid()), + MSG_SMB_BLOCKING_LOCK_CANCEL, + &msg, sizeof(msg), True); + + return True; +} diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 8a63d3b227..08e4a24a56 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -268,8 +268,6 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type) int err = 0; int err1 = 0; - remove_pending_lock_requests_by_fid(fsp); - if (fsp->aio_write_behind) { /* * If we're finishing write behind on a close we can get a write diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index ff3c6832e4..ec618db3f8 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -2371,7 +2371,6 @@ 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); - BOOL my_lock_ctx = False; START_PROFILE(SMBlockread); CHECK_FSP(fsp,conn); @@ -2396,13 +2395,13 @@ 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_spin(fsp, - (uint32)SVAL(inbuf,smb_pid), - (SMB_BIG_UINT)numtoread, - (SMB_BIG_UINT)startpos, - WRITE_LOCK, - WINDOWS_LOCK, - &my_lock_ctx); + status = do_lock(fsp, + (uint32)SVAL(inbuf,smb_pid), + (SMB_BIG_UINT)numtoread, + (SMB_BIG_UINT)startpos, + WRITE_LOCK, + WINDOWS_LOCK, + 0 /* zero timeout. */); if (NT_STATUS_V(status)) { #if 0 @@ -2412,7 +2411,7 @@ int reply_lockread(connection_struct *conn, char *inbuf,char *outbuf, int length * tester. JRA. */ - if (lp_blocking_locks(SNUM(conn)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) { + 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 @@ -3418,7 +3417,6 @@ int reply_lock(connection_struct *conn, SMB_BIG_UINT count,offset; NTSTATUS status; files_struct *fsp = file_fsp(inbuf,smb_vwv0); - BOOL my_lock_ctx = False; START_PROFILE(SMBlock); @@ -3432,17 +3430,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_spin(fsp, - (uint32)SVAL(inbuf,smb_pid), - count, - offset, - WRITE_LOCK, - WINDOWS_LOCK, - &my_lock_ctx); + status = do_lock(fsp, + (uint32)SVAL(inbuf,smb_pid), + count, + offset, + WRITE_LOCK, + WINDOWS_LOCK, + 0 /* zero timeout. */); + 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)) && !my_lock_ctx && ERROR_WAS_LOCK_DENIED(status)) { + 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 @@ -5228,7 +5227,6 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES)?True:False; BOOL err; - BOOL my_lock_ctx = False; NTSTATUS status = NT_STATUS_UNSUCCESSFUL; START_PROFILE(SMBlockingX); @@ -5244,11 +5242,6 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_NT(NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks)); } - if (locktype & LOCKING_ANDX_CANCEL_LOCK) { - /* Need to make this like a cancel.... JRA. */ - return ERROR_NT(NT_STATUS_UNSUCCESSFUL); - } - /* Check if this is an oplock break on a file we have granted an oplock on. */ @@ -5360,7 +5353,11 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, /* Setup the timeout in seconds. */ - lock_timeout = ((lock_timeout == -1) ? -1 : (lock_timeout+999)/1000); + if (lp_blocking_locks(SNUM(conn))) { + lock_timeout = ((lock_timeout == -1) ? -1 : (lock_timeout+999)/1000); + } else { + lock_timeout = 0; + } /* Now do any requested locks */ data += ((large_file_format ? 20 : 10)*num_ulocks); @@ -5369,7 +5366,8 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, of smb_lkrng structs */ for(i = 0; i < (int)num_locks; i++) { - enum brl_type lock_type = ((locktype & 1) ? READ_LOCK:WRITE_LOCK); + 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); @@ -5387,31 +5385,54 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, (double)count, (unsigned int)lock_pid, fsp->fsp_name, (int)lock_timeout )); - status = do_lock_spin(fsp, + if (locktype & LOCKING_ANDX_CANCEL_LOCK) { + if (lp_blocking_locks(SNUM(conn))) { + + /* Schedule a message to ourselves to + remove the blocking lock record and + return the right error. */ + + if (!blocking_lock_cancel(fsp, + lock_pid, + offset, + count, + WINDOWS_LOCK, + locktype, + NT_STATUS_FILE_LOCK_CONFLICT)) { + END_PROFILE(SMBlockingX); + return ERROR_NT(NT_STATUS_DOS(ERRDOS, ERRcancelviolation)); + } + } + /* Remove a matching pending lock. */ + status = do_lock_cancel(fsp, + lock_pid, + count, + offset, + WINDOWS_LOCK); + } else { + status = do_lock(fsp, lock_pid, count, offset, lock_type, WINDOWS_LOCK, - &my_lock_ctx); + lock_timeout); - if (NT_STATUS_V(status)) { - /* - * 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. So I removed a "&& - * !my_lock_ctx" from the following if statement. JRA. - */ - if ((lock_timeout != 0) && - lp_blocking_locks(SNUM(conn)) && - ERROR_WAS_LOCK_DENIED(status)) { + if (NT_STATUS_V(status)) { /* - * A blocking lock was requested. Package up - * this smb into a queued request and push it - * onto the blocking lock queue. + * 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. */ - if(push_blocking_lock_request(inbuf, length, + 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, @@ -5420,17 +5441,25 @@ int reply_lockingX(connection_struct *conn, char *inbuf, char *outbuf, WINDOWS_LOCK, offset, count)) { - END_PROFILE(SMBlockingX); - return -1; + END_PROFILE(SMBlockingX); + return -1; + } } } - break; + } + + if (NT_STATUS_V(status)) { + END_PROFILE(SMBlockingX); + return ERROR_NT(status); } } /* If any of the above locks failed, then we must unlock all of the previous locks (X/Open spec). */ - if (i != num_locks && num_locks != 0) { + + if (!(locktype & LOCKING_ANDX_CANCEL_LOCK) && + (i != num_locks) && + (num_locks != 0)) { /* * 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 diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 43437469d7..329d5bb0a5 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -4505,7 +4505,6 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", uint32 lock_pid; BOOL lock_blocking; enum brl_type lock_type; - BOOL my_lock_ctx; if (fsp == NULL || fsp->fh->fd == -1) { return ERROR_NT(NT_STATUS_INVALID_HANDLE); @@ -4564,8 +4563,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", count, offset, lock_type, - POSIX_LOCK, - &my_lock_ctx); + POSIX_LOCK); if (lock_blocking && lp_blocking_locks(SNUM(conn)) && ERROR_WAS_LOCK_DENIED(status)) { /* -- cgit