mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
s3:smbd: abstract the main locking logic from the LockingAndX parsing
This prepares SMB2 Lock support. metze
This commit is contained in:
parent
be1e5493c5
commit
598a9892bc
@ -7223,6 +7223,212 @@ uint64_t get_lock_offset(const uint8_t *data, int data_offset,
|
||||
return offset;
|
||||
}
|
||||
|
||||
struct smbd_lock_element {
|
||||
uint32_t smbpid;
|
||||
enum brl_type brltype;
|
||||
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));
|
||||
|
||||
if (e->brltype != UNLOCK_LOCK) {
|
||||
/* this can only happen with SMB2 */
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
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,
|
||||
e->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,
|
||||
e->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.
|
||||
****************************************************************************/
|
||||
@ -7235,14 +7441,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);
|
||||
|
||||
@ -7355,12 +7562,27 @@ 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);
|
||||
ulocks[i].brltype = UNLOCK_LOCK;
|
||||
|
||||
/*
|
||||
* There is no error code marked "stupid client bug".... :-).
|
||||
@ -7370,32 +7592,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 */
|
||||
@ -7405,11 +7601,23 @@ 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);
|
||||
|
||||
if (locktype & LOCKING_ANDX_SHARED_LOCK) {
|
||||
if (locktype & LOCKING_ANDX_CANCEL_LOCK) {
|
||||
locks[i].brltype = PENDING_READ_LOCK;
|
||||
} else {
|
||||
locks[i].brltype = READ_LOCK;
|
||||
}
|
||||
} else {
|
||||
if (locktype & LOCKING_ANDX_CANCEL_LOCK) {
|
||||
locks[i].brltype = PENDING_WRITE_LOCK;
|
||||
} else {
|
||||
locks[i].brltype = WRITE_LOCK;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There is no error code marked "stupid client bug".... :-).
|
||||
@ -7419,154 +7627,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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user