From 22dbd67708f1651a2341d70ce576fac360affccf Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 10 Apr 2006 15:33:04 +0000 Subject: r15018: Merge Volker's ipc/trans2/nttrans changes over into 3.0. Also merge the new POSIX lock code - this is not enabled unless -DDEVELOPER is defined. This doesn't yet map onto underlying system POSIX locks. Updates vfs to allow lock queries. Jeremy. (This used to be commit 08e52ead03304ff04229e1bfe544ff40e2564fc7) --- source3/locking/brlock.c | 1300 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 985 insertions(+), 315 deletions(-) (limited to 'source3/locking/brlock.c') diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 5078515b3e..b95bb895cc 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -52,6 +52,7 @@ struct lock_struct { br_off size; int fnum; enum brl_type lock_type; + enum brl_flavour lock_flav; }; /* The key used in the brlock database. */ @@ -65,6 +66,26 @@ struct lock_key { static TDB_CONTEXT *tdb; +/**************************************************************************** + Debug info at level 10 for lock struct. +****************************************************************************/ + +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) )); +} + /**************************************************************************** Create a locking key - ensuring zero filled for pad purposes. ****************************************************************************/ @@ -86,8 +107,8 @@ static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode) See if two locking contexts are equal. ****************************************************************************/ -static BOOL brl_same_context(struct lock_context *ctx1, - struct lock_context *ctx2) +static 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) && @@ -98,8 +119,8 @@ static BOOL brl_same_context(struct lock_context *ctx1, See if lck1 and lck2 overlap. ****************************************************************************/ -static BOOL brl_overlap(struct lock_struct *lck1, - struct lock_struct *lck2) +static BOOL brl_overlap(const struct lock_struct *lck1, + const struct lock_struct *lck2) { /* this extra check is not redundent - it copes with locks that go beyond the end of 64 bit file space */ @@ -120,12 +141,14 @@ static BOOL brl_overlap(struct lock_struct *lck1, See if lock2 can be added when lock1 is in place. ****************************************************************************/ -static BOOL brl_conflict(struct lock_struct *lck1, - struct lock_struct *lck2) +static BOOL brl_conflict(const struct lock_struct *lck1, + const struct lock_struct *lck2) { + /* Ignore PENDING locks. */ if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) return False; + /* Read locks never conflict. */ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) { return False; } @@ -138,9 +161,42 @@ static BOOL brl_conflict(struct lock_struct *lck1, return brl_overlap(lck1, lck2); } +/**************************************************************************** + See if lock2 can be added when lock1 is in place - when both locks are POSIX + flavour. POSIX locks ignore fnum - they only care about dev/ino which we + know already match. +****************************************************************************/ + +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 + + /* Ignore PENDING locks. */ + if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) + return False; + + /* Read locks never conflict. */ + if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) { + return False; + } + + /* Locks on the same context con't conflict. Ignore fnum. */ + if (brl_same_context(&lck1->context, &lck2->context)) { + return False; + } + + /* One is read, the other write, or the context is different, + do they overlap ? */ + return brl_overlap(lck1, lck2); +} + #if ZERO_ZERO -static BOOL brl_conflict1(struct lock_struct *lck1, - struct lock_struct *lck2) +static BOOL brl_conflict1(const struct lock_struct *lck1, + const struct lock_struct *lck2) { if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) return False; @@ -169,10 +225,11 @@ static BOOL brl_conflict1(struct lock_struct *lck1, /**************************************************************************** Check to see if this lock conflicts, but ignore our own locks on the - same fnum only. + same fnum only. This is the read/write lock check code path. + This is never used in the POSIX lock case. ****************************************************************************/ -static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2) +static BOOL brl_conflict_other(const struct lock_struct *lck1, const struct lock_struct *lck2) { if (lck1->lock_type == PENDING_LOCK || lck2->lock_type == PENDING_LOCK ) return False; @@ -180,6 +237,12 @@ static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) return False; + /* POSIX flavour locks never conflict here - this is only called + in the read/write path. */ + + if (lck1->lock_flav == POSIX_LOCK && lck2->lock_flav == POSIX_LOCK) + return False; + /* * Incoming WRITE locks conflict with existing READ locks even * if the context is the same. JRA. See LOCKTEST7 in smbtorture. @@ -200,7 +263,7 @@ static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck app depends on this ? ****************************************************************************/ -static NTSTATUS brl_lock_failed(struct lock_struct *lock) +static NTSTATUS brl_lock_failed(const struct lock_struct *lock) { static struct lock_struct last_lock_failure; @@ -222,146 +285,432 @@ static NTSTATUS brl_lock_failed(struct lock_struct *lock) return NT_STATUS_LOCK_NOT_GRANTED; } -#if DONT_DO_THIS - /* doing this traversal could kill solaris machines under high load (tridge) */ - /* delete any dead locks */ - /**************************************************************************** - Delete a record if it is for a dead process, if check_self is true, then - delete any records belonging to this pid also (there shouldn't be any). + Open up the brlock.tdb database. ****************************************************************************/ -static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +void brl_init(int read_only) { - struct lock_struct *locks; - int count, i; - BOOL check_self = *(BOOL *)state; - pid_t mypid = sys_getpid(); - - tdb_chainlock(tdb, kbuf); + 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; + } +} - locks = (struct lock_struct *)dbuf.dptr; +/**************************************************************************** + Close down the brlock.tdb database. +****************************************************************************/ - count = dbuf.dsize / sizeof(*locks); - for (i=0; icontext.pid)) { +#if ZERO_ZERO +/**************************************************************************** + Compare two locks for sorting. +****************************************************************************/ - DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n", - (unsigned int)lock->context.pid )); +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 - } else if (process_exists(&lock->context.pid)) { +/**************************************************************************** + Lock a range of bytes - Windows lock semantics. +****************************************************************************/ - DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock->context.pid )); - continue; +static NTSTATUS brl_lock_windows(struct byte_range_lock *br_lck, + const struct lock_struct *plock, + BOOL *my_lock_ctx) +{ + 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++) { + /* 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; + } +#if ZERO_ZERO + if (plock->start == 0 && plock->size == 0 && + locks[i].size == 0) { + break; } +#endif + } - DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n", - (unsigned int)lock->context.pid )); + /* We can get the Windows lock, now see if it needs to + be mapped into a lower level POSIX one, and if so can + we get it ? We tell the lower lock layer about the + lock type so it can cope with the difference between + Windows "stacking" locks and POSIX "flat" ones. */ - if (count > 1 && i < count-1) { - memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); + if ((plock->lock_type != PENDING_LOCK) && lp_posix_locking(SNUM(fsp->conn))) { + if (!set_posix_lock(fsp, plock->start, plock->size, plock->lock_type, WINDOWS_LOCK)) { + if (errno == EACCES || errno == EAGAIN) { + return NT_STATUS_FILE_LOCK_CONFLICT; + } else { + return map_nt_error_from_unix(errno); + } } - count--; - i--; } - if (count == 0) { - tdb_delete(tdb, kbuf); - } else if (count < (dbuf.dsize / sizeof(*locks))) { - dbuf.dsize = count * sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + /* no conflicts - add it to the list of locks */ + locks = (struct lock_struct *)SMB_REALLOC(locks, (br_lck->num_locks + 1) * sizeof(*locks)); + if (!locks) { + return NT_STATUS_NO_MEMORY; } - tdb_chainunlock(tdb, kbuf); - return 0; + 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; } -#endif /**************************************************************************** - Open up the brlock.tdb database. + Cope with POSIX range splits and merges. ****************************************************************************/ -void brl_init(int read_only) +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) { - 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\n")); - return; + BOOL lock_types_differ = (ex->lock_type != plock->lock_type); + + /* We can't merge non-conflicting locks on different context - ignore fnum. */ + + if (!brl_same_context(&ex->context, &plock->context)) { + /* Just copy. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + return 1; } -#if DONT_DO_THIS - /* doing this traversal could kill solaris machines under high load (tridge) */ - /* delete any dead locks */ - if (!read_only) { - BOOL check_self = False; - tdb_traverse(tdb, delete_fn, &check_self); + /* We now know we have the same context. */ + + /* Did we overlap ? */ + +/********************************************* + +---------+ + | ex | + +---------+ + +-------+ + | plock | + +-------+ +OR.... + +---------+ + | ex | + +---------+ +**********************************************/ + + if ( (ex->start >= (plock->start + plock->size)) || + (plock->start >= (ex->start + ex->size))) { + /* No overlap with this lock - copy existing. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + return 1; } -#endif + +/********************************************* + +---------+ + | ex | + +---------+ + +---------------------------+ + | plock | -> replace with plock. + +---------------------------+ +**********************************************/ + + 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; + } + +/********************************************* + +---------------+ + | ex | + +---------------+ + +---------------+ + | plock | + +---------------+ +BECOMES.... + +---------------+-------+ + | plock | ex | - different lock types. + +---------------+-------+ +OR.... + +-----------------------+ + | ex | - same lock type. + +-----------------------+ +**********************************************/ + + if ( (ex->start >= plock->start) && + (ex->start < plock->start + plock->size) && + (ex->start + ex->size > plock->start + plock->size) ) { + + *lock_was_added = True; + + /* If the lock types are the same, we merge, if different, we + add the new lock before the old. */ + + if (lock_types_differ) { + /* Add new. */ + memcpy(&lck_arr[0], plock, sizeof(struct lock_struct)); + memcpy(&lck_arr[1], ex, sizeof(struct lock_struct)); + /* Adjust existing start and size. */ + lck_arr[1].start = plock->start + plock->size; + lck_arr[1].size = (ex->start + ex->size) - (plock->start + plock->size); + return 2; + } else { + /* Merge. */ + memcpy(&lck_arr[0], plock, sizeof(struct lock_struct)); + /* Set new start and size. */ + lck_arr[0].start = plock->start; + lck_arr[0].size = (ex->start + ex->size) - plock->start; + return 1; + } + } + +/********************************************* + +---------------+ + | ex | + +---------------+ + +---------------+ + | plock | + +---------------+ +BECOMES.... + +-------+---------------+ + | ex | plock | - different lock types + +-------+---------------+ + +OR + +-----------------------+ + | ex | - same lock type. + +-----------------------+ + +**********************************************/ + + if ( (ex->start < plock->start) && + (ex->start + ex->size > plock->start) && + (ex->start + ex->size <= plock->start + plock->size) ) { + + *lock_was_added = True; + + /* If the lock types are the same, we merge, if different, we + add the new lock after the old. */ + + if (lock_types_differ) { + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + memcpy(&lck_arr[1], plock, sizeof(struct lock_struct)); + /* Adjust existing size. */ + lck_arr[0].size = plock->start - ex->start; + return 2; + } else { + /* Merge. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + /* Adjust existing size. */ + lck_arr[0].size = (plock->start + plock->size) - ex->start; + return 1; + } + } + +/********************************************* + +---------------------------+ + | ex | + +---------------------------+ + +---------+ + | plock | + +---------+ +BECOMES..... + +-------+---------+---------+ + | ex | plock | ex | - different lock types. + +-------+---------+---------+ +OR + +---------------------------+ + | ex | - same lock type. + +---------------------------+ +**********************************************/ + + if ( (ex->start < plock->start) && (ex->start + ex->size > plock->start + plock->size) ) { + *lock_was_added = True; + + if (lock_types_differ) { + + /* We have to split ex into two locks here. */ + + 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)); + + /* Adjust first existing size. */ + lck_arr[0].size = plock->start - ex->start; + + /* Adjust second existing start and size. */ + lck_arr[2].start = plock->start + plock->size; + lck_arr[2].size = (ex->start + ex->size) - (plock->start + plock->size); + return 3; + } else { + /* Just eat plock. */ + memcpy(&lck_arr[0], ex, sizeof(struct lock_struct)); + return 1; + } + } + + /* Never get here. */ + smb_panic("brlock_posix_split_merge\n"); + /* Notreached. */ + abort(); } /**************************************************************************** - Close down the brlock.tdb database. + Lock a range of bytes - POSIX lock semantics. + We must cope with range splits and merges. ****************************************************************************/ -void brl_shutdown(int read_only) +static NTSTATUS brl_lock_posix(struct byte_range_lock *br_lck, + const struct lock_struct *plock, + BOOL *my_lock_ctx) { - if (!tdb) - return; + unsigned int i, count; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + struct lock_struct *tp; + files_struct *fsp = br_lck->fsp; + BOOL lock_was_added = False; + + /* No zero-zero locks for POSIX. */ + if (plock->start == 0 && plock->size == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* Don't allow 64-bit lock wrap. */ + if (plock->start + plock->size < plock->start || + plock->start + plock->size < plock->size) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* The worst case scenario here is we have to split an + existing POSIX lock range into two, and add our lock, + so we need at most 2 more entries. */ + + 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++) { + 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; + } + /* Just copy the Windows lock into the new array. */ + memcpy(&tp[count], &locks[i], sizeof(struct lock_struct)); + count++; + } else { + /* POSIX conflict semantics are different. */ + if (brl_conflict_posix(&locks[i], plock)) { + /* Can't block ourselves with POSIX locks. */ + /* No games with error messages. */ + SAFE_FREE(tp); + return NT_STATUS_FILE_LOCK_CONFLICT; + } + + /* Work out overlaps. */ + count += brlock_posix_split_merge(&tp[count], &locks[i], plock, &lock_was_added); + } + } + + /* We can get the POSIX lock, now see if it needs to + be mapped into a lower level POSIX one, and if so can + we get it ? We well the lower lock layer about the + lock type so it can cope with the difference between + Windows "stacking" locks and POSIX "flat" ones. */ + +#if 0 + /* FIXME - this call doesn't work correctly yet for POSIX locks... */ -#if DONT_DO_THIS - /* doing this traversal could kill solaris machines under high load (tridge) */ - /* delete any dead locks */ - if (!read_only) { - BOOL check_self = True; - tdb_traverse(tdb, delete_fn, &check_self); + if ((plock->lock_type != PENDING_LOCK) && lp_posix_locking(SNUM(fsp->conn))) { + + + if (!set_posix_lock(fsp, plock->start, plock->size, plock->lock_type, POSIX_LOCK)) { + if (errno == EACCES || errno == EAGAIN) { + SAFE_FREE(tp); + return NT_STATUS_FILE_LOCK_CONFLICT; + } else { + SAFE_FREE(tp); + return map_nt_error_from_unix(errno); + } + } } #endif - tdb_close(tdb); -} + if (!lock_was_added) { + memcpy(&tp[count], plock, sizeof(struct lock_struct)); + count++; + } -#if ZERO_ZERO -/**************************************************************************** -compare two locks for sorting -****************************************************************************/ -static int lock_compare(struct lock_struct *lck1, - 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); + /* Realloc so we don't leak entries per lock call. */ + tp = (struct lock_struct *)SMB_REALLOC(tp, count * sizeof(*locks)); + if (!tp) { + return NT_STATUS_NO_MEMORY; } - return 0; + br_lck->num_locks = count; + br_lck->lock_data = (void *)tp; + br_lck->modified = True; + return NT_STATUS_OK; } -#endif /**************************************************************************** Lock a range of bytes. ****************************************************************************/ -NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, - uint16 smbpid, struct process_id pid, uint16 tid, - br_off start, br_off size, - enum brl_type lock_type, BOOL *my_lock_ctx) +NTSTATUS brl_lock(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_type lock_type, + enum brl_flavour lock_flav, + BOOL *my_lock_ctx) { - TDB_DATA kbuf, dbuf; - int count, i; - struct lock_struct lock, *locks; - NTSTATUS status = NT_STATUS_OK; + NTSTATUS ret; + struct lock_struct lock; *my_lock_ctx = False; - kbuf = locking_key(dev,ino); - - dbuf.dptr = NULL; #if !ZERO_ZERO if (start == 0 && size == 0) { @@ -369,66 +718,27 @@ NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, } #endif - tdb_chainlock(tdb, kbuf); - dbuf = tdb_fetch(tdb, kbuf); - lock.context.smbpid = smbpid; lock.context.pid = pid; - lock.context.tid = tid; + lock.context.tid = br_lck->fsp->conn->cnum; lock.start = start; lock.size = size; - lock.fnum = fnum; + lock.fnum = br_lck->fsp->fnum; lock.lock_type = lock_type; + lock.lock_flav = lock_flav; - if (dbuf.dptr) { - /* there are existing locks - make sure they don't conflict */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); - for (i=0; ilock_data, (size_t)br_lck->num_locks, sizeof(lock), lock_compare); #endif - if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) { - status = NT_STATUS_INTERNAL_DB_CORRUPTION; - goto fail; - } - - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return NT_STATUS_OK; - - fail: - - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return status; + return ret; } /**************************************************************************** @@ -445,260 +755,532 @@ static BOOL brl_pending_overlap(struct lock_struct *lock, struct lock_struct *pe } /**************************************************************************** - Unlock a range of bytes. + Unlock a range of bytes - Windows semantics. ****************************************************************************/ -BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, - uint16 smbpid, struct process_id pid, uint16 tid, - br_off start, br_off size, - BOOL remove_pending_locks_only, - void (*pre_unlock_fn)(void *), - void *pre_unlock_data) +static BOOL brl_unlock_windows(struct byte_range_lock *br_lck, const struct lock_struct *plock) { - TDB_DATA kbuf, dbuf; - int count, i, j; - struct lock_struct *locks; - struct lock_context context; + unsigned int i, j; + struct lock_struct *lock = NULL; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; - kbuf = locking_key(dev,ino); +#if ZERO_ZERO + for (i = 0; i < br_lck->num_locks; i++) { + lock = &locks[i]; - dbuf.dptr = NULL; + 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) { - tdb_chainlock(tdb, kbuf); - dbuf = tdb_fetch(tdb, kbuf); + /* found it - delete it */ + if (i < br_lck->num_locks - 1) { + memmove(&locks[i], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + } - if (!dbuf.dptr) { - DEBUG(10,("brl_unlock: tdb_fetch failed !\n")); - goto fail; + br_lck->num_locks -= 1; + br_lck->modified = True; + return True; + } } +#endif - context.smbpid = smbpid; - context.pid = pid; - context.tid = tid; + for (i = 0; i < br_lck->num_locks; i++) { + lock = &locks[i]; - /* there are existing locks - find a match */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); + /* Only remove our own locks that match in start, size, and flavour. */ + 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 ) { + break; + } + } -#if ZERO_ZERO - for (i=0; inum_locks) { + /* we didn't find it */ + return False; + } - if (lock->lock_type == WRITE_LOCK && - brl_same_context(&lock->context, &context) && - lock->fnum == fnum && - lock->start == start && - lock->size == size) { + /* Unlock any POSIX regions. */ + if(lp_posix_locking(br_lck->fsp->conn->cnum)) { + release_posix_lock(br_lck->fsp, plock->start, plock->size); + } - if (pre_unlock_fn) - (*pre_unlock_fn)(pre_unlock_data); + /* Send unlock messages to any pending waiters that overlap. */ + for (j=0; j < br_lck->num_locks; j++) { + struct lock_struct *pend_lock = &locks[j]; - /* found it - delete it */ - if (count == 1) { - tdb_delete(tdb, kbuf); - } else { - if (i < count-1) { - memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); - } - dbuf.dsize -= sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); - } + /* Ignore non-pending locks. */ + if (pend_lock->lock_type != PENDING_LOCK) { + continue; + } - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return True; + /* We could send specific lock info here... */ + if (brl_pending_overlap(lock, pend_lock)) { + DEBUG(10,("brl_unlock: sending unlock message to pid %s\n", + procid_str_static(&pend_lock->context.pid ))); + + become_root(); + message_send_pid(pend_lock->context.pid, + MSG_SMB_UNLOCK, + NULL, 0, True); + unbecome_root(); } } -#endif - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); - for (i=0; inum_locks - 1) { + memmove(&locks[i], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + } - if (brl_same_context(&lock->context, &context) && - lock->fnum == fnum && - lock->start == start && - lock->size == size) { + br_lck->num_locks -= 1; + br_lck->modified = True; + return True; +} + +/**************************************************************************** + Unlock a range of bytes - POSIX semantics. +****************************************************************************/ - if (remove_pending_locks_only && lock->lock_type != PENDING_LOCK) - continue; +static BOOL brl_unlock_posix(struct byte_range_lock *br_lck, const struct lock_struct *plock) +{ + unsigned int i, j, count; + struct lock_struct *lock = NULL; + struct lock_struct *tp; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + BOOL overlap_found = False; + + /* No zero-zero locks for POSIX. */ + if (plock->start == 0 && plock->size == 0) { + return False; + } - if (lock->lock_type != PENDING_LOCK) { + /* Don't allow 64-bit lock wrap. */ + if (plock->start + plock->size < plock->start || + plock->start + plock->size < plock->size) { + DEBUG(10,("brl_unlock_posix: lock wrap\n")); + return False; + } - /* Do any POSIX unlocks needed. */ - if (pre_unlock_fn) - (*pre_unlock_fn)(pre_unlock_data); + /* The worst case scenario here is we have to split an + existing POSIX lock range into two, so we need at most + 1 more entry. */ - /* Send unlock messages to any pending waiters that overlap. */ - for (j=0; jnum_locks + 1)); + if (!tp) { + DEBUG(10,("brl_unlock_posix: malloc fail\n")); + return False; + } - /* Ignore non-pending locks. */ - if (pend_lock->lock_type != PENDING_LOCK) - continue; + count = 0; + for (i = 0; i < br_lck->num_locks; i++) { + struct lock_struct tmp_lock[3]; + BOOL lock_was_added = False; + unsigned int tmp_count; - /* We could send specific lock info here... */ - if (brl_pending_overlap(lock, pend_lock)) { - DEBUG(10,("brl_unlock: sending unlock message to pid %s\n", - procid_str_static(&pend_lock->context.pid ))); + lock = &locks[i]; - message_send_pid(pend_lock->context.pid, - MSG_SMB_UNLOCK, - NULL, 0, True); - } - } - } + /* Only remove our own locks - ignore fnum. */ + if (lock->lock_type == PENDING_LOCK || + !brl_same_context(&lock->context, &plock->context)) { + memcpy(&tp[count], lock, sizeof(struct lock_struct)); + count++; + continue; + } - /* found it - delete it */ - if (count == 1) { - tdb_delete(tdb, kbuf); + /* Work out overlaps. */ + tmp_count = brlock_posix_split_merge(&tmp_lock[0], &locks[i], plock, &lock_was_added); + + if (tmp_count == 1) { + /* Ether the locks didn't overlap, or the unlock completely + overlapped this lock. If it didn't overlap, then there's + no change in the locks. */ + if (tmp_lock[0].lock_type != UNLOCK_LOCK) { + SMB_ASSERT(tmp_lock[0].lock_type == locks[i].lock_type); + /* No change in this lock. */ + memcpy(&tp[count], &tmp_lock[0], sizeof(struct lock_struct)); + count++; } else { - if (i < count-1) { - memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); - } - dbuf.dsize -= sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + SMB_ASSERT(tmp_lock[0].lock_type == UNLOCK_LOCK); + overlap_found = True; } + continue; + } else if (tmp_count == 2) { + /* The unlock overlapped an existing lock. Copy the truncated + lock into the lock array. */ + 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)); + } 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)); + } + count++; + overlap_found = True; + continue; + } else { + /* tmp_count == 3 - (we split a lock range in two). */ + 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; + /* Optimisation... */ + /* We know we're finished here as we can't overlap any + more POSIX locks. Copy the rest of the lock array. */ + 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; + } + } - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return True; + if (!overlap_found) { + /* Just ignore - no change. */ + SAFE_FREE(tp); + DEBUG(10,("brl_unlock_posix: No overlap - unlocked.\n")); + return True; + } + +#if 0 + /* FIXME - this call doesn't work correctly yet for POSIX locks... */ + + /* Unlock any POSIX regions. */ + if(lp_posix_locking(br_lck->fsp->conn->cnum)) { + release_posix_lock(br_lck->fsp, plock->start, plock->size); + } +#endif + + /* Realloc so we don't leak entries per unlock call. */ + 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 { + /* We deleted the last lock. */ + SAFE_FREE(tp); + tp = NULL; } - /* we didn't find it */ + br_lck->num_locks = count; + br_lck->lock_data = (void *)tp; + br_lck->modified = True; - fail: - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); - return False; -} + /* Send unlock messages to any pending waiters that overlap. */ + locks = tp; + for (j=0; j < br_lck->num_locks; j++) { + struct lock_struct *pend_lock = &locks[j]; + + /* Ignore non-pending locks. */ + if (pend_lock->lock_type != PENDING_LOCK) { + continue; + } + + /* We could send specific lock info here... */ + if (brl_pending_overlap(lock, pend_lock)) { + DEBUG(10,("brl_unlock: sending unlock message to pid %s\n", + procid_str_static(&pend_lock->context.pid ))); + + become_root(); + message_send_pid(pend_lock->context.pid, + MSG_SMB_UNLOCK, + NULL, 0, True); + unbecome_root(); + } + } + + return True; +} /**************************************************************************** - Test if we could add a lock if we wanted to. + Unlock a range of bytes. ****************************************************************************/ -BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum, - uint16 smbpid, struct process_id pid, uint16 tid, - br_off start, br_off size, - enum brl_type lock_type) +BOOL brl_unlock(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_flavour lock_flav) { - TDB_DATA kbuf, dbuf; - int count, i; - struct lock_struct lock, *locks; + struct lock_struct lock; - kbuf = locking_key(dev,ino); + 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); + } +} - dbuf.dptr = NULL; +/**************************************************************************** + Test if we could add a lock if we wanted to. + Returns True if the region required is currently unlocked, False if locked. +****************************************************************************/ - dbuf = tdb_fetch(tdb, kbuf); +BOOL brl_locktest(struct byte_range_lock *br_lck, + uint16 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; + 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 = tid; + lock.context.tid = br_lck->fsp->conn->cnum; lock.start = start; lock.size = size; - lock.fnum = fnum; + lock.fnum = fsp->fnum; lock.lock_type = lock_type; - - if (dbuf.dptr) { - /* there are existing locks - make sure they don't conflict */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); - for (i=0; inum_locks; i++) { + /* + * Our own locks don't conflict. + */ + if (brl_conflict_other(&locks[i], &lock)) { + return False; } } + /* + * There is no lock held by an SMB daemon, check to + * see if there is a POSIX lock from a UNIX or NFS process. + * This only conflicts with Windows locks, not POSIX locks. + */ + + if(lp_posix_locking(fsp->conn->cnum) && (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 )); + + /* We need to return the inverse of is_posix_locked. */ + ret = !ret; + } + /* no conflicts - we could have added it */ - SAFE_FREE(dbuf.dptr); - return True; + return ret; +} - fail: - SAFE_FREE(dbuf.dptr); - return False; +/**************************************************************************** + Query for existing locks. +****************************************************************************/ + +NTSTATUS brl_lockquery(struct byte_range_lock *br_lck, + uint16 *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; + 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; + + /* Make sure existing locks don't conflict */ + for (i=0; i < br_lck->num_locks; i++) { + 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; + } + } + + /* + * There is no lock held by an SMB daemon, check to + * see if there is a POSIX lock from a UNIX or NFS process. + */ + + if(lp_posix_locking(fsp->conn->cnum)) { + 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) { + /* Hmmm. No clue what to set smbpid to - use -1. */ + *psmbpid = 0xFFFF; + return NT_STATUS_LOCK_NOT_GRANTED; + } + } + + return NT_STATUS_OK; } + /**************************************************************************** - Remove any locks associated with a open file. + Remove a particular pending lock. ****************************************************************************/ -void brl_close(SMB_DEV_T dev, SMB_INO_T ino, struct process_id pid, int tid, int fnum) +BOOL brl_remove_pending_lock(struct byte_range_lock *br_lck, + uint16 smbpid, + struct process_id pid, + br_off start, + br_off size, + enum brl_flavour lock_flav) { - TDB_DATA kbuf, dbuf; - int count, i, j, dcount=0; - struct lock_struct *locks; + 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]; + + /* For pending locks we *always* care about the fnum. */ + if (brl_same_context(&lock->context, &context) && + lock->fnum == br_lck->fsp->fnum && + lock->lock_type == PENDING_LOCK && + lock->lock_flav == lock_flav && + lock->start == start && + lock->size == size) { + break; + } + } - kbuf = locking_key(dev,ino); + if (i == br_lck->num_locks) { + /* Didn't find it. */ + return False; + } - dbuf.dptr = NULL; + if (i < br_lck->num_locks - 1) { + /* Found this particular pending lock - delete it */ + memmove(&locks[i], &locks[i+1], + sizeof(*locks)*((br_lck->num_locks-1) - i)); + } - tdb_chainlock(tdb, kbuf); - dbuf = tdb_fetch(tdb, kbuf); + br_lck->num_locks -= 1; + br_lck->modified = True; + return True; +} - if (!dbuf.dptr) goto fail; - /* there are existing locks - remove any for this fnum */ - locks = (struct lock_struct *)dbuf.dptr; - count = dbuf.dsize / sizeof(*locks); +/**************************************************************************** + Remove any locks associated with a open file. +****************************************************************************/ + +void brl_close_fnum(struct byte_range_lock *br_lck, struct process_id pid) +{ + files_struct *fsp = br_lck->fsp; + uint16 tid = fsp->conn->cnum; + int fnum = fsp->fnum; + unsigned int i, j, dcount=0; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + + /* Remove any existing locks for this fnum (or any fnum if they're POSIX). */ - for (i=0; inum_locks; i++) { struct lock_struct *lock = &locks[i]; + BOOL del_this_lock = False; - if (lock->context.tid == tid && - procid_equal(&lock->context.pid, &pid) && - lock->fnum == fnum) { + if (lock->context.tid == tid && procid_equal(&lock->context.pid, &pid)) { + if ((lock->lock_flav == WINDOWS_LOCK) && (lock->fnum == fnum)) { + del_this_lock = True; + } else if (lock->lock_flav == POSIX_LOCK) { + del_this_lock = True; + } + } + if (del_this_lock) { /* Send unlock messages to any pending waiters that overlap. */ - for (j=0; jnum_locks; j++) { struct lock_struct *pend_lock = &locks[j]; /* Ignore our own or non-pending locks. */ - if (pend_lock->lock_type != PENDING_LOCK) + if (pend_lock->lock_type != PENDING_LOCK) { continue; + } + /* Optimisation - don't send to this fnum as we're + closing it. */ if (pend_lock->context.tid == tid && procid_equal(&pend_lock->context.pid, &pid) && - pend_lock->fnum == fnum) + pend_lock->fnum == fnum) { continue; + } /* We could send specific lock info here... */ - if (brl_pending_overlap(lock, pend_lock)) + if (brl_pending_overlap(lock, pend_lock)) { + become_root(); message_send_pid(pend_lock->context.pid, MSG_SMB_UNLOCK, NULL, 0, True); + unbecome_root(); + } } /* found it - delete it */ - if (count > 1 && i < count-1) { + if (br_lck->num_locks > 1 && i < br_lck->num_locks - 1) { memmove(&locks[i], &locks[i+1], - sizeof(*locks)*((count-1) - i)); + sizeof(*locks)*((br_lck->num_locks-1) - i)); } - count--; + br_lck->num_locks--; + br_lck->modified = True; i--; dcount++; } } - - if (count == 0) { - tdb_delete(tdb, kbuf); - } else if (count < (dbuf.dsize / sizeof(*locks))) { - dbuf.dsize -= dcount * sizeof(*locks); - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); - } - - /* we didn't find it */ - fail: - SAFE_FREE(dbuf.dptr); - tdb_chainunlock(tdb, kbuf); } /**************************************************************************** @@ -718,9 +1300,11 @@ static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *st key = (struct lock_key *)kbuf.dptr; for (i=0;idevice, key->inode, + traverse_callback(key->device, + key->inode, locks[i].context.pid, locks[i].lock_type, + locks[i].lock_flav, locks[i].start, locks[i].size); } @@ -733,6 +1317,92 @@ static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *st int brl_forall(BRLOCK_FN(fn)) { - if (!tdb) return 0; + if (!tdb) { + return 0; + } return tdb_traverse(tdb, traverse_fn, (void *)fn); } + +/******************************************************************* + Store a potentially modified set of byte range lock data back into + the database. + Unlock the record. +********************************************************************/ + +static int byte_range_lock_destructor(void *p) +{ + struct byte_range_lock *br_lck = + talloc_get_type_abort(p, struct byte_range_lock); + TDB_DATA key = locking_key(br_lck->fsp->dev, br_lck->fsp->inode); + + if (!br_lck->modified) { + goto done; + } + + if (br_lck->num_locks == 0) { + /* No locks - delete this entry. */ + if (tdb_delete(tdb, key) == -1) { + smb_panic("Could not delete byte range lock entry\n"); + } + } else { + TDB_DATA data; + data.dptr = 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: + + SAFE_FREE(br_lck->lock_data); + tdb_chainunlock(tdb, key); + return 0; +} + +/******************************************************************* + Fetch a set of byte range lock data from the database. + Leave the record locked. +********************************************************************/ + +struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, + files_struct *fsp) +{ + TDB_DATA key = locking_key(fsp->dev, fsp->inode); + TDB_DATA data; + struct byte_range_lock *br_lck; + + 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; + + if (tdb_chainlock(tdb, key) != 0) { + DEBUG(3, ("Could not lock byte range lock entry\n")); + TALLOC_FREE(br_lck); + return NULL; + } + + 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 (DEBUGLEVEL >= 10) { + unsigned int i; + struct lock_struct *locks = (struct lock_struct *)br_lck->lock_data; + DEBUG(10,("brl_get_locks: %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; +} -- cgit