summaryrefslogtreecommitdiffstats
path: root/source3/locking/locking.c
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2000-04-28 20:54:23 +0000
committerJeremy Allison <jra@samba.org>2000-04-28 20:54:23 +0000
commit9a5eb068ab32418e793d482db1f078f147f9ee08 (patch)
tree9939206f1f8220e16f168cba1f19f18ee25dacc6 /source3/locking/locking.c
parent46e84a0090516e3ae67b3ceb06b3d9a546e8e71e (diff)
downloadsamba-9a5eb068ab32418e793d482db1f078f147f9ee08.tar.gz
samba-9a5eb068ab32418e793d482db1f078f147f9ee08.tar.xz
samba-9a5eb068ab32418e793d482db1f078f147f9ee08.zip
Ok - this is the *third* implementation of this (third time's the charm :-).
This implementation keeps all POSIX lock records in a separate in memory tdb database only known about in locking/posix.c. In addition, the pending close fd's are also held in a tdb which has an array of fd's indexed by device and inode. The walk-split code uglyness has been moved to posix.c from brlock.c, which is the only place that needs to know about it, and the extra functions hacked into brlock to expose internal state have been removed. This implementation passes smbtorture locktest4, the only thing I need to check now for completeness is what to do about lock upgrade/downgrades which Win32 allows under some *very* strange circumstances. Jeremy. (This used to be commit 3f655de1c764b9ee1472a111621d4317f19f624d)
Diffstat (limited to 'source3/locking/locking.c')
-rw-r--r--source3/locking/locking.c641
1 files changed, 13 insertions, 628 deletions
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index fa56e0a628..75198d6303 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -42,190 +42,6 @@ int global_smbpid;
/* the locking database handle */
static TDB_CONTEXT *tdb;
-/*
- * Doubly linked list to hold pending closes needed for
- * POSIX locks. This may be changed to use a hash table (as
- * in lib/hash.c if this is too slow in use.... JRA.
- */
-
-struct pending_closes {
- struct pending_closes *next;
- struct pending_closes *prev;
- SMB_DEV_T dev;
- SMB_INO_T inode;
- int num_posix_locks;
- size_t fd_array_size;
- int *fd_array;
-};
-
-static struct pending_closes *pending_close_list = NULL;
-
-/****************************************************************************
- Find a dev/inode pair in the pending close list.
-****************************************************************************/
-
-static struct pending_closes *find_pending_close_entry(SMB_DEV_T dev, SMB_INO_T inode)
-{
- struct pending_closes *pc;
-
- for(pc = pending_close_list; pc; pc = pc->next) {
- if (dev == pc->dev && inode == pc->inode) {
- DLIST_PROMOTE(pending_close_list,pc);
- return pc;
- }
- }
-
- return NULL;
-}
-
-/****************************************************************************
- Add an fd into the pending close array.
-****************************************************************************/
-
-static BOOL add_fd_to_close_entry(struct pending_closes *pc, int fd)
-{
- if ((pc->fd_array = (int *)Realloc(pc->fd_array, (pc->fd_array_size + 1)*sizeof(int))) == NULL) {
- DEBUG(0,("add_fd_to_close_entry: Unable to increase fd_array !\n"));
- return False;
- }
-
- pc->fd_array[pc->fd_array_size] = fd;
- pc->fd_array_size++;
-
- DEBUG(10,("add_fd_to_close_entry: added fd = %d, size = %u : dev = %.0f, ino = %.0f\n",
- fd, (unsigned int)pc->fd_array_size, (double)pc->dev, (double)pc->inode ));
-
- return True;
-}
-
-/****************************************************************************
- Deal with pending closes needed by POSIX locking support.
-****************************************************************************/
-
-int fd_close_posix(struct connection_struct *conn, files_struct *fsp)
-{
- struct pending_closes *pc;
- int saved_errno = 0;
- int ret;
- size_t i;
-
- if (!lp_posix_locking(SNUM(conn))) {
- ret = conn->vfs_ops.close(fsp->fd);
- fsp->fd = -1;
- return ret;
- }
-
- pc = find_pending_close_entry(fsp->dev, fsp->inode);
-
- if (!pc) {
- /*
- * No other open with a POSIX lock on this dev/inode within this smbd.
- * Just close the fd.
- */
- ret = conn->vfs_ops.close(fsp->fd);
- fsp->fd = -1;
- return ret;
- }
-
- if (pc->num_posix_locks) {
- /*
- * There are outstanding locks on this dev/inode pair.
- * Add our fd to the list and set fsp->fd to -1 to
- * stop the close.
- */
-
- if (!add_fd_to_close_entry(pc, fsp->fd))
- return False;
-
- fsp->fd = -1;
- return 0;
- }
-
- DEBUG(10,("fd_close_posix: doing close on %u fd's.\n", (unsigned int)pc->fd_array_size ));
-
- /*
- * This is the last close. If there are pending fd's close them
- * now. Save the errno just in case.
- */
-
- for(i = 0; i < pc->fd_array_size; i++) {
- if (pc->fd_array[i] != -1) {
- if (conn->vfs_ops.close(pc->fd_array[i]) == -1) {
- saved_errno = errno;
- }
- }
- }
-
- if (pc->fd_array)
- free((char *)pc->fd_array);
-
- DLIST_REMOVE(pending_close_list, pc);
-
- free((char *)pc);
-
- ret = conn->vfs_ops.close(fsp->fd);
-
- if (saved_errno != 0) {
- errno = saved_errno;
- ret = -1;
- }
-
- fsp->fd = -1;
-
- return ret;
-}
-
-/****************************************************************************
- A POSIX lock was granted. Increment the lock list count (create if needed).
-****************************************************************************/
-
-static void increment_posix_lock_list(files_struct *fsp)
-{
- struct pending_closes *pc;
-
- if ((pc = find_pending_close_entry(fsp->dev, fsp->inode)) == NULL) {
- if (!(pc = (struct pending_closes *)malloc(sizeof(struct pending_closes)))) {
- DEBUG(0,("increment_lock_list: malloc fail.\n"));
- return;
- }
- ZERO_STRUCTP(pc);
- pc->dev = fsp->dev;
- pc->inode = fsp->inode;
- DLIST_ADD(pending_close_list, pc);
-
- DEBUG(10,("increment_posix_lock_list: creating entry for file %s: dev = %.0f, ino = %.0f\n",
- fsp->fsp_name, (double)fsp->dev, (double)fsp->inode ));
- }
-
- pc->num_posix_locks++;
-
- DEBUG(10,("increment_posix_lock_list: entry for file %s: dev = %.0f, ino = %.0f, num_locks = %d\n",
- fsp->fsp_name, (double)pc->dev, (double)pc->inode, pc->num_posix_locks ));
-}
-
-
-/****************************************************************************
- A POSIX lock was granted. Decrement the lock list count.
-****************************************************************************/
-
-static void decrement_posix_lock_list(files_struct *fsp)
-{
- struct pending_closes *pc;
-
- pc = find_pending_close_entry(fsp->dev, fsp->inode);
-
- if (pc == NULL) {
- smb_panic("decrement_lock_list: Unlock not found !\n");
- }
-
- pc->num_posix_locks--;
-
- DEBUG(10,("decrement_posix_lock_list: entry for file %s: dev = %.0f, ino = %.0f, num_locks = %d\n",
- fsp->fsp_name, (double)pc->dev, (double)pc->inode, pc->num_posix_locks ));
-
- SMB_ASSERT(pc->num_posix_locks >= 0);
-}
-
/****************************************************************************
Debugging aid :-).
****************************************************************************/
@@ -236,347 +52,6 @@ static const char *lock_type_name(enum brl_type lock_type)
}
/****************************************************************************
- Utility function to map a lock type correctly depending on the open
- mode of a file.
-****************************************************************************/
-
-static int map_posix_lock_type( files_struct *fsp, enum brl_type lock_type)
-{
- if((lock_type == WRITE_LOCK) && !fsp->can_write) {
- /*
- * Many UNIX's cannot get a write lock on a file opened read-only.
- * Win32 locking semantics allow this.
- * Do the best we can and attempt a read-only lock.
- */
- DEBUG(10,("map_posix_lock_type: Downgrading write lock to read due to read-only file.\n"));
- return F_RDLCK;
- } else if((lock_type == READ_LOCK) && !fsp->can_read) {
- /*
- * Ditto for read locks on write only files.
- */
- DEBUG(10,("map_posix_lock_type: Changing read lock to write due to write-only file.\n"));
- return F_WRLCK;
- }
-
- /*
- * This return should be the most normal, as we attempt
- * to always open files read/write.
- */
-
- return (lock_type == READ_LOCK) ? F_RDLCK : F_WRLCK;
-}
-
-/****************************************************************************
- Check to see if the given unsigned lock range is within the possible POSIX
- range. Modifies the given args to be in range if possible, just returns
- False if not.
-****************************************************************************/
-
-static BOOL posix_lock_in_range(SMB_OFF_T *offset_out, SMB_OFF_T *count_out,
- SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
-{
- SMB_OFF_T offset;
- SMB_OFF_T count;
-
-#if defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
-
- SMB_OFF_T mask2 = ((SMB_OFF_T)0x4) << (SMB_OFF_T_BITS-4);
- SMB_OFF_T mask = (mask2<<1);
- SMB_OFF_T neg_mask = ~mask;
-
- /*
- * In this case SMB_OFF_T is 64 bits,
- * and the underlying system can handle 64 bit signed locks.
- * Cast to signed type.
- */
-
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
-
- /*
- * Deal with a very common case of count of all ones.
- * (lock entire file).
- */
-
- if(count == (SMB_OFF_T)-1)
- count &= ~mask;
-
- /*
- * POSIX lock ranges cannot be negative.
- * Fail if any combination becomes negative.
- */
-
- if(offset < 0 || count < 0 || (offset + count < 0)) {
- DEBUG(10,("posix_lock_in_range: negative range: offset = %.0f, count = %.0f. Ignoring lock.\n",
- (double)offset, (double)count ));
- return False;
- }
-
- /*
- * In this case SMB_OFF_T is 64 bits, the offset and count
- * fit within the positive range, and the underlying
- * system can handle 64 bit locks. Just return as the
- * cast values are ok.
- */
-
-#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
-
- /*
- * In this case either SMB_OFF_T is 32 bits,
- * or the underlying system cannot handle 64 bit signed locks.
- * Either way we have to try and mangle to fit within 31 bits.
- * This is difficult.
- */
-
-#if defined(HAVE_BROKEN_FCNTL64_LOCKS)
-
- /*
- * SMB_OFF_T is 64 bits, but we need to use 31 bits due to
- * broken large locking.
- */
-
- /*
- * Deal with a very common case of count of all ones.
- * (lock entire file).
- */
-
- if(u_count == (SMB_BIG_UINT)-1)
- count = 0x7FFFFFFF;
-
- if(((u_offset >> 32) & 0xFFFFFFFF) || ((u_count >> 32) & 0xFFFFFFFF)) {
- DEBUG(10,("posix_lock_in_range: top 32 bits not zero. offset = %.0f, count = %.0f. Ignoring lock.\n",
- (double)u_offset, (double)u_count ));
- /* Top 32 bits of offset or count were not zero. */
- return False;
- }
-
- /* Cast from 64 bits unsigned to 64 bits signed. */
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
-
- /*
- * Check if we are within the 2^31 range.
- */
-
- {
- int32 low_offset = (int32)offset;
- int32 low_count = (int32)count;
-
- if(low_offset < 0 || low_count < 0 || (low_offset + low_count < 0)) {
- DEBUG(10,("posix_lock_in_range: not within 2^31 range. low_offset = %d, low_count = %d. Ignoring lock.\n",
- low_offset, low_count ));
- return False;
- }
- }
-
- /*
- * Ok - we can map from a 64 bit number to a 31 bit lock.
- */
-
-#else /* HAVE_BROKEN_FCNTL64_LOCKS */
-
- /*
- * SMB_OFF_T is 32 bits.
- */
-
-#if defined(HAVE_LONGLONG)
-
- /*
- * SMB_BIG_UINT is 64 bits, we can do a 32 bit shift.
- */
-
- /*
- * Deal with a very common case of count of all ones.
- * (lock entire file).
- */
-
- if(u_count == (SMB_BIG_UINT)-1)
- count = 0x7FFFFFFF;
-
- if(((u_offset >> 32) & 0xFFFFFFFF) || ((u_count >> 32) & 0xFFFFFFFF)) {
- DEBUG(10,("posix_lock_in_range: top 32 bits not zero. u_offset = %.0f, u_count = %.0f. Ignoring lock.\n",
- (double)u_offset, (double)u_count ));
- return False;
- }
-
- /* Cast from 64 bits unsigned to 32 bits signed. */
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
-
- /*
- * Check if we are within the 2^31 range.
- */
-
- if(offset < 0 || count < 0 || (offset + count < 0)) {
- DEBUG(10,("posix_lock_in_range: not within 2^31 range. offset = %d, count = %d. Ignoring lock.\n",
- (int)offset, (int)count ));
- return False;
- }
-
-#else /* HAVE_LONGLONG */
-
- /*
- * SMB_BIG_UINT and SMB_OFF_T are both 32 bits,
- * just cast.
- */
-
- /*
- * Deal with a very common case of count of all ones.
- * (lock entire file).
- */
-
- if(u_count == (SMB_BIG_UINT)-1)
- count = 0x7FFFFFFF;
-
- /* Cast from 32 bits unsigned to 32 bits signed. */
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
-
- /*
- * Check if we are within the 2^31 range.
- */
-
- if(offset < 0 || count < 0 || (offset + count < 0)) {
- DEBUG(10,("posix_lock_in_range: not within 2^31 range. offset = %d, count = %d. Ignoring lock.\n",
- (int)offset, (int)count ));
- return False;
- }
-
-#endif /* HAVE_LONGLONG */
-#endif /* LARGE_SMB_OFF_T */
-#endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
-
- /*
- * The mapping was successful.
- */
-
- DEBUG(10,("posix_lock_in_range: offset_out = %.0f, count_out = %.0f\n",
- (double)offset, (double)count ));
-
- *offset_out = offset;
- *count_out = count;
-
- return True;
-}
-
-/****************************************************************************
- POSIX function to see if a file region is locked. Returns True if the
- region is locked, False otherwise.
-****************************************************************************/
-
-static BOOL is_posix_locked(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
-{
- SMB_OFF_T offset;
- SMB_OFF_T count;
-
- DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n",
- fsp->fsp_name, (double)u_offset, (double)u_count, lock_type_name(lock_type) ));
-
- /*
- * If the requested lock won't fit in the POSIX range, we will
- * never set it, so presume it is not locked.
- */
-
- if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
- return False;
-
- /*
- * Note that most UNIX's can *test* for a write lock on
- * a read-only fd, just not *set* a write lock on a read-only
- * fd. So we don't need to use map_lock_type here.
- */
-
- return fcntl_lock(fsp->fd,SMB_F_GETLK,offset,count,lock_type);
-}
-
-/****************************************************************************
- POSIX function to acquire a lock. Returns True if the
- lock could be granted, False if not.
-****************************************************************************/
-
-static BOOL set_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count, enum brl_type lock_type)
-{
- SMB_OFF_T offset;
- SMB_OFF_T count;
- BOOL ret = True;
-
- DEBUG(5,("set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s\n",
- fsp->fsp_name, (double)u_offset, (double)u_count, lock_type_name(lock_type) ));
-
- /*
- * If the requested lock won't fit in the POSIX range, we will
- * pretend it was successful.
- */
-
- if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) {
- increment_posix_lock_list(fsp);
- return True;
- }
-
- /*
- * Note that setting multiple overlapping read locks on different
- * file descriptors will not be held separately by the kernel (POSIX
- * braindamage), but will be merged into one continuous read lock
- * range. We cope with this case in the release_posix_lock code
- * below. JRA.
- */
-
- ret = fcntl_lock(fsp->fd,SMB_F_SETLK,offset,count,map_posix_lock_type(fsp,lock_type));
-
- if (ret)
- increment_posix_lock_list(fsp);
-
- return ret;
-}
-
-/****************************************************************************
- POSIX function to release a lock given a list. Returns True if the
- lock could be released, False if not.
-****************************************************************************/
-
-static BOOL release_posix_lock(files_struct *fsp, struct unlock_list *ulist)
-{
- BOOL ret = True;
-
- for(; ulist; ulist = ulist->next) {
- SMB_OFF_T offset = ulist->start;
- SMB_OFF_T count = ulist->size;
-
- DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n",
- fsp->fsp_name, (double)offset, (double)count ));
-
- if(count == 0) {
-
- /*
- * This lock must overlap with an existing read-only lock
- * held by another fd. Don't do any POSIX call.
- */
-
- continue;
- }
-
- /*
- * If the requested lock won't fit in the POSIX range, we will
- * pretend it was successful.
- */
-
- if(!posix_lock_in_range(&offset, &count, offset, count))
- continue;
-
- ret = fcntl_lock(fsp->fd,SMB_F_SETLK,offset,count,F_UNLCK);
- }
-
- /*
- * We treat this as one unlock request for POSIX accounting purposes even
- * if it may have been split into multiple smaller POSIX unlock ranges.
- */
-
- decrement_posix_lock_list(fsp);
-
- return ret;
-}
-
-/****************************************************************************
Utility function called to see if a file region is locked.
****************************************************************************/
@@ -675,10 +150,6 @@ BOOL do_unlock(files_struct *fsp,connection_struct *conn,
int *eclass,uint32 *ecode)
{
BOOL ok = False;
- TALLOC_CTX *ul_ctx = NULL;
- struct unlock_list *ulist = NULL;
- struct unlock_list *ul = NULL;
- pid_t pid;
if (!lp_locking(SNUM(conn)))
return(True);
@@ -698,10 +169,8 @@ BOOL do_unlock(files_struct *fsp,connection_struct *conn,
* match then don't bother looking to remove POSIX locks.
*/
- pid = getpid();
-
ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum,
- global_smbpid, pid, conn->cnum, offset, count);
+ global_smbpid, getpid(), conn->cnum, offset, count);
if (!ok) {
DEBUG(10,("do_unlock: returning ERRlock.\n" ));
@@ -713,47 +182,7 @@ BOOL do_unlock(files_struct *fsp,connection_struct *conn,
if (!lp_posix_locking(SNUM(conn)))
return True;
- if ((ul_ctx = talloc_init()) == NULL) {
- DEBUG(0,("do_unlock: unable to init talloc context.\n"));
- return True; /* Not a fatal error. */
- }
-
- if ((ul = (struct unlock_list *)talloc(ul_ctx, sizeof(struct unlock_list))) == NULL) {
- DEBUG(0,("do_unlock: unable to talloc unlock list.\n"));
- talloc_destroy(ul_ctx);
- return True; /* Not a fatal error. */
- }
-
- /*
- * Create the initial list entry containing the
- * lock we want to remove.
- */
-
- ZERO_STRUCTP(ul);
- ul->start = offset;
- ul->size = count;
- ul->smbpid = global_smbpid;
-
- DLIST_ADD(ulist, ul);
-
- /*
- * The following call calculates if there are any
- * overlapping read locks held by this process on
- * other fd's open on the same file and creates a
- * list of unlock ranges that will allow other
- * POSIX lock ranges to remain on the file whilst the
- * unlocks are performed.
- */
-
- ulist = brl_unlock_list(ul_ctx, ulist, pid, fsp->dev, fsp->inode);
-
- /*
- * Release the POSIX locks on the list of ranges returned.
- */
-
- (void)release_posix_lock(fsp, ulist);
-
- talloc_destroy(ul_ctx);
+ (void)release_posix_lock(fsp, offset, count);
return True; /* Did unlock */
}
@@ -769,66 +198,19 @@ void locking_close_file(files_struct *fsp)
if (!lp_locking(SNUM(fsp->conn)))
return;
- if(lp_posix_locking(SNUM(fsp->conn))) {
-
- TALLOC_CTX *ul_ctx = NULL;
- struct unlock_list *ul = NULL;
- int eclass;
- uint32 ecode;
- struct pending_closes *pc;
-
- /*
- * Optimization for the common case where we are the only
- * opener of a file. If all fd entries are our own, we don't
- * need to explicitly release all the locks via the POSIX functions,
- * we can just release all the brl locks, as in the no POSIX locking case.
- */
-
- if ((pc = find_pending_close_entry(fsp->dev, fsp->inode)) != NULL) {
-
- if (pc->fd_array_size == 1 && pc->fd_array[0] == fsp->fd ) {
- /*
- * Just release all the brl locks, no need to release individually.
- */
-
- brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
- return;
- }
- }
-
- if ((ul_ctx = talloc_init()) == NULL) {
- DEBUG(0,("locking_close_file: unable to init talloc context.\n"));
- return;
- }
-
- /*
- * We need to release all POSIX locks we have on this
- * fd. Get all our existing locks from the tdb locking database.
- */
-
- ul = brl_getlocklist(ul_ctx, fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
-
- /*
- * Now unlock all of them. This will remove the brl entry also
- * for each lock. Note we need to make sure the global_smbpid matches
- * the one associated with each lock in case the client plays games
- * with smbpids (like smbtorture does :-).
- */
+ /*
+ * Just release all the brl locks, no need to release individually.
+ */
- for(; ul; ul = ul->next) {
- global_smbpid = ul->smbpid;
- do_unlock(fsp,fsp->conn,ul->size,ul->start,&eclass,&ecode);
- }
-
- talloc_destroy(ul_ctx);
+ brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
- } else {
+ if(lp_posix_locking(SNUM(fsp->conn))) {
- /*
- * Just release all the brl locks, no need to release individually.
+ /*
+ * Release all the POSIX locks.
*/
+ posix_locking_close_file(fsp);
- brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum);
}
}
@@ -851,6 +233,9 @@ BOOL locking_init(int read_only)
return False;
}
+ if (!posix_locking_init())
+ return False;
+
return True;
}