summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/locking/brlock.c275
-rw-r--r--source3/locking/locking.c66
2 files changed, 296 insertions, 45 deletions
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
new file mode 100644
index 0000000000..5d928daa58
--- /dev/null
+++ b/source3/locking/brlock.c
@@ -0,0 +1,275 @@
+/*
+ Unix SMB/Netbios implementation.
+ Version 3.0
+ byte range locking code
+ Copyright (C) Andrew Tridgell 1992-1998
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* this module implements a tdb based byte range locking service,
+ replacing the fcntl() based byte range locking previously
+ used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+
+extern int DEBUGLEVEL;
+
+/* this contains elements that differentiate locks. The smbpid is a
+ client supplied pid, and is essentially the locking context for
+ this client */
+struct lock_context {
+ uint16 smbpid;
+ uint16 tid;
+ pid_t pid;
+};
+
+/* the data in brlock records is an unsorted linear array of these
+ records. It is unnecessary to store the count as tdb provides the
+ size of the record */
+struct lock_struct {
+ struct lock_context context;
+ br_off start;
+ br_off size;
+ enum lock_type lock_type;
+};
+
+/* the key used in the brlock database */
+struct lock_key {
+ SMB_DEV_T device;
+ SMB_INO_T inode;
+};
+
+/* the open brlock.tdb database */
+static TDB_CONTEXT *tdb;
+
+
+/****************************************************************************
+see if two locking contexts are equal
+****************************************************************************/
+static BOOL brl_same_context(struct lock_context *ctx1,
+ struct lock_context *ctx2)
+{
+ return (ctx1->pid == ctx2->pid) &&
+ (ctx1->smbpid == ctx2->smbpid) &&
+ (ctx1->tid == ctx2->tid);
+}
+
+/****************************************************************************
+see if lock2 can be added when lock1 is in place
+****************************************************************************/
+static BOOL brl_conflict(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ 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) return False;
+
+ if (lck1->start >= (lck2->start + lck2->size) ||
+ lck2->start >= (lck1->start + lck1->size)) return False;
+
+ return True;
+}
+
+
+/****************************************************************************
+open up the brlock.tdb database
+****************************************************************************/
+void brl_init(void)
+{
+ if (tdb) return;
+ tdb = tdb_open(lock_path("brlock.tdb"), 0, TDB_CLEAR_IF_FIRST,
+ O_RDWR | O_CREAT, 0644);
+ if (!tdb) {
+ DEBUG(0,("Failed to open byte range locking database\n"));
+ }
+}
+
+
+/****************************************************************************
+lock a range of bytes
+****************************************************************************/
+BOOL brl_lock(SMB_DEV_T dev, SMB_INO_T ino,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ enum lock_type lock_type)
+{
+ struct lock_key key;
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+
+ key.device = dev;
+ key.inode = ino;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+
+ dbuf.dptr = NULL;
+
+ tdb_lockchain(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.pid = pid;
+ lock.context.tid = tid;
+ lock.start = start;
+ lock.size = size;
+ 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; i<count; i++) {
+ if (brl_conflict(&locks[i], &lock)) {
+ goto fail;
+ }
+ }
+ }
+
+ /* no conflicts - add it to the list of locks */
+ dbuf.dptr = Realloc(dbuf.dptr, dbuf.dsize + sizeof(*locks));
+ if (!dbuf.dptr) goto fail;
+ memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock));
+ dbuf.dsize += sizeof(lock);
+ tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+
+ free(dbuf.dptr);
+ tdb_unlockchain(tdb, kbuf);
+ return True;
+
+ fail:
+ if (dbuf.dptr) free(dbuf.dptr);
+ tdb_unlockchain(tdb, kbuf);
+ return False;
+}
+
+
+/****************************************************************************
+unlock a range of bytes
+****************************************************************************/
+BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size)
+{
+ struct lock_key key;
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct *locks;
+ struct lock_context context;
+
+ key.device = dev;
+ key.inode = ino;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+
+ dbuf.dptr = NULL;
+
+ tdb_lockchain(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ if (!dbuf.dptr) goto fail;
+
+ context.smbpid = smbpid;
+ context.pid = pid;
+ context.tid = tid;
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ if (brl_same_context(&locks[i].context, &context) &&
+ locks[i].start == start &&
+ locks[i].size == size) {
+ /* 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);
+ }
+
+ free(dbuf.dptr);
+ tdb_unlockchain(tdb, kbuf);
+ return True;
+ }
+ }
+
+ /* we didn't find it */
+
+ fail:
+ if (dbuf.dptr) free(dbuf.dptr);
+ tdb_unlockchain(tdb, kbuf);
+ return False;
+}
+
+
+
+/****************************************************************************
+test if we could add a lock if we wanted to
+****************************************************************************/
+BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino,
+ uint16 smbpid, pid_t pid, uint16 tid,
+ br_off start, br_off size,
+ enum lock_type lock_type)
+{
+ struct lock_key key;
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+
+ key.device = dev;
+ key.inode = ino;
+ kbuf.dptr = (char *)&key;
+ kbuf.dsize = sizeof(key);
+
+ dbuf.dptr = NULL;
+
+ tdb_lockchain(tdb, kbuf);
+ dbuf = tdb_fetch(tdb, kbuf);
+
+ lock.context.smbpid = smbpid;
+ lock.context.pid = pid;
+ lock.context.tid = tid;
+ lock.start = start;
+ lock.size = size;
+ 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; i<count; i++) {
+ if (brl_conflict(&locks[i], &lock)) {
+ goto fail;
+ }
+ }
+ }
+
+ /* no conflicts - we could have added it */
+ free(dbuf.dptr);
+ tdb_unlockchain(tdb, kbuf);
+ return True;
+
+ fail:
+ if (dbuf.dptr) free(dbuf.dptr);
+ tdb_unlockchain(tdb, kbuf);
+ return False;
+}
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 78d5899322..a0d140bffd 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -39,40 +39,11 @@ extern int DEBUGLEVEL;
static TDB_CONTEXT *tdb;
/****************************************************************************
- Utility function to map a lock type correctly depending on the real open
- mode of a file.
-****************************************************************************/
-static int map_lock_type(files_struct *fsp, int lock_type)
-{
- if((lock_type == F_WRLCK) && (fsp->fd_ptr->real_open_flags == O_RDONLY)) {
- /*
- * 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_lock_type: Downgrading write lock to read due to read-only file.\n"));
- return F_RDLCK;
- } else if( (lock_type == F_RDLCK) && (fsp->fd_ptr->real_open_flags == O_WRONLY)) {
- /*
- * Ditto for read locks on write only files.
- */
- DEBUG(10,("map_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;
-}
-
-/****************************************************************************
Utility function called to see if a file region is locked.
****************************************************************************/
BOOL is_locked(files_struct *fsp,connection_struct *conn,
- SMB_OFF_T count,SMB_OFF_T offset, int lock_type)
+ SMB_OFF_T count,SMB_OFF_T offset,
+ enum lock_type lock_type)
{
int snum = SNUM(conn);
@@ -82,13 +53,9 @@ BOOL is_locked(files_struct *fsp,connection_struct *conn,
if (!lp_locking(snum) || !lp_strict_locking(snum))
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_ptr->fd,SMB_F_GETLK,offset,count,lock_type));
+ return !brl_locktest(fsp->fd_ptr->inode, fsp->fd_ptr->dev,
+ 1, getpid(), conn->cnum,
+ offset, count, lock_type);
}
@@ -96,7 +63,7 @@ BOOL is_locked(files_struct *fsp,connection_struct *conn,
Utility function called by locking requests.
****************************************************************************/
BOOL do_lock(files_struct *fsp,connection_struct *conn,
- SMB_OFF_T count,SMB_OFF_T offset,int lock_type,
+ SMB_OFF_T count,SMB_OFF_T offset,enum lock_type lock_type,
int *eclass,uint32 *ecode)
{
BOOL ok = False;
@@ -113,9 +80,12 @@ BOOL do_lock(files_struct *fsp,connection_struct *conn,
DEBUG(10,("do_lock: lock type %d start=%.0f len=%.0f requested for file %s\n",
lock_type, (double)offset, (double)count, fsp->fsp_name ));
- if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn))
- ok = fcntl_lock(fsp->fd_ptr->fd,SMB_F_SETLK,offset,count,
- map_lock_type(fsp,lock_type));
+ if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) {
+ ok = brl_lock(fsp->fd_ptr->inode, fsp->fd_ptr->dev,
+ 1, getpid(), conn->cnum,
+ offset, count,
+ lock_type);
+ }
if (!ok) {
*eclass = ERRDOS;
@@ -130,7 +100,8 @@ BOOL do_lock(files_struct *fsp,connection_struct *conn,
Utility function called by unlocking requests.
****************************************************************************/
BOOL do_unlock(files_struct *fsp,connection_struct *conn,
- SMB_OFF_T count,SMB_OFF_T offset,int *eclass,uint32 *ecode)
+ SMB_OFF_T count,SMB_OFF_T offset,
+ int *eclass,uint32 *ecode)
{
BOOL ok = False;
@@ -140,8 +111,11 @@ BOOL do_unlock(files_struct *fsp,connection_struct *conn,
DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n",
(double)offset, (double)count, fsp->fsp_name ));
- if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn))
- ok = fcntl_lock(fsp->fd_ptr->fd,SMB_F_SETLK,offset,count,F_UNLCK);
+ if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) {
+ ok = brl_unlock(fsp->fd_ptr->inode, fsp->fd_ptr->dev,
+ 1, getpid(), conn->cnum,
+ offset, count);
+ }
if (!ok) {
*eclass = ERRDOS;
@@ -156,6 +130,8 @@ BOOL do_unlock(files_struct *fsp,connection_struct *conn,
****************************************************************************/
BOOL locking_init(int read_only)
{
+ brl_init();
+
if (tdb) return True;
tdb = tdb_open(lock_path("locking.tdb"),