diff options
-rw-r--r-- | source3/locking/brlock.c | 275 | ||||
-rw-r--r-- | source3/locking/locking.c | 66 |
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"), |