#include "includes.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_LOCKING
#define ZERO_ZERO 0
static TDB_CONTEXT *tdb;
static void print_lock_struct(unsigned int i, struct lock_struct *pls)
{
DEBUG(10,("[%u]: smbpid = %u, tid = %u, pid = %u, ",
i,
(unsigned int)pls->context.smbpid,
(unsigned int)pls->context.tid,
(unsigned int)procid_to_pid(&pls->context.pid) ));
DEBUG(10,("start = %.0f, size = %.0f, fnum = %d, %s %s\n",
(double)pls->start,
(double)pls->size,
pls->fnum,
lock_type_name(pls->lock_type),
lock_flav_name(pls->lock_flav) ));
}
BOOL brl_same_context(const struct lock_context *ctx1,
const struct lock_context *ctx2)
{
return (procid_equal(&ctx1->pid, &ctx2->pid) &&
(ctx1->smbpid == ctx2->smbpid) &&
(ctx1->tid == ctx2->tid));
}
static BOOL brl_overlap(const struct lock_struct *lck1,
const struct lock_struct *lck2)
{
if (lck1->size != 0 &&
lck1->start == lck2->start &&
lck1->size == lck2->size) {
return True;
}
if (lck1->start >= (lck2->start+lck2->size) ||
lck2->start >= (lck1->start+lck1->size)) {
return False;
}
return True;
}
static BOOL brl_conflict(const struct lock_struct *lck1,
const struct lock_struct *lck2)
{
if (IS_PENDING_LOCK(lck1->lock_type) || IS_PENDING_LOCK(lck2->lock_type))
return False;
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
return False;
}
if (brl_same_context(&lck1->context, &lck2->context) &&
lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
return False;
}
return brl_overlap(lck1, lck2);
}
static BOOL brl_conflict_posix(const struct lock_struct *lck1,
const struct lock_struct *lck2)
{
#if defined(DEVELOPER)
SMB_ASSERT(lck1->lock_flav == POSIX_LOCK);
SMB_ASSERT(lck2->lock_flav == POSIX_LOCK);
#endif
if (IS_PENDING_LOCK(lck1->lock_type) || IS_PENDING_LOCK(lck2->lock_type))
return False;
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
return False;
}
if (brl_same_context(&lck1->context, &lck2->context)) {
return False;
}
return brl_overlap(lck1, lck2);
}
#if ZERO_ZERO
static BOOL brl_conflict1(const struct lock_struct *lck1,
const struct lock_struct *lck2)
{
if (IS_PENDING_LOCK(lck1->lock_type) || IS_PENDING_LOCK(lck2->lock_type))
return False;
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
return False;
}
if (brl_same_context(&lck1->context, &lck2->context) &&
lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
return False;
}
if (lck2->start == 0 && lck2->size == 0 && lck1->size != 0) {
return True;
}
if (lck1->start >= (lck2->start + lck2->size) ||
lck2->start >= (lck1->start + lck1->size)) {
return False;
}
return True;
}
#endif
static BOOL brl_conflict_other(const struct lock_struct *lck1, const struct lock_struct *lck2)
{
if (IS_PENDING_LOCK(lck1->lock_type) || IS_PENDING_LOCK(lck2->lock_type))
return False;
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
return False;
if (lck1->lock_flav == POSIX_LOCK && lck2->lock_flav == POSIX_LOCK)
return False;
if (!(lck2->lock_type == WRITE_LOCK && lck1->lock_type == READ_LOCK)) {
if (brl_same_context(&lck1->context, &lck2->context) &&
lck1->fnum == lck2->fnum)
return False;
}
return brl_overlap(lck1, lck2);
}
static BOOL brl_pending_overlap(const struct lock_struct *lock, const struct lock_struct *pend_lock)
{
if ((lock->start <= pend_lock->start) && (lock->start + lock->size > pend_lock->start))
return True;
if ((lock->start >= pend_lock->start) && (lock->start <= pend_lock->start + pend_lock->size))
return True;
return False;
}
static NTSTATUS brl_lock_failed(files_struct *fsp, const struct lock_struct *lock, BOOL blocking_lock)
{
if (lock->start >= 0xEF000000 && (lock->start >> 63) == 0) {
if (!blocking_lock) {
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 (!blocking_lock) {
fsp->last_lock_failure = *lock;
}
return NT_STATUS_LOCK_NOT_GRANTED;
}
void brl_init(int read_only)
{
if (tdb) {
return;
}
tdb = tdb_open_log(lock_path("brlock.tdb"),
lp_open_files_db_hash_size(),
TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST),
read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644 );
if (!tdb) {
DEBUG(0,("Failed to open byte range locking database %s\n",
lock_path("brlock.tdb")));
return;
}
tdb_set_max_dead(tdb, 5);
}
void brl_shutdown(int read_only)
{
if (!tdb) {
return;
}
tdb_close(tdb);
}
#if ZERO_ZERO
static int lock_compare(const struct lock_struct *lck1,
const struct lock_struct *lck2)
{
if (lck1->start != lck2->start) {
return (lck1->start - lck2->start);
}
if (lck2->size != lck1->size) {
return ((int)lck1->size - (int)lck2->size);
}
return 0;
}
#endif
static NTSTATUS brl_lock_windows(struct byte_range_lock *br_lck,
struct lock_struct *plock, BOOL blocking_lock)
{
unsigned int i;
files_struct *fsp = br_lck->fsp;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
for (i=0; i < br_lck->num_locks; i++) {
if (brl_conflict(&locks[i], plock)) {
plock->context.smbpid = locks[i].context.smbpid;
return brl_lock_failed(fsp,plock,blocking_lock);
}
#if ZERO_ZERO
if (plock->start == 0 && plock->size == 0 &&
locks[i].size == 0) {
break;
}
#endif
}
if (!IS_PENDING_LOCK(plock->lock_type) && lp_posix_locking(fsp->conn->params)) {
int errno_ret;
if (!set_posix_lock_windows_flavour(fsp,
plock->start,
plock->size,
plock->lock_type,
&plock->context,
locks,
br_lck->num_locks,
&errno_ret)) {
plock->context.smbpid = 0xFFFFFFFF;
if (errno_ret == EACCES || errno_ret == EAGAIN) {
return NT_STATUS_FILE_LOCK_CONFLICT;
} else {
return map_nt_error_from_unix(errno);
}
}
}
locks = (struct lock_struct *)SMB_REALLOC(locks, (br_lck->num_locks + 1) * sizeof(*locks));
if (!locks) {
return NT_STATUS_NO_MEMORY;
}
memcpy(&locks[br_lck->num_locks], plock, sizeof(struct lock_struct));
br_lck->num_locks += 1;
br_lck->lock_data = (void *)locks;
br_lck->modified = True;
return NT_STATUS_OK;
}
static unsigned int brlock_posix_split_merge(struct lock_struct *lck_arr,
const struct lock_struct *ex,
const struct lock_struct *plock,
BOOL *lock_was_added)
{
BOOL lock_types_differ = (ex->lock_type != plock->lock_type);
if (!brl_same_context(&ex->context, &plock->context)) {
memcpy(&lck_arr[0], ex, sizeof(struct lock_struct));
return 1;
}
if ( (ex->start > (plock->start + plock->size)) ||
(plock->start > (ex->start + ex->size))) {
memcpy(&lck_arr[0], ex, sizeof(struct lock_struct));
return 1;
}
if ( (ex->start >= plock->start) &&
(ex->start + ex->size <= plock->start + plock->size) ) {
memcpy(&lck_arr[0], plock, sizeof(struct lock_struct));
*lock_was_added = True;
return 1;
}
if ( (ex->start >= plock->start) &&
(ex->start <= plock->start + plock->size) &&
(ex->start + ex->size > plock->start + plock->size) ) {
*lock_was_added = True;
if (lock_types_differ) {
memcpy(&lck_arr[0], plock, sizeof(struct lock_struct));
memcpy(&lck_arr[1], ex, sizeof(struct lock_struct));
lck_arr[1].start = plock->start + plock->size;
lck_arr[1].size = (ex->start + ex->size) - (plock->start + plock->size);
return 2;
} else {
memcpy(&lck_arr[0], plock, sizeof(struct lock_struct));
lck_arr[0].start = plock->start;
lck_arr[0].size = (ex->start + ex->size) - plock->start;
return 1;
}
}
if ( (ex->start < plock->start) &&
(ex->start + ex->size >= plock->start) &&
(ex->start + ex->size <= plock->start + plock->size) ) {
*lock_was_added = True;
if (lock_types_differ) {
memcpy(&lck_arr[0], ex, sizeof(struct lock_struct));
memcpy(&lck_arr[1], plock, sizeof(struct lock_struct));
lck_arr[0].size = plock->start - ex->start;
return 2;
} else {
memcpy(&lck_arr[0], ex, sizeof(struct lock_struct));
lck_arr[0].size = (plock->start + plock->size) - ex->start;
return 1;
}
}
if ( (ex->start < plock->start) && (ex->start + ex->size > plock->start + plock->size) ) {
*lock_was_added = True;
if (lock_types_differ) {
memcpy(&lck_arr[0], ex, sizeof(struct lock_struct));
memcpy(&lck_arr[1], plock, sizeof(struct lock_struct));
memcpy(&lck_arr[2], ex, sizeof(struct lock_struct));
lck_arr[0].size = plock->start - ex->start;
lck_arr[2].start = plock->start + plock->size;
lck_arr[2].size = (ex->start + ex->size) - (plock->start + plock->size);
return 3;
} else {
memcpy(&lck_arr[0], ex, sizeof(struct lock_struct));
return 1;
}
}
smb_panic("brlock_posix_split_merge\n");
abort();
return 0;
}
static NTSTATUS brl_lock_posix(struct byte_range_lock *br_lck,
struct lock_struct *plock)
{
unsigned int i, count;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
struct lock_struct *tp;
BOOL lock_was_added = False;
BOOL signal_pending_read = False;
if (plock->start == 0 && plock->size == 0) {
return NT_STATUS_INVALID_PARAMETER;
}
if (plock->start + plock->size < plock->start ||
plock->start + plock->size < plock->size) {
return NT_STATUS_INVALID_PARAMETER;
}
tp = SMB_MALLOC_ARRAY(struct lock_struct, (br_lck->num_locks + 2));
if (!tp) {
return NT_STATUS_NO_MEMORY;
}
count = 0;
for (i=0; i < br_lck->num_locks; i++) {
struct lock_struct *curr_lock = &locks[i];
if (curr_lock->lock_type == PENDING_READ_LOCK &&
brl_pending_overlap(plock, curr_lock)) {
signal_pending_read = True;
}
if (curr_lock->lock_flav == WINDOWS_LOCK) {
if (brl_conflict(curr_lock, plock)) {
SAFE_FREE(tp);
plock->context.smbpid = curr_lock->context.smbpid;
return NT_STATUS_FILE_LOCK_CONFLICT;
}
memcpy(&tp[count], curr_lock, sizeof(struct lock_struct));
count++;
} else {
if (brl_conflict_posix(curr_lock, plock)) {
SAFE_FREE(tp);
plock->context.smbpid = curr_lock->context.smbpid;
return NT_STATUS_FILE_LOCK_CONFLICT;
}
count += brlock_posix_split_merge(&tp[count], curr_lock, plock, &lock_was_added);
}
}
if (!lock_was_added) {
memcpy(&tp[count], plock, sizeof(struct lock_struct));
count++;
}
if (!IS_PENDING_LOCK(plock->lock_type) && lp_posix_locking(br_lck->fsp->conn->params)) {
int errno_ret;
if (!set_posix_lock_posix_flavour(br_lck->fsp,
plock->start,
plock->size,
plock->lock_type,
&errno_ret)) {
plock->context.smbpid = 0xFFFFFFFF;
if (errno_ret == EACCES || errno_ret == EAGAIN) {
SAFE_FREE(tp);
return NT_STATUS_FILE_LOCK_CONFLICT;
} else {
SAFE_FREE(tp);
return map_nt_error_from_unix(errno);
}
}
}
tp = (struct lock_struct *)SMB_REALLOC(tp, count * sizeof(*locks));
if (!tp) {
return NT_STATUS_NO_MEMORY;
}
br_lck->num_locks = count;
SAFE_FREE(br_lck->lock_data);
br_lck->lock_data = (void *)tp;
locks = tp;
br_lck->modified = True;
if (signal_pending_read) {
for (i=0; i < br_lck->num_locks; i++) {
struct lock_struct *pend_lock = &locks[i];
if (!IS_PENDING_LOCK(pend_lock->lock_type)) {
continue;
}
if (pend_lock->lock_type == PENDING_READ_LOCK &&
brl_pending_overlap(plock, pend_lock)) {
DEBUG(10,("brl_lock_posix: sending unlock message to pid %s\n",
procid_str_static(&pend_lock->context.pid )));
message_send_pid(pend_lock->context.pid,
MSG_SMB_UNLOCK,
NULL, 0, True);
}
}
}
return NT_STATUS_OK;
}
NTSTATUS brl_lock(struct byte_range_lock *br_lck,
uint32 smbpid,
struct process_id pid,
br_off start,
br_off size,
enum brl_type lock_type,
enum brl_flavour lock_flav,
BOOL blocking_lock,
uint32 *psmbpid)
{
NTSTATUS ret;
struct lock_struct lock;
#if !ZERO_ZERO
if (start == 0 && size == 0) {
DEBUG(0,("client sent 0/0 lock - please report this\n"));
}
#endif
lock.context.smbpid = smbpid;
lock.context.pid = pid;
lock.context.tid = br_lck->fsp->conn->cnum;
lock.start = start;
lock.size = size;
lock.fnum = br_lck->fsp->fnum;
lock.lock_type = lock_type;
lock.lock_flav = lock_flav;
if (lock_flav == WINDOWS_LOCK) {
ret = brl_lock_windows(br_lck, &lock, blocking_lock);
} else {
ret = brl_lock_posix(br_lck, &lock);
}
#if ZERO_ZERO
qsort(br_lck->lock_data, (size_t)br_lck->num_locks, sizeof(lock), lock_compare);
#endif
if (!NT_STATUS_IS_OK(ret) && psmbpid) {
*psmbpid = lock.context.smbpid;
}
return ret;
}
static BOOL brl_unlock_windows(struct byte_range_lock *br_lck, const struct lock_struct *plock)
{
unsigned int i, j;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
enum brl_type deleted_lock_type = READ_LOCK;
#if ZERO_ZERO
for (i = 0; i < br_lck->num_locks; i++) {
struct lock_struct *lock = &locks[i];
if (lock->lock_type == WRITE_LOCK &&
brl_same_context(&lock->context, &plock->context) &&
lock->fnum == plock->fnum &&
lock->lock_flav == WINDOWS_LOCK &&
lock->start == plock->start &&
lock->size == plock->size) {
deleted_lock_type = lock->lock_type;
break;
}
}
if (i != br_lck->num_locks) {
goto unlock_continue;
}
#endif
for (i = 0; i < br_lck->num_locks; i++) {
struct lock_struct *lock = &locks[i];
if (brl_same_context(&lock->context, &plock->context) &&
lock->fnum == plock->fnum &&
lock->lock_flav == WINDOWS_LOCK &&
lock->start == plock->start &&
lock->size == plock->size ) {
deleted_lock_type = lock->lock_type;
break;
}
}
if (i == br_lck->num_locks) {
return False;
}
#if ZERO_ZERO
unlock_continue:
#endif
if (i < br_lck->num_locks - 1) {
memmove(&locks[i], &locks[i+1],
sizeof(*locks)*((br_lck->num_locks-1) - i));
}
br_lck->num_locks -= 1;
br_lck->modified = True;
if(lp_posix_locking(br_lck->fsp->conn->params)) {
release_posix_lock_windows_flavour(br_lck->fsp,
plock->start,
plock->size,
deleted_lock_type,
&plock->context,
locks,
br_lck->num_locks);
}
for (j=0; j < br_lck->num_locks; j++) {
struct lock_struct *pend_lock = &locks[j];
if (!IS_PENDING_LOCK(pend_lock->lock_type)) {
continue;
}
if (brl_pending_overlap(plock, pend_lock)) {
DEBUG(10,("brl_unlock: sending unlock message to pid %s\n",
procid_str_static(&pend_lock->context.pid )));
message_send_pid(pend_lock->context.pid,
MSG_SMB_UNLOCK,
NULL, 0, True);
}
}
return True;
}
static BOOL brl_unlock_posix(struct byte_range_lock *br_lck, const struct lock_struct *plock)
{
unsigned int i, j, count;
struct lock_struct *tp;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
BOOL overlap_found = False;
if (plock->start == 0 && plock->size == 0) {
return False;
}
if (plock->start + plock->size < plock->start ||
plock->start + plock->size < plock->size) {
DEBUG(10,("brl_unlock_posix: lock wrap\n"));
return False;
}
tp = SMB_MALLOC_ARRAY(struct lock_struct, (br_lck->num_locks + 1));
if (!tp) {
DEBUG(10,("brl_unlock_posix: malloc fail\n"));
return False;
}
count = 0;
for (i = 0; i < br_lck->num_locks; i++) {
struct lock_struct *lock = &locks[i];
struct lock_struct tmp_lock[3];
BOOL lock_was_added = False;
unsigned int tmp_count;
if (IS_PENDING_LOCK(lock->lock_type) ||
!brl_same_context(&lock->context, &plock->context)) {
memcpy(&tp[count], lock, sizeof(struct lock_struct));
count++;
continue;
}
tmp_count = brlock_posix_split_merge(&tmp_lock[0], &locks[i], plock, &lock_was_added);
if (tmp_count == 1) {
if (tmp_lock[0].lock_type != UNLOCK_LOCK) {
SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type);
memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct));
count++;
} else {
SMB_ASSERT(tmp_lock[0].lock_type == UNLOCK_LOCK);
overlap_found = True;
}
continue;
} else if (tmp_count == 2) {
if (tmp_lock[0].lock_type != UNLOCK_LOCK) {
SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type);
SMB_ASSERT(tmp_lock[1].lock_type == UNLOCK_LOCK);
memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct));
if (tmp_lock[0].size != locks[i].size) {
overlap_found = True;
}
} else {
SMB_ASSERT(tmp_lock[0].lock_type == UNLOCK_LOCK);
SMB_ASSERT(tmp_lock[1].lock_type == locks[i].lock_type);
memcpy(&tp[count], &tmp_lock[1], sizeof(struct lock_struct));
if (tmp_lock[1].start != locks[i].start) {
overlap_found = True;
}
}
count++;
continue;
} else {
SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type);
SMB_ASSERT(tmp_lock[1].lock_type == UNLOCK_LOCK);
SMB_ASSERT(tmp_lock[2].lock_type == locks[i].lock_type);
memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct));
count++;
memcpy(&tp[count], &tmp_lock[2], sizeof(struct lock_struct));
count++;
overlap_found = True;
if (i < br_lck->num_locks - 1) {
memcpy(&tp[count], &locks[i+1],
sizeof(*locks)*((br_lck->num_locks-1) - i));
count += ((br_lck->num_locks-1) - i);
}
break;
}
}
if (!overlap_found) {
SAFE_FREE(tp);
DEBUG(10,("brl_unlock_posix: No overlap - unlocked.\n"));
return True;
}
if(lp_posix_locking(br_lck->fsp->conn->params)) {
release_posix_lock_posix_flavour(br_lck->fsp,
plock->start,
plock->size,
&plock->context,
tp,
count);
}
if (count) {
tp = (struct lock_struct *)SMB_REALLOC(tp, count * sizeof(*locks));
if (!tp) {
DEBUG(10,("brl_unlock_posix: realloc fail\n"));
return False;
}
} else {
SAFE_FREE(tp);
tp = NULL;
}
br_lck->num_locks = count;
SAFE_FREE(br_lck->lock_data);
locks = tp;
br_lck->lock_data = (void *)tp;
br_lck->modified = True;
for (j=0; j < br_lck->num_locks; j++) {
struct lock_struct *pend_lock = &locks[j];
if (!IS_PENDING_LOCK(pend_lock->lock_type)) {
continue;
}
if (brl_pending_overlap(plock, pend_lock)) {
DEBUG(10,("brl_unlock: sending unlock message to pid %s\n",
procid_str_static(&pend_lock->context.pid )));
message_send_pid(pend_lock->context.pid,
MSG_SMB_UNLOCK,
NULL, 0, True);
}
}
return True;
}
BOOL brl_unlock(struct byte_range_lock *br_lck,
uint32 smbpid,
struct process_id pid,
br_off start,
br_off size,
enum brl_flavour lock_flav)
{
struct lock_struct lock;
lock.context.smbpid = smbpid;
lock.context.pid = pid;
lock.context.tid = br_lck->fsp->conn->cnum;
lock.start = start;
lock.size = size;
lock.fnum = br_lck->fsp->fnum;
lock.lock_type = UNLOCK_LOCK;
lock.lock_flav = lock_flav;
if (lock_flav == WINDOWS_LOCK) {
return brl_unlock_windows(br_lck, &lock);
} else {
return brl_unlock_posix(br_lck, &lock);
}
}
BOOL brl_locktest(struct byte_range_lock *br_lck,
uint32 smbpid,
struct process_id pid,
br_off start,
br_off size,
enum brl_type lock_type,
enum brl_flavour lock_flav)
{
BOOL ret = True;
unsigned int i;
struct lock_struct lock;
const struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
files_struct *fsp = br_lck->fsp;
lock.context.smbpid = smbpid;
lock.context.pid = pid;
lock.context.tid = br_lck->fsp->conn->cnum;
lock.start = start;
lock.size = size;
lock.fnum = fsp->fnum;
lock.lock_type = lock_type;
lock.lock_flav = lock_flav;
for (i=0; i < br_lck->num_locks; i++) {
if (brl_conflict_other(&locks[i], &lock)) {
return False;
}
}
if(lp_posix_locking(fsp->conn->params) && (lock_flav == WINDOWS_LOCK)) {
ret = is_posix_locked(fsp, &start, &size, &lock_type, WINDOWS_LOCK);
DEBUG(10,("brl_locktest: posix start=%.0f len=%.0f %s for fnum %d file %s\n",
(double)start, (double)size, ret ? "locked" : "unlocked",
fsp->fnum, fsp->fsp_name ));
ret = !ret;
}
return ret;
}
NTSTATUS brl_lockquery(struct byte_range_lock *br_lck,
uint32 *psmbpid,
struct process_id pid,
br_off *pstart,
br_off *psize,
enum brl_type *plock_type,
enum brl_flavour lock_flav)
{
unsigned int i;
struct lock_struct lock;
const struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
files_struct *fsp = br_lck->fsp;
lock.context.smbpid = *psmbpid;
lock.context.pid = pid;
lock.context.tid = br_lck->fsp->conn->cnum;
lock.start = *pstart;
lock.size = *psize;
lock.fnum = fsp->fnum;
lock.lock_type = *plock_type;
lock.lock_flav = lock_flav;
for (i=0; i < br_lck->num_locks; i++) {
const struct lock_struct *exlock = &locks[i];
BOOL conflict = False;
if (exlock->lock_flav == WINDOWS_LOCK) {
conflict = brl_conflict(exlock, &lock);
} else {
conflict = brl_conflict_posix(exlock, &lock);
}
if (conflict) {
*psmbpid = exlock->context.smbpid;
*pstart = exlock->start;
*psize = exlock->size;
*plock_type = exlock->lock_type;
return NT_STATUS_LOCK_NOT_GRANTED;
}
}
if(lp_posix_locking(fsp->conn->params)) {
BOOL ret = is_posix_locked(fsp, pstart, psize, plock_type, POSIX_LOCK);
DEBUG(10,("brl_lockquery: posix start=%.0f len=%.0f %s for fnum %d file %s\n",
(double)*pstart, (double)*psize, ret ? "locked" : "unlocked",
fsp->fnum, fsp->fsp_name ));
if (ret) {
*psmbpid = 0xFFFF;
return NT_STATUS_LOCK_NOT_GRANTED;
}
}
return NT_STATUS_OK;
}
BOOL brl_lock_cancel(struct byte_range_lock *br_lck,
uint32 smbpid,
struct process_id pid,
br_off start,
br_off size,
enum brl_flavour lock_flav)
{
unsigned int i;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
struct lock_context context;
context.smbpid = smbpid;
context.pid = pid;
context.tid = br_lck->fsp->conn->cnum;
for (i = 0; i < br_lck->num_locks; i++) {
struct lock_struct *lock = &locks[i];
if (brl_same_context(&lock->context, &context) &&
lock->fnum == br_lck->fsp->fnum &&
IS_PENDING_LOCK(lock->lock_type) &&
lock->lock_flav == lock_flav &&
lock->start == start &&
lock->size == size) {
break;
}
}
if (i == br_lck->num_locks) {
return False;
}
if (i < br_lck->num_locks - 1) {
memmove(&locks[i], &locks[i+1],
sizeof(*locks)*((br_lck->num_locks-1) - i));
}
br_lck->num_locks -= 1;
br_lck->modified = True;
return True;
}
void brl_close_fnum(struct byte_range_lock *br_lck)
{
files_struct *fsp = br_lck->fsp;
uint16 tid = fsp->conn->cnum;
int fnum = fsp->fnum;
unsigned int i, j, dcount=0;
int num_deleted_windows_locks = 0;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
struct process_id pid = procid_self();
BOOL unlock_individually = False;
if(lp_posix_locking(fsp->conn->params)) {
for (i=0; i < br_lck->num_locks; i++) {
struct lock_struct *lock = &locks[i];
if (!procid_equal(&lock->context.pid, &pid)) {
continue;
}
if (lock->lock_type != READ_LOCK && lock->lock_type != WRITE_LOCK) {
continue;
}
if (lock->context.tid != tid || lock->fnum != fnum) {
unlock_individually = True;
break;
}
}
if (unlock_individually) {
struct lock_struct *locks_copy;
unsigned int num_locks_copy;
if (br_lck->num_locks) {
locks_copy = (struct lock_struct *)TALLOC_MEMDUP(br_lck, locks, br_lck->num_locks * sizeof(struct lock_struct));
if (!locks_copy) {
smb_panic("brl_close_fnum: talloc fail.\n");
}
} else {
locks_copy = NULL;
}
num_locks_copy = br_lck->num_locks;
for (i=0; i < num_locks_copy; i++) {
struct lock_struct *lock = &locks_copy[i];
if (lock->context.tid == tid && procid_equal(&lock->context.pid, &pid) &&
(lock->fnum == fnum)) {
brl_unlock(br_lck,
lock->context.smbpid,
pid,
lock->start,
lock->size,
lock->lock_flav);
}
}
return;
}
}
for (i=0; i < br_lck->num_locks; i++) {
struct lock_struct *lock = &locks[i];
BOOL del_this_lock = False;
if (lock->context.tid == tid && procid_equal(&lock->context.pid, &pid)) {
if ((lock->lock_flav == WINDOWS_LOCK) && (lock->fnum == fnum)) {
del_this_lock = True;
num_deleted_windows_locks++;
} else if (lock->lock_flav == POSIX_LOCK) {
del_this_lock = True;
}
}
if (del_this_lock) {
for (j=0; j < br_lck->num_locks; j++) {
struct lock_struct *pend_lock = &locks[j];
if (!IS_PENDING_LOCK(pend_lock->lock_type)) {
continue;
}
if (pend_lock->context.tid == tid &&
procid_equal(&pend_lock->context.pid, &pid) &&
pend_lock->fnum == fnum) {
continue;
}
if (brl_pending_overlap(lock, pend_lock)) {
message_send_pid(pend_lock->context.pid,
MSG_SMB_UNLOCK,
NULL, 0, True);
}
}
if (br_lck->num_locks > 1 && i < br_lck->num_locks - 1) {
memmove(&locks[i], &locks[i+1],
sizeof(*locks)*((br_lck->num_locks-1) - i));
}
br_lck->num_locks--;
br_lck->modified = True;
i--;
dcount++;
}
}
if(lp_posix_locking(fsp->conn->params) && num_deleted_windows_locks) {
reduce_windows_lock_ref_count(fsp, num_deleted_windows_locks);
}
}
static BOOL validate_lock_entries(unsigned int *pnum_entries, struct lock_struct **pplocks)
{
unsigned int i;
unsigned int num_valid_entries = 0;
struct lock_struct *locks = *pplocks;
for (i = 0; i < *pnum_entries; i++) {
struct lock_struct *lock_data = &locks[i];
if (!process_exists(lock_data->context.pid)) {
ZERO_STRUCTP(lock_data);
} else {
num_valid_entries++;
}
}
if (num_valid_entries != *pnum_entries) {
struct lock_struct *new_lock_data = NULL;
if (num_valid_entries) {
new_lock_data = SMB_MALLOC_ARRAY(struct lock_struct, num_valid_entries);
if (!new_lock_data) {
DEBUG(3, ("malloc fail\n"));
return False;
}
num_valid_entries = 0;
for (i = 0; i < *pnum_entries; i++) {
struct lock_struct *lock_data = &locks[i];
if (lock_data->context.smbpid &&
lock_data->context.tid) {
memcpy(&new_lock_data[num_valid_entries],
lock_data, sizeof(struct lock_struct));
num_valid_entries++;
}
}
}
SAFE_FREE(*pplocks);
*pplocks = new_lock_data;
*pnum_entries = num_valid_entries;
}
return True;
}
static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
{
struct lock_struct *locks;
struct lock_key *key;
unsigned int i;
unsigned int num_locks = 0;
unsigned int orig_num_locks = 0;
BRLOCK_FN(traverse_callback) = (BRLOCK_FN_CAST())state;
locks = (struct lock_struct *)memdup(dbuf.dptr, dbuf.dsize);
if (!locks) {
return -1;
}
key = (struct lock_key *)kbuf.dptr;
orig_num_locks = num_locks = dbuf.dsize/sizeof(*locks);
if (!validate_lock_entries(&num_locks, &locks)) {
SAFE_FREE(locks);
return -1;
}
if (orig_num_locks != num_locks) {
dbuf.dptr = (char *)locks;
dbuf.dsize = num_locks * sizeof(*locks);
if (dbuf.dsize) {
tdb_store(ttdb, kbuf, dbuf, TDB_REPLACE);
} else {
tdb_delete(ttdb, kbuf);
}
}
for ( i=0; i<num_locks; i++) {
traverse_callback(key->device,
key->inode,
locks[i].context.pid,
locks[i].lock_type,
locks[i].lock_flav,
locks[i].start,
locks[i].size);
}
SAFE_FREE(locks);
return 0;
}
int brl_forall(BRLOCK_FN(fn))
{
if (!tdb) {
return 0;
}
return tdb_traverse(tdb, traverse_fn, (void *)fn);
}
static int byte_range_lock_destructor(struct byte_range_lock *br_lck)
{
TDB_DATA key;
key.dptr = (char *)&br_lck->key;
key.dsize = sizeof(struct lock_key);
if (br_lck->read_only) {
SMB_ASSERT(!br_lck->modified);
}
if (!br_lck->modified) {
goto done;
}
if (br_lck->num_locks == 0) {
if (tdb_delete(tdb, key) == -1) {
smb_panic("Could not delete byte range lock entry\n");
}
} else {
TDB_DATA data;
data.dptr = (char *)br_lck->lock_data;
data.dsize = br_lck->num_locks * sizeof(struct lock_struct);
if (tdb_store(tdb, key, data, TDB_REPLACE) == -1) {
smb_panic("Could not store byte range mode entry\n");
}
}
done:
if (!br_lck->read_only) {
tdb_chainunlock(tdb, key);
}
SAFE_FREE(br_lck->lock_data);
return 0;
}
static struct byte_range_lock *brl_get_locks_internal(TALLOC_CTX *mem_ctx,
files_struct *fsp, BOOL read_only)
{
TDB_DATA key;
TDB_DATA data;
struct byte_range_lock *br_lck = TALLOC_P(mem_ctx, struct byte_range_lock);
if (br_lck == NULL) {
return NULL;
}
br_lck->fsp = fsp;
br_lck->num_locks = 0;
br_lck->modified = False;
memset(&br_lck->key, '\0', sizeof(struct lock_key));
br_lck->key.device = fsp->dev;
br_lck->key.inode = fsp->inode;
key.dptr = (char *)&br_lck->key;
key.dsize = sizeof(struct lock_key);
if (!fsp->lockdb_clean) {
read_only = False;
}
if (read_only) {
br_lck->read_only = True;
} else {
if (tdb_chainlock(tdb, key) != 0) {
DEBUG(3, ("Could not lock byte range lock entry\n"));
TALLOC_FREE(br_lck);
return NULL;
}
br_lck->read_only = False;
}
talloc_set_destructor(br_lck, byte_range_lock_destructor);
data = tdb_fetch(tdb, key);
br_lck->lock_data = (void *)data.dptr;
br_lck->num_locks = data.dsize / sizeof(struct lock_struct);
if (!fsp->lockdb_clean) {
int orig_num_locks = br_lck->num_locks;
struct lock_struct *locks =
(struct lock_struct *)br_lck->lock_data;
if (!validate_lock_entries(&br_lck->num_locks, &locks)) {
SAFE_FREE(br_lck->lock_data);
TALLOC_FREE(br_lck);
return NULL;
}
if (orig_num_locks != br_lck->num_locks) {
br_lck->modified = True;
}
br_lck->lock_data = locks;
fsp->lockdb_clean = True;
}
if (DEBUGLEVEL >= 10) {
unsigned int i;
struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data;
DEBUG(10,("brl_get_locks_internal: %u current locks on dev=%.0f, inode=%.0f\n",
br_lck->num_locks,
(double)fsp->dev, (double)fsp->inode ));
for( i = 0; i < br_lck->num_locks; i++) {
print_lock_struct(i, &locks[i]);
}
}
return br_lck;
}
struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx,
files_struct *fsp)
{
return brl_get_locks_internal(mem_ctx, fsp, False);
}
struct byte_range_lock *brl_get_locks_readonly(TALLOC_CTX *mem_ctx,
files_struct *fsp)
{
return brl_get_locks_internal(mem_ctx, fsp, True);
}