/*
- Unix SMB/Netbios implementation.
- Version 3.0
+ Unix SMB/CIFS implementation.
Locking functions
- Copyright (C) Jeremy Allison 1992-2000
+ Copyright (C) Jeremy Allison 1992-2006
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
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
Revision History:
*/
#include "includes.h"
-extern int DEBUGLEVEL;
-extern int global_smbpid;
-/*
- * The POSIX locking database handle.
- */
-
-static TDB_CONTEXT *posix_lock_tdb;
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
/*
* The pending close database handle.
static TDB_CONTEXT *posix_pending_close_tdb;
-/*
- * The data in POSIX lock 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 posix_lock {
- int fd;
- SMB_OFF_T start;
- SMB_OFF_T size;
- int lock_type;
-};
-
-/*
- * The data in POSIX pending close records is an unsorted linear array of int
- * records. It is unnecessary to store the count as tdb provides the
- * size of the record.
- */
-
-/* The key used in both the POSIX databases. */
-
-struct posix_lock_key {
- SMB_DEV_T device;
- SMB_INO_T inode;
-};
-
-/*******************************************************************
- Form a static locking key for a dev/inode pair.
-******************************************************************/
-
-static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
-{
- static struct posix_lock_key key;
- TDB_DATA kbuf;
- key.device = dev;
- key.inode = inode;
- kbuf.dptr = (char *)&key;
- kbuf.dsize = sizeof(key);
- return kbuf;
-}
-
-/*******************************************************************
- Convenience function to get a key from an fsp.
-******************************************************************/
-
-static TDB_DATA locking_key_fsp(files_struct *fsp)
-{
- return locking_key(fsp->dev, fsp->inode);
-}
+/****************************************************************************
+ First - the functions that deal with the underlying system locks - these
+ functions are used no matter if we're mapping CIFS Windows locks or CIFS
+ POSIX locks onto POSIX.
+****************************************************************************/
/****************************************************************************
- Add an fd to the pending close tdb.
+ Utility function to map a lock type correctly depending on the open
+ mode of a file.
****************************************************************************/
-static BOOL add_fd_to_close_entry(files_struct *fsp)
+static int map_posix_lock_type( files_struct *fsp, enum brl_type lock_type)
{
- TDB_DATA kbuf = locking_key_fsp(fsp);
- TDB_DATA dbuf;
-
- dbuf.dptr = NULL;
-
- dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
-
- dbuf.dptr = Realloc(dbuf.dptr, dbuf.dsize + sizeof(int));
- if (!dbuf.dptr) {
- DEBUG(0,("add_fd_to_close_entry: Realloc fail !\n"));
- return False;
+ 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;
}
- memcpy(dbuf.dptr + dbuf.dsize, &fsp->fd, sizeof(int));
- dbuf.dsize += sizeof(int);
- if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
- DEBUG(0,("add_fd_to_close_entry: tdb_store fail !\n"));
- }
+ /*
+ * This return should be the most normal, as we attempt
+ * to always open files read/write.
+ */
- free(dbuf.dptr);
- return True;
+ return (lock_type == READ_LOCK) ? F_RDLCK : F_WRLCK;
}
/****************************************************************************
- Remove all fd entries for a specific dev/inode pair from the tdb.
+ Debugging aid :-).
****************************************************************************/
-static void delete_close_entries(files_struct *fsp)
+static const char *posix_lock_type_name(int lock_type)
{
- TDB_DATA kbuf = locking_key_fsp(fsp);
-
- if (tdb_delete(posix_pending_close_tdb, kbuf) == -1)
- DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
+ return (lock_type == F_RDLCK) ? "READ" : "WRITE";
}
/****************************************************************************
- Get the array of POSIX pending close records for an open fsp. Caller must
- free. Returns number of entries.
+ 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 size_t get_posix_pending_close_entries(files_struct *fsp, int **entries)
+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)
{
- TDB_DATA kbuf = locking_key_fsp(fsp);
- TDB_DATA dbuf;
- size_t count = 0;
-
- *entries = NULL;
- dbuf.dptr = NULL;
+ SMB_OFF_T offset = (SMB_OFF_T)u_offset;
+ SMB_OFF_T count = (SMB_OFF_T)u_count;
- dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+ /*
+ * For the type of system we are, attempt to
+ * find the maximum positive lock offset as an SMB_OFF_T.
+ */
- if (!dbuf.dptr) {
- return 0;
- }
+#if defined(MAX_POSITIVE_LOCK_OFFSET) /* Some systems have arbitrary limits. */
- *entries = (int *)dbuf.dptr;
- count = (size_t)(dbuf.dsize / sizeof(int));
+ SMB_OFF_T max_positive_lock_offset = (MAX_POSITIVE_LOCK_OFFSET);
- return count;
-}
+#elif defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
-/****************************************************************************
- Get the array of POSIX locks for an fsp. Caller must free. Returns
- number of entries.
-****************************************************************************/
-
-static size_t get_posix_lock_entries(files_struct *fsp, struct posix_lock **entries)
-{
- TDB_DATA kbuf = locking_key_fsp(fsp);
- TDB_DATA dbuf;
- size_t count = 0;
-
- *entries = NULL;
-
- dbuf.dptr = NULL;
+ /*
+ * In this case SMB_OFF_T is 64 bits,
+ * and the underlying system can handle 64 bit signed locks.
+ */
- dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+ SMB_OFF_T mask2 = ((SMB_OFF_T)0x4) << (SMB_OFF_T_BITS-4);
+ SMB_OFF_T mask = (mask2<<1);
+ SMB_OFF_T max_positive_lock_offset = ~mask;
- if (!dbuf.dptr) {
- return 0;
- }
+#else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
- *entries = (struct posix_lock *)dbuf.dptr;
- count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+ /*
+ * In this case either SMB_OFF_T is 32 bits,
+ * or the underlying system cannot handle 64 bit signed locks.
+ * All offsets & counts must be 2^31 or less.
+ */
- return count;
-}
+ SMB_OFF_T max_positive_lock_offset = 0x7FFFFFFF;
-/****************************************************************************
- Deal with pending closes needed by POSIX locking support.
-****************************************************************************/
+#endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
-int fd_close_posix(struct connection_struct *conn, files_struct *fsp)
-{
- int saved_errno = 0;
- int ret;
- size_t count, i;
- struct posix_lock *entries = NULL;
- int *fd_array = NULL;
+ /*
+ * POSIX locks of length zero mean lock to end-of-file.
+ * Win32 locks of length zero are point probes. Ignore
+ * any Win32 locks of length zero. JRA.
+ */
- if (!lp_posix_locking(SNUM(conn))) {
- /*
- * No POSIX to worry about, just close.
- */
- ret = conn->vfs_ops.close(fsp->fd);
- fsp->fd = -1;
- return ret;
+ if (count == (SMB_OFF_T)0) {
+ DEBUG(10,("posix_lock_in_range: count = 0, ignoring.\n"));
+ return False;
}
/*
- * Get the number of outstanding POSIX locks on this dev/inode pair.
+ * If the given offset was > max_positive_lock_offset then we cannot map this at all
+ * ignore this lock.
*/
- count = get_posix_lock_entries(fsp, &entries);
-
- if (count) {
-
- /*
- * There are outstanding locks on this dev/inode pair on other fds.
- * Add our fd to the pending close tdb and set fsp->fd to -1.
- */
-
- if (!add_fd_to_close_entry(fsp)) {
- free((char *)entries);
- return False;
- }
-
- free((char *)entries);
- fsp->fd = -1;
- return 0;
+ if (u_offset & ~((SMB_BIG_UINT)max_positive_lock_offset)) {
+ DEBUG(10,("posix_lock_in_range: (offset = %.0f) offset > %.0f and we cannot handle this. Ignoring lock.\n",
+ (double)u_offset, (double)((SMB_BIG_UINT)max_positive_lock_offset) ));
+ return False;
}
- if(entries)
- free((char *)entries);
-
/*
- * No outstanding POSIX locks. Get the pending close fd's
- * from the tdb and close them all.
+ * We must truncate the count to less than max_positive_lock_offset.
*/
- count = get_posix_pending_close_entries(fsp, &fd_array);
-
- if (count) {
- DEBUG(10,("fd_close_posix: doing close on %u fd's.\n", (unsigned int)count ));
-
- for(i = 0; i < count; i++) {
- if (conn->vfs_ops.close(fd_array[i]) == -1) {
- saved_errno = errno;
- }
- }
+ if (u_count & ~((SMB_BIG_UINT)max_positive_lock_offset)) {
+ count = max_positive_lock_offset;
+ }
- /*
- * Delete all fd's stored in the tdb
- * for this dev/inode pair.
- */
+ /*
+ * Truncate count to end at max lock offset.
+ */
- delete_close_entries(fsp);
+ if (offset + count < 0 || offset + count > max_positive_lock_offset) {
+ count = max_positive_lock_offset - offset;
}
- if (fd_array)
- free((char *)fd_array);
-
/*
- * Finally close the fd associated with this fsp.
+ * If we ate all the count, ignore this lock.
*/
- ret = conn->vfs_ops.close(fsp->fd);
+ if (count == 0) {
+ DEBUG(10,("posix_lock_in_range: Count = 0. Ignoring lock u_offset = %.0f, u_count = %.0f\n",
+ (double)u_offset, (double)u_count ));
+ return False;
+ }
- if (saved_errno != 0) {
- errno = saved_errno;
- ret = -1;
- }
+ /*
+ * The mapping was successful.
+ */
- fsp->fd = -1;
+ DEBUG(10,("posix_lock_in_range: offset_out = %.0f, count_out = %.0f\n",
+ (double)offset, (double)count ));
- return ret;
+ *offset_out = offset;
+ *count_out = count;
+
+ return True;
}
/****************************************************************************
- Debugging aid :-).
+ Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and
+ broken NFS implementations.
****************************************************************************/
-static const char *posix_lock_type_name(int lock_type)
+static bool posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
{
- return (lock_type == F_RDLCK) ? "READ" : "WRITE";
-}
+ bool ret;
-/****************************************************************************
- Add an entry into the POSIX locking tdb. Returns the number of records that
- match the given start and size, or -1 on error.
-****************************************************************************/
+ DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fh->fd,op,(double)offset,(double)count,type));
-static int add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, int lock_type)
-{
- TDB_DATA kbuf = locking_key_fsp(fsp);
- TDB_DATA dbuf;
- struct posix_lock pl;
- struct posix_lock *entries;
- size_t i, count;
- int num_records = 0;
+ ret = SMB_VFS_LOCK(fsp,fsp->fh->fd,op,offset,count,type);
- /*
- * Windows is very strange. It allows read locks to be overlayed on
- * a write lock, but leaves the write lock in force until the first
- * unlock. It also reference counts the locks. This means the following sequence :
- *
- * process1 process2
- * ------------------------------------------------------------------------
- * WRITE LOCK : start = 0, len = 10
- * READ LOCK: start =0, len = 10 - FAIL
- * READ LOCK : start = 0, len = 10
- * READ LOCK: start =0, len = 10 - FAIL
- * UNLOCK : start = 0, len = 10
- * READ LOCK: start =0, len = 10 - OK
- *
- * Under POSIX, the same sequence in steps 1 and 2 would not be reference counted, but
- * would leave a single read lock over the 0-10 region. In order to
- * re-create Windows semantics mapped to POSIX locks, we create multiple TDB
- * entries, one for each overlayed lock request. We are guarenteed by the brlock
- * semantics that if a write lock is added, then it will be first in the array.
- */
-
- dbuf.dptr = NULL;
+ if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno == EINVAL))) {
- dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+ DEBUG(0,("posix_fcntl_lock: WARNING: lock request at offset %.0f, length %.0f returned\n",
+ (double)offset,(double)count));
+ DEBUGADD(0,("an %s error. This can happen when using 64 bit lock offsets\n", strerror(errno)));
+ DEBUGADD(0,("on 32 bit NFS mounted file systems.\n"));
- /*
- * New record.
- */
+ /*
+ * If the offset is > 0x7FFFFFFF then this will cause problems on
+ * 32 bit NFS mounted filesystems. Just ignore it.
+ */
- pl.fd = fsp->fd;
- pl.start = start;
- pl.size = size;
- pl.lock_type = lock_type;
+ if (offset & ~((SMB_OFF_T)0x7fffffff)) {
+ DEBUG(0,("Offset greater than 31 bits. Returning success.\n"));
+ return True;
+ }
- dbuf.dptr = Realloc(dbuf.dptr, dbuf.dsize + sizeof(pl));
- if (!dbuf.dptr) {
- DEBUG(0,("add_posix_lock_entry: Realloc fail !\n"));
- goto fail;
+ if (count & ~((SMB_OFF_T)0x7fffffff)) {
+ /* 32 bit NFS file system, retry with smaller offset */
+ DEBUG(0,("Count greater than 31 bits - retrying with 31 bit truncated length.\n"));
+ errno = 0;
+ count &= 0x7fffffff;
+ ret = SMB_VFS_LOCK(fsp,fsp->fh->fd,op,offset,count,type);
+ }
}
- memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
- dbuf.dsize += sizeof(pl);
+ DEBUG(8,("posix_fcntl_lock: Lock call %s\n", ret ? "successful" : "failed"));
+ return ret;
+}
- count = (size_t)(dbuf.dsize / sizeof(pl));
- entries = (struct posix_lock *)dbuf.dptr;
+/****************************************************************************
+ Actual function that gets POSIX locks. Copes with 64 -> 32 bit cruft and
+ broken NFS implementations.
+****************************************************************************/
- for (i = 0; i < count; i++) {
- struct posix_lock *entry = &entries[i];
+static bool posix_fcntl_getlock(files_struct *fsp, SMB_OFF_T *poffset, SMB_OFF_T *pcount, int *ptype)
+{
+ pid_t pid;
+ bool ret;
- if (fsp->fd == entry->fd &&
- start == entry->start &&
- size == entry->size)
- num_records++;
+ DEBUG(8,("posix_fcntl_getlock %d %.0f %.0f %d\n",
+ fsp->fh->fd,(double)*poffset,(double)*pcount,*ptype));
- }
+ ret = SMB_VFS_GETLOCK(fsp,fsp->fh->fd,poffset,pcount,ptype,&pid);
- if (tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
- DEBUG(0,("add_posix_lock: Failed to add lock entry on file %s\n", fsp->fsp_name));
- goto fail;
- }
+ if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno == EINVAL))) {
+
+ DEBUG(0,("posix_fcntl_getlock: WARNING: lock request at offset %.0f, length %.0f returned\n",
+ (double)*poffset,(double)*pcount));
+ DEBUGADD(0,("an %s error. This can happen when using 64 bit lock offsets\n", strerror(errno)));
+ DEBUGADD(0,("on 32 bit NFS mounted file systems.\n"));
- free(dbuf.dptr);
+ /*
+ * If the offset is > 0x7FFFFFFF then this will cause problems on
+ * 32 bit NFS mounted filesystems. Just ignore it.
+ */
- DEBUG(10,("add_posix_lock: File %s: type = %s: start=%.0f size=%.0f: num_records = %d : dev=%.0f inode=%.0f\n",
- fsp->fsp_name, posix_lock_type_name(lock_type), (double)start, (double)size, num_records,
- (double)fsp->dev, (double)fsp->inode ));
+ if (*poffset & ~((SMB_OFF_T)0x7fffffff)) {
+ DEBUG(0,("Offset greater than 31 bits. Returning success.\n"));
+ return True;
+ }
- return num_records;
+ if (*pcount & ~((SMB_OFF_T)0x7fffffff)) {
+ /* 32 bit NFS file system, retry with smaller offset */
+ DEBUG(0,("Count greater than 31 bits - retrying with 31 bit truncated length.\n"));
+ errno = 0;
+ *pcount &= 0x7fffffff;
+ ret = SMB_VFS_GETLOCK(fsp,fsp->fh->fd,poffset,pcount,ptype,&pid);
+ }
+ }
- fail:
- if (dbuf.dptr)
- free(dbuf.dptr);
- return -1;
+ DEBUG(8,("posix_fcntl_getlock: Lock query call %s\n", ret ? "successful" : "failed"));
+ return ret;
}
/****************************************************************************
- Delete an entry from the POSIX locking tdb. Returns a copy of the entry being
- deleted and the number of remaining matching records, or -1 on error.
+ POSIX function to see if a file region is locked. Returns True if the
+ region is locked, False otherwise.
****************************************************************************/
-static int delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, struct posix_lock *pl)
+bool is_posix_locked(files_struct *fsp,
+ SMB_BIG_UINT *pu_offset,
+ SMB_BIG_UINT *pu_count,
+ enum brl_type *plock_type,
+ enum brl_flavour lock_flav)
{
- TDB_DATA kbuf = locking_key_fsp(fsp);
- TDB_DATA dbuf;
- struct posix_lock *locks;
- size_t i, count;
- int num_records = 0;
-
- dbuf.dptr = NULL;
-
- dbuf = tdb_fetch(posix_lock_tdb, kbuf);
-
- if (!dbuf.dptr) {
- DEBUG(10,("delete_posix_lock_entry: tdb_fetch failed !\n"));
- goto fail;
- }
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ int posix_lock_type = map_posix_lock_type(fsp,*plock_type);
- /* There are existing locks - find a match. */
- locks = (struct posix_lock *)dbuf.dptr;
- count = (size_t)(dbuf.dsize / sizeof(*locks));
+ DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)*pu_offset, (double)*pu_count, posix_lock_type_name(*plock_type) ));
/*
- * Count the number of entries that match this
- * unlock request.
+ * If the requested lock won't fit in the POSIX range, we will
+ * never set it, so presume it is not locked.
*/
- for (i = 0; i < count; i++) {
- struct posix_lock *entry = &locks[i];
-
- if (entry->fd == fsp->fd &&
- entry->start == start &&
- entry->size == size) {
- num_records++;
- }
+ if(!posix_lock_in_range(&offset, &count, *pu_offset, *pu_count)) {
+ return False;
}
- for (i=0; i<count; i++) {
- struct posix_lock *entry = &locks[i];
-
- if (entry->fd == fsp->fd &&
- entry->start == start &&
- entry->size == size) {
-
- num_records--; /* We're deleting one. */
-
- DEBUG(10,("delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, num_records = %d\n",
- posix_lock_type_name(pl->lock_type), (double)pl->start, (double)pl->size,
- (unsigned int)num_records ));
-
- /* Make a copy if requested. */
- if (pl)
- *pl = *entry;
-
- /* Found it - delete it. */
- if (count == 1) {
- tdb_delete(posix_lock_tdb, kbuf);
- } else {
- if (i < count-1) {
- memmove(&locks[i], &locks[i+1], sizeof(*locks)*((count-1) - i));
- }
- dbuf.dsize -= sizeof(*locks);
- tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
- }
-
- free(dbuf.dptr);
- return num_records;
- }
+ if (!posix_fcntl_getlock(fsp,&offset,&count,&posix_lock_type)) {
+ return False;
}
- /* We didn't find it. */
+ if (posix_lock_type == F_UNLCK) {
+ return False;
+ }
- fail:
- if (dbuf.dptr)
- free(dbuf.dptr);
- return -1;
+ if (lock_flav == POSIX_LOCK) {
+ /* Only POSIX lock queries need to know the details. */
+ *pu_offset = (SMB_BIG_UINT)offset;
+ *pu_count = (SMB_BIG_UINT)count;
+ *plock_type = (posix_lock_type == F_RDLCK) ? READ_LOCK : WRITE_LOCK;
+ }
+ return True;
}
/****************************************************************************
- Utility function to map a lock type correctly depending on the open
- mode of a file.
+ Next - the functions that deal with in memory database storing representations
+ of either Windows CIFS locks or POSIX CIFS locks.
****************************************************************************/
-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;
- }
+/* The key used in the in-memory POSIX databases. */
- /*
- * This return should be the most normal, as we attempt
- * to always open files read/write.
- */
+struct lock_ref_count_key {
+ struct file_id id;
+ char r;
+};
+
+/*******************************************************************
+ Form a static locking key for a dev/inode pair for the fd array.
+******************************************************************/
- return (lock_type == READ_LOCK) ? F_RDLCK : F_WRLCK;
+static TDB_DATA fd_array_key(struct file_id id)
+{
+ static struct file_id key;
+ TDB_DATA kbuf;
+ key = id;
+ kbuf.dptr = (uint8 *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
}
-/****************************************************************************
- 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.
-****************************************************************************/
+/*******************************************************************
+ Form a static locking key for a dev/inode pair for the lock ref count
+******************************************************************/
-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)
+static TDB_DATA locking_ref_count_key(struct file_id id)
{
- SMB_OFF_T offset;
- SMB_OFF_T count;
-
-#if defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
+ static struct lock_ref_count_key key;
+ TDB_DATA kbuf;
- 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;
+ memset(&key, '\0', sizeof(key));
+ key.id = id;
+ key.r = 'r';
+ kbuf.dptr = (uint8 *)&key;
+ kbuf.dsize = sizeof(key);
+ return kbuf;
+}
- /*
- * In this case SMB_OFF_T is 64 bits,
- * and the underlying system can handle 64 bit signed locks.
- * Cast to signed type.
- */
+/*******************************************************************
+ Convenience function to get an fd_array key from an fsp.
+******************************************************************/
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
+static TDB_DATA fd_array_key_fsp(files_struct *fsp)
+{
+ return fd_array_key(fsp->file_id);
+}
- /*
- * Deal with a very common case of count of all ones.
- * (lock entire file).
- */
+/*******************************************************************
+ Convenience function to get a lock ref count key from an fsp.
+******************************************************************/
- if(count == (SMB_OFF_T)-1)
- count &= ~mask;
+static TDB_DATA locking_ref_count_key_fsp(files_struct *fsp)
+{
+ return locking_ref_count_key(fsp->file_id);
+}
- /*
- * POSIX lock ranges cannot be negative.
- * Fail if any combination becomes negative.
- */
+/*******************************************************************
+ Create the in-memory POSIX lock databases.
+********************************************************************/
- 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 ));
+bool posix_locking_init(int read_only)
+{
+ if (posix_pending_close_tdb) {
+ return True;
+ }
+
+ if (!posix_pending_close_tdb) {
+ posix_pending_close_tdb = tdb_open_log(NULL, 0, TDB_INTERNAL,
+ read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
+ }
+ if (!posix_pending_close_tdb) {
+ DEBUG(0,("Failed to open POSIX pending close database.\n"));
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).
- */
+ return True;
+}
- if(u_count == (SMB_BIG_UINT)-1)
- count = 0x7FFFFFFF;
+/*******************************************************************
+ Delete the in-memory POSIX lock databases.
+********************************************************************/
- 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. */
+bool posix_locking_end(void)
+{
+ if (posix_pending_close_tdb && tdb_close(posix_pending_close_tdb) != 0) {
return False;
}
+ return True;
+}
- /* Cast from 64 bits unsigned to 64 bits signed. */
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
+/****************************************************************************
+ Next - the functions that deal with storing fd's that have outstanding
+ POSIX locks when closed.
+****************************************************************************/
- /*
- * Check if we are within the 2^31 range.
- */
+/****************************************************************************
+ The records in posix_pending_close_tdb are composed of an array of ints
+ keyed by dev/ino pair.
+ The first int is a reference count of the number of outstanding locks on
+ all open fd's on this dev/ino pair. Any subsequent ints are the fd's that
+ were open on this dev/ino pair that should have been closed, but can't as
+ the lock ref count is non zero.
+****************************************************************************/
- {
- int32 low_offset = (int32)offset;
- int32 low_count = (int32)count;
+/****************************************************************************
+ Keep a reference count of the number of Windows locks open on this dev/ino
+ pair. Creates entry if it doesn't exist.
+****************************************************************************/
- 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;
+static void increment_windows_lock_ref_count(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_ref_count_key_fsp(fsp);
+ TDB_DATA dbuf;
+ int lock_ref_count;
+
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+ if (dbuf.dptr == NULL) {
+ dbuf.dptr = (uint8 *)SMB_MALLOC_P(int);
+ if (!dbuf.dptr) {
+ smb_panic("increment_windows_lock_ref_count: malloc fail");
}
+ memset(dbuf.dptr, '\0', sizeof(int));
+ dbuf.dsize = sizeof(int);
}
- /*
- * Ok - we can map from a 64 bit number to a 31 bit lock.
- */
-
-#else /* HAVE_BROKEN_FCNTL64_LOCKS */
+ memcpy(&lock_ref_count, dbuf.dptr, sizeof(int));
+ lock_ref_count++;
+ memcpy(dbuf.dptr, &lock_ref_count, sizeof(int));
+
+ if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ smb_panic("increment_windows_lock_ref_count: tdb_store_fail");
+ }
+ SAFE_FREE(dbuf.dptr);
- /*
- * SMB_OFF_T is 32 bits.
- */
+ DEBUG(10,("increment_windows_lock_ref_count for file now %s = %d\n",
+ fsp->fsp_name, lock_ref_count ));
+}
-#if defined(HAVE_LONGLONG)
+static void decrement_windows_lock_ref_count(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_ref_count_key_fsp(fsp);
+ TDB_DATA dbuf;
+ int lock_ref_count;
- /*
- * SMB_BIG_UINT is 64 bits, we can do a 32 bit shift.
- */
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+ if (!dbuf.dptr) {
+ smb_panic("decrement_windows_lock_ref_count: logic error");
+ }
- /*
- * Deal with a very common case of count of all ones.
- * (lock entire file).
- */
+ memcpy(&lock_ref_count, dbuf.dptr, sizeof(int));
+ lock_ref_count--;
+ memcpy(dbuf.dptr, &lock_ref_count, sizeof(int));
- if(u_count == (SMB_BIG_UINT)-1)
- count = 0x7FFFFFFF;
+ if (lock_ref_count < 0) {
+ smb_panic("decrement_windows_lock_ref_count: lock_count logic error");
+ }
- 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;
+ if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ smb_panic("decrement_windows_lock_ref_count: tdb_store_fail");
}
+ SAFE_FREE(dbuf.dptr);
+
+ DEBUG(10,("decrement_windows_lock_ref_count for file now %s = %d\n",
+ fsp->fsp_name, lock_ref_count ));
+}
- /* Cast from 64 bits unsigned to 32 bits signed. */
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
+/****************************************************************************
+ Bulk delete - subtract as many locks as we've just deleted.
+****************************************************************************/
- /*
- * Check if we are within the 2^31 range.
- */
+void reduce_windows_lock_ref_count(files_struct *fsp, unsigned int dcount)
+{
+ TDB_DATA kbuf = locking_ref_count_key_fsp(fsp);
+ TDB_DATA dbuf;
+ int lock_ref_count;
- 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;
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+ if (!dbuf.dptr) {
+ return;
}
-#else /* HAVE_LONGLONG */
+ memcpy(&lock_ref_count, dbuf.dptr, sizeof(int));
+ lock_ref_count -= dcount;
- /*
- * 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 (lock_ref_count < 0) {
+ smb_panic("reduce_windows_lock_ref_count: lock_count logic error");
+ }
+ memcpy(dbuf.dptr, &lock_ref_count, sizeof(int));
+
+ if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ smb_panic("reduce_windows_lock_ref_count: tdb_store_fail");
+ }
+ SAFE_FREE(dbuf.dptr);
- if(u_count == (SMB_BIG_UINT)-1)
- count = 0x7FFFFFFF;
+ DEBUG(10,("reduce_windows_lock_ref_count for file now %s = %d\n",
+ fsp->fsp_name, lock_ref_count ));
+}
- /* Cast from 32 bits unsigned to 32 bits signed. */
- offset = (SMB_OFF_T)u_offset;
- count = (SMB_OFF_T)u_count;
+/****************************************************************************
+ Fetch the lock ref count.
+****************************************************************************/
- /*
- * Check if we are within the 2^31 range.
- */
+static int get_windows_lock_ref_count(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_ref_count_key_fsp(fsp);
+ TDB_DATA dbuf;
+ int lock_ref_count;
- 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;
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
+ if (!dbuf.dptr) {
+ lock_ref_count = 0;
+ } else {
+ memcpy(&lock_ref_count, dbuf.dptr, sizeof(int));
}
+ SAFE_FREE(dbuf.dptr);
-#endif /* HAVE_LONGLONG */
-#endif /* LARGE_SMB_OFF_T */
-#endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
+ DEBUG(10,("get_windows_lock_count for file %s = %d\n",
+ fsp->fsp_name, lock_ref_count ));
+ return lock_ref_count;
+}
- /*
- * The mapping was successful.
- */
+/****************************************************************************
+ Delete a lock_ref_count entry.
+****************************************************************************/
- DEBUG(10,("posix_lock_in_range: offset_out = %.0f, count_out = %.0f\n",
- (double)offset, (double)count ));
+static void delete_windows_lock_ref_count(files_struct *fsp)
+{
+ TDB_DATA kbuf = locking_ref_count_key_fsp(fsp);
- *offset_out = offset;
- *count_out = count;
-
- return True;
+ /* Not a bug if it doesn't exist - no locks were ever granted. */
+ tdb_delete(posix_pending_close_tdb, kbuf);
+ DEBUG(10,("delete_windows_lock_ref_count for file %s\n", fsp->fsp_name));
}
-#if defined(LARGE_SMB_OFF_T)
/****************************************************************************
- Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-).
+ Add an fd to the pending close tdb.
****************************************************************************/
-static uint32 map_lock_offset(uint32 high, uint32 low)
+static void add_fd_to_close_entry(files_struct *fsp)
{
- unsigned int i;
- uint32 mask = 0;
- uint32 highcopy = high;
+ TDB_DATA kbuf = fd_array_key_fsp(fsp);
+ TDB_DATA dbuf;
- /*
- * Try and find out how many significant bits there are in high.
- */
+ dbuf.dptr = NULL;
+ dbuf.dsize = 0;
- for(i = 0; highcopy; i++)
- highcopy >>= 1;
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
- /*
- * We use 31 bits not 32 here as POSIX
- * lock offsets may not be negative.
- */
+ dbuf.dptr = (uint8 *)SMB_REALLOC(dbuf.dptr, dbuf.dsize + sizeof(int));
+ if (!dbuf.dptr) {
+ smb_panic("add_fd_to_close_entry: SMB_REALLOC failed");
+ }
- mask = (~0) << (31 - i);
+ memcpy(dbuf.dptr + dbuf.dsize, &fsp->fh->fd, sizeof(int));
+ dbuf.dsize += sizeof(int);
- if(low & mask)
- return 0; /* Fail. */
+ if (tdb_store(posix_pending_close_tdb, kbuf, dbuf, TDB_REPLACE) == -1) {
+ smb_panic("add_fd_to_close_entry: tdb_store_fail");
+ }
- high <<= (31 - i);
+ DEBUG(10,("add_fd_to_close_entry: added fd %d file %s\n",
+ fsp->fh->fd, fsp->fsp_name ));
- return (high|low);
+ SAFE_FREE(dbuf.dptr);
}
-#endif
/****************************************************************************
- Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and
- broken NFS implementations.
+ Remove all fd entries for a specific dev/inode pair from the tdb.
****************************************************************************/
-static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+static void delete_close_entries(files_struct *fsp)
{
- int ret;
- struct connection_struct *conn = fsp->conn;
+ TDB_DATA kbuf = fd_array_key_fsp(fsp);
-#if defined(LARGE_SMB_OFF_T)
- /*
- * In the 64 bit locking case we store the original
- * values in case we have to map to a 32 bit lock on
- * a filesystem that doesn't support 64 bit locks.
- */
- SMB_OFF_T orig_offset = offset;
- SMB_OFF_T orig_count = count;
-#endif /* LARGE_SMB_OFF_T */
+ if (tdb_delete(posix_pending_close_tdb, kbuf) == -1) {
+ smb_panic("delete_close_entries: tdb_delete failed");
+ }
+}
+
+/****************************************************************************
+ Get the array of POSIX pending close records for an open fsp. Caller must
+ free. Returns number of entries.
+****************************************************************************/
+
+static size_t get_posix_pending_close_entries(files_struct *fsp, int **entries)
+{
+ TDB_DATA kbuf = fd_array_key_fsp(fsp);
+ TDB_DATA dbuf;
+ size_t count = 0;
- DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fd,op,(double)offset,(double)count,type));
+ *entries = NULL;
+ dbuf.dptr = NULL;
- ret = conn->vfs_ops.lock(fsp->fd,op,offset,count,type);
+ dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
- if (!ret && (errno == EFBIG)) {
- if( DEBUGLVL( 0 )) {
- dbgtext("posix_fcntl_lock: WARNING: lock request at offset %.0f, length %.0f returned\n", (double)offset,(double)count);
- dbgtext("a 'file too large' error. This can happen when using 64 bit lock offsets\n");
- dbgtext("on 32 bit NFS mounted file systems. Retrying with 32 bit truncated length.\n");
- }
- /* 32 bit NFS file system, retry with smaller offset */
- errno = 0;
- count &= 0x7fffffff;
- ret = conn->vfs_ops.lock(fsp->fd,op,offset,count,type);
+ if (!dbuf.dptr) {
+ return 0;
}
- /* A lock query - just return. */
- if (op == SMB_F_GETLK)
- return ret;
+ *entries = (int *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(int));
- /* A lock set or unset. */
- if (!ret) {
- DEBUG(3,("posix_fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n",
- (double)offset,(double)count,op,type,strerror(errno)));
+ return count;
+}
- /* Perhaps it doesn't support this sort of locking ? */
- if (errno == EINVAL) {
-#if defined(LARGE_SMB_OFF_T)
- {
- /*
- * Ok - if we get here then we have a 64 bit lock request
- * that has returned EINVAL. Try and map to 31 bits for offset
- * and length and try again. This may happen if a filesystem
- * doesn't support 64 bit offsets (efs/ufs) although the underlying
- * OS does.
- */
- uint32 off_low = (orig_offset & 0xFFFFFFFF);
- uint32 off_high = ((orig_offset >> 32) & 0xFFFFFFFF);
-
- count = (orig_count & 0x7FFFFFFF);
- offset = (SMB_OFF_T)map_lock_offset(off_high, off_low);
- ret = conn->vfs_ops.lock(fsp->fd,op,offset,count,type);
- if (!ret) {
- if (errno == EINVAL) {
- DEBUG(3,("posix_fcntl_lock: locking not supported? returning True\n"));
- return(True);
- }
- return False;
- }
- DEBUG(3,("posix_fcntl_lock: 64 -> 32 bit modified lock call successful\n"));
- return True;
- }
-#else /* LARGE_SMB_OFF_T */
- DEBUG(3,("locking not supported? returning True\n"));
- return(True);
-#endif /* LARGE_SMB_OFF_T */
+/****************************************************************************
+ Deal with pending closes needed by POSIX locking support.
+ Note that posix_locking_close_file() is expected to have been called
+ to delete all locks on this fsp before this function is called.
+****************************************************************************/
+
+NTSTATUS fd_close_posix(struct connection_struct *conn, files_struct *fsp)
+{
+ int saved_errno = 0;
+ int ret;
+ int *fd_array = NULL;
+ size_t count, i;
+
+ if (!lp_locking(fsp->conn->params) || !lp_posix_locking(conn->params)) {
+ /*
+ * No locking or POSIX to worry about or we want POSIX semantics
+ * which will lose all locks on all fd's open on this dev/inode,
+ * just close.
+ */
+ ret = SMB_VFS_CLOSE(fsp,fsp->fh->fd);
+ fsp->fh->fd = -1;
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
}
-
- return(False);
+ return NT_STATUS_OK;
}
- DEBUG(8,("posix_fcntl_lock: Lock call successful\n"));
-
- return(True);
-}
-
-/****************************************************************************
- POSIX function to see if a file region is locked. Returns True if the
- region is locked, False otherwise.
-****************************************************************************/
+ if (get_windows_lock_ref_count(fsp)) {
-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;
- int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+ /*
+ * There are outstanding locks on this dev/inode pair on other fds.
+ * Add our fd to the pending close tdb and set fsp->fh->fd to -1.
+ */
- DEBUG(10,("is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s\n",
- fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+ add_fd_to_close_entry(fsp);
+ fsp->fh->fd = -1;
+ return NT_STATUS_OK;
+ }
/*
- * If the requested lock won't fit in the POSIX range, we will
- * never set it, so presume it is not locked.
+ * No outstanding locks. Get the pending close fd's
+ * from the tdb and close them all.
*/
- 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.
- */
+ count = get_posix_pending_close_entries(fsp, &fd_array);
- return posix_fcntl_lock(fsp,SMB_F_GETLK,offset,count,posix_lock_type);
-}
+ if (count) {
+ DEBUG(10,("fd_close_posix: doing close on %u fd's.\n", (unsigned int)count ));
-/****************************************************************************
- POSIX function to acquire a lock. Returns True if the
- lock could be granted, False if not.
-****************************************************************************/
+ for(i = 0; i < count; i++) {
+ if (SMB_VFS_CLOSE(fsp,fd_array[i]) == -1) {
+ saved_errno = errno;
+ }
+ }
-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;
- int posix_lock_type = map_posix_lock_type(fsp,lock_type);
- int ref_count;
+ /*
+ * Delete all fd's stored in the tdb
+ * for this dev/inode pair.
+ */
- DEBUG(5,("set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s\n",
- fsp->fsp_name, (double)u_offset, (double)u_count, posix_lock_type_name(lock_type) ));
+ delete_close_entries(fsp);
+ }
- /*
- * If the requested lock won't fit in the POSIX range, we will
- * pretend it was successful.
- */
+ SAFE_FREE(fd_array);
- if(!posix_lock_in_range(&offset, &count, u_offset, u_count))
- return True;
+ /* Don't need a lock ref count on this dev/ino anymore. */
+ delete_windows_lock_ref_count(fsp);
/*
- * Note that setting multiple overlapping locks on different
- * file descriptors will not be held separately by the kernel (POSIX
- * braindamage), but will be merged into one continuous lock
- * range. We cope with this case in the release_posix_lock code
- * below. We need to add the posix lock entry into the tdb before
- * doing the real posix lock call to deal with the locking overlay
- * case described above in add_posix_lock_entry().
+ * Finally close the fd associated with this fsp.
*/
- ref_count = add_posix_lock_entry(fsp,offset,count,posix_lock_type);
+ ret = SMB_VFS_CLOSE(fsp,fsp->fh->fd);
- if (ref_count == 1) {
- /*
- * First lock entry created. Do a real POSIX lock.
- */
- ret = posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type);
+ if (ret == 0 && saved_errno != 0) {
+ errno = saved_errno;
+ ret = -1;
+ }
- /*
- * Oops, POSIX lock failed, delete the tdb entry.
- */
- if (!ret)
- delete_posix_lock_entry(fsp,offset,count,NULL);
+ fsp->fh->fd = -1;
+
+ if (ret == -1) {
+ return map_nt_error_from_unix(errno);
}
- return ret;
+ return NT_STATUS_OK;
}
+/****************************************************************************
+ Next - the functions that deal with the mapping CIFS Windows locks onto
+ the underlying system POSIX locks.
+****************************************************************************/
+
/*
* Structure used when splitting a lock range
* into a POSIX lock range. Doubly linked list.
*/
-struct unlock_list {
- struct unlock_list *next;
- struct unlock_list *prev;
- SMB_OFF_T start;
- SMB_OFF_T size;
+struct lock_list {
+ struct lock_list *next;
+ struct lock_list *prev;
+ SMB_OFF_T start;
+ SMB_OFF_T size;
};
/****************************************************************************
Create a list of lock ranges that don't overlap a given range. Used in calculating
- POSIX lock unlocks. This is a difficult function that requires ASCII art to
+ POSIX locks and unlocks. This is a difficult function that requires ASCII art to
understand it :-).
****************************************************************************/
-static struct unlock_list *posix_unlock_list(TALLOC_CTX *ctx, struct unlock_list *ulhead, files_struct *fsp)
+static struct lock_list *posix_lock_list(TALLOC_CTX *ctx,
+ struct lock_list *lhead,
+ const struct lock_context *lock_ctx, /* Lock context lhead belongs to. */
+ files_struct *fsp,
+ const struct lock_struct *plocks,
+ int num_locks)
{
- TDB_DATA kbuf = locking_key_fsp(fsp);
- TDB_DATA dbuf;
- struct posix_lock *locks;
- size_t num_locks, i;
-
- dbuf.dptr = NULL;
-
- dbuf = tdb_fetch(posix_lock_tdb, kbuf);
-
- if (!dbuf.dptr) {
- return ulhead;
- }
-
- locks = (struct posix_lock *)dbuf.dptr;
- num_locks = (size_t)(dbuf.dsize / sizeof(*locks));
+ int i;
/*
* Check the current lock list on this dev/inode pair.
* Quit if the list is deleted.
*/
- DEBUG(10,("posix_unlock_list: curr: start=%.0f,size=%.0f\n",
- (double)ulhead->start, (double)ulhead->size ));
+ DEBUG(10,("posix_lock_list: curr: start=%.0f,size=%.0f\n",
+ (double)lhead->start, (double)lhead->size ));
- for (i=0; i<num_locks && ulhead; i++) {
+ for (i=0; i<num_locks && lhead; i++) {
+ const struct lock_struct *lock = &plocks[i];
+ struct lock_list *l_curr;
+
+ /* Ignore all but read/write locks. */
+ if (lock->lock_type != READ_LOCK && lock->lock_type != WRITE_LOCK) {
+ continue;
+ }
- struct posix_lock *lock = &locks[i];
- struct unlock_list *ul_curr;
+ /* Ignore locks not owned by this process. */
+ if (!procid_equal(&lock->context.pid, &lock_ctx->pid)) {
+ continue;
+ }
/*
- * Walk the unlock list, checking for overlaps. Note that
- * the unlock list can expand within this loop if the current
+ * Walk the lock list, checking for overlaps. Note that
+ * the lock list can expand within this loop if the current
* range being examined needs to be split.
*/
- for (ul_curr = ulhead; ul_curr;) {
+ for (l_curr = lhead; l_curr;) {
- DEBUG(10,("posix_unlock_list: lock: start=%.0f,size=%.0f:",
- (double)lock->start, (double)lock->size ));
+ DEBUG(10,("posix_lock_list: lock: fnum=%d: start=%.0f,size=%.0f:type=%s", lock->fnum,
+ (double)lock->start, (double)lock->size, posix_lock_type_name(lock->lock_type) ));
- if ( (ul_curr->start >= (lock->start + lock->size)) ||
- (lock->start > (ul_curr->start + ul_curr->size))) {
+ if ( (l_curr->start >= (lock->start + lock->size)) ||
+ (lock->start >= (l_curr->start + l_curr->size))) {
- /* No overlap with this lock - leave this range alone. */
+ /* No overlap with existing lock - leave this range alone. */
/*********************************************
+---------+
- | ul_curr |
+ | l_curr |
+---------+
+-------+
| lock |
+-------+
OR....
+---------+
- | ul_curr |
+ | l_curr |
+---------+
**********************************************/
- DEBUG(10,("no overlap case.\n" ));
+ DEBUG(10,(" no overlap case.\n" ));
- ul_curr = ul_curr->next;
+ l_curr = l_curr->next;
- } else if ( (ul_curr->start >= lock->start) &&
- (ul_curr->start + ul_curr->size <= lock->start + lock->size) ) {
+ } else if ( (l_curr->start >= lock->start) &&
+ (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
/*
- * This unlock is completely overlapped by this existing lock range
- * and thus should have no effect (not be unlocked). Delete it from the list.
+ * This range is completely overlapped by this existing lock range
+ * and thus should have no effect. Delete it from the list.
*/
/*********************************************
+---------+
- | ul_curr |
+ | l_curr |
+---------+
+---------------------------+
| lock |
+---------------------------+
**********************************************/
/* Save the next pointer */
- struct unlock_list *ul_next = ul_curr->next;
+ struct lock_list *ul_next = l_curr->next;
- DEBUG(10,("delete case.\n" ));
+ DEBUG(10,(" delete case.\n" ));
- DLIST_REMOVE(ulhead, ul_curr);
- if(ulhead == NULL)
+ DLIST_REMOVE(lhead, l_curr);
+ if(lhead == NULL) {
break; /* No more list... */
+ }
- ul_curr = ul_next;
+ l_curr = ul_next;
- } else if ( (ul_curr->start >= lock->start) &&
- (ul_curr->start < lock->start + lock->size) &&
- (ul_curr->start + ul_curr->size > lock->start + lock->size) ) {
+ } else if ( (l_curr->start >= lock->start) &&
+ (l_curr->start < lock->start + lock->size) &&
+ (l_curr->start + l_curr->size > lock->start + lock->size) ) {
/*
- * This unlock overlaps the existing lock range at the high end.
+ * This range overlaps the existing lock range at the high end.
* Truncate by moving start to existing range end and reducing size.
*/
/*********************************************
+---------------+
- | ul_curr |
+ | l_curr |
+---------------+
+---------------+
| lock |
+---------------+
BECOMES....
+-------+
- |ul_curr|
+ | l_curr|
+-------+
**********************************************/
- ul_curr->size = (ul_curr->start + ul_curr->size) - (lock->start + lock->size);
- ul_curr->start = lock->start + lock->size;
+ l_curr->size = (l_curr->start + l_curr->size) - (lock->start + lock->size);
+ l_curr->start = lock->start + lock->size;
- DEBUG(10,("truncate high case: start=%.0f,size=%.0f\n",
- (double)ul_curr->start, (double)ul_curr->size ));
+ DEBUG(10,(" truncate high case: start=%.0f,size=%.0f\n",
+ (double)l_curr->start, (double)l_curr->size ));
- ul_curr = ul_curr->next;
+ l_curr = l_curr->next;
- } else if ( (ul_curr->start < lock->start) &&
- (ul_curr->start + ul_curr->size > lock->start) ) {
+ } else if ( (l_curr->start < lock->start) &&
+ (l_curr->start + l_curr->size > lock->start) &&
+ (l_curr->start + l_curr->size <= lock->start + lock->size) ) {
/*
- * This unlock overlaps the existing lock range at the low end.
+ * This range overlaps the existing lock range at the low end.
* Truncate by reducing size.
*/
/*********************************************
+---------------+
- | ul_curr |
+ | l_curr |
+---------------+
+---------------+
| lock |
+---------------+
BECOMES....
+-------+
- |ul_curr|
+ | l_curr|
+-------+
**********************************************/
- ul_curr->size = lock->start - ul_curr->start;
+ l_curr->size = lock->start - l_curr->start;
- DEBUG(10,("truncate low case: start=%.0f,size=%.0f\n",
- (double)ul_curr->start, (double)ul_curr->size ));
+ DEBUG(10,(" truncate low case: start=%.0f,size=%.0f\n",
+ (double)l_curr->start, (double)l_curr->size ));
- ul_curr = ul_curr->next;
+ l_curr = l_curr->next;
- } else if ( (ul_curr->start < lock->start) &&
- (ul_curr->start + ul_curr->size > lock->start + lock->size) ) {
+ } else if ( (l_curr->start < lock->start) &&
+ (l_curr->start + l_curr->size > lock->start + lock->size) ) {
/*
- * Worst case scenario. Unlock request completely overlaps an existing
+ * Worst case scenario. Range completely overlaps an existing
* lock range. Split the request into two, push the new (upper) request
- * into the dlink list, and continue with the entry after ul_new (as we
- * know that ul_new will not overlap with this lock).
+ * into the dlink list, and continue with the entry after l_new (as we
+ * know that l_new will not overlap with this lock).
*/
/*********************************************
+---------------------------+
- | ul_curr |
+ | l_curr |
+---------------------------+
+---------+
| lock |
+---------+
BECOMES.....
+-------+ +---------+
- |ul_curr| |ul_new |
+ | l_curr| | l_new |
+-------+ +---------+
**********************************************/
- struct unlock_list *ul_new = (struct unlock_list *)talloc(ctx,
- sizeof(struct unlock_list));
+ struct lock_list *l_new = TALLOC_P(ctx, struct lock_list);
- if(ul_new == NULL) {
- DEBUG(0,("posix_unlock_list: talloc fail.\n"));
+ if(l_new == NULL) {
+ DEBUG(0,("posix_lock_list: talloc fail.\n"));
return NULL; /* The talloc_destroy takes care of cleanup. */
}
- ZERO_STRUCTP(ul_new);
- ul_new->start = lock->start + lock->size;
- ul_new->size = ul_curr->start + ul_curr->size - ul_new->start;
+ ZERO_STRUCTP(l_new);
+ l_new->start = lock->start + lock->size;
+ l_new->size = l_curr->start + l_curr->size - l_new->start;
- /* Add into the dlink list after the ul_curr point - NOT at ulhead. */
- DLIST_ADD(ul_curr, ul_new);
+ /* Truncate the l_curr. */
+ l_curr->size = lock->start - l_curr->start;
- /* Truncate the ul_curr. */
- ul_curr->size = lock->start - ul_curr->start;
+ DEBUG(10,(" split case: curr: start=%.0f,size=%.0f \
+new: start=%.0f,size=%.0f\n", (double)l_curr->start, (double)l_curr->size,
+ (double)l_new->start, (double)l_new->size ));
- DEBUG(10,("split case: curr: start=%.0f,size=%.0f \
-new: start=%.0f,size=%.0f\n", (double)ul_curr->start, (double)ul_curr->size,
- (double)ul_new->start, (double)ul_new->size ));
+ /*
+ * Add into the dlink list after the l_curr point - NOT at lhead.
+ * Note we can't use DLINK_ADD here as this inserts at the head of the given list.
+ */
- ul_curr = ul_new->next;
+ l_new->prev = l_curr;
+ l_new->next = l_curr->next;
+ l_curr->next = l_new;
+
+ /* And move after the link we added. */
+ l_curr = l_new->next;
} else {
* This logic case should never happen. Ensure this is the
* case by forcing an abort.... Remove in production.
*/
+ pstring msg;
+
+ slprintf(msg, sizeof(msg)-1, "logic flaw in cases: l_curr: start = %.0f, size = %.0f : \
+lock: start = %.0f, size = %.0f", (double)l_curr->start, (double)l_curr->size, (double)lock->start, (double)lock->size );
- smb_panic("logic flaw in cases...\n");
+ smb_panic(msg);
}
- } /* end for ( ul_curr = ulhead; ul_curr;) */
+ } /* end for ( l_curr = lhead; l_curr;) */
} /* end for (i=0; i<num_locks && ul_head; i++) */
- if (dbuf.dptr)
- free(dbuf.dptr);
-
- return ulhead;
+ return lhead;
}
/****************************************************************************
- POSIX function to release a lock. Returns True if the
- lock could be released, False if not.
+ POSIX function to acquire a lock. Returns True if the
+ lock could be granted, False if not.
****************************************************************************/
-BOOL release_posix_lock(files_struct *fsp, SMB_BIG_UINT u_offset, SMB_BIG_UINT u_count)
+bool set_posix_lock_windows_flavour(files_struct *fsp,
+ SMB_BIG_UINT u_offset,
+ SMB_BIG_UINT u_count,
+ enum brl_type lock_type,
+ const struct lock_context *lock_ctx,
+ const struct lock_struct *plocks,
+ int num_locks,
+ int *errno_ret)
{
SMB_OFF_T offset;
SMB_OFF_T count;
- BOOL ret = True;
- TALLOC_CTX *ul_ctx = NULL;
- struct unlock_list *ulist = NULL;
- struct unlock_list *ul = NULL;
- struct posix_lock deleted_lock;
- int num_entries;
+ int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+ bool ret = True;
+ size_t lock_count;
+ TALLOC_CTX *l_ctx = NULL;
+ struct lock_list *llist = NULL;
+ struct lock_list *ll = NULL;
- DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n",
- fsp->fsp_name, (double)u_offset, (double)u_count ));
+ DEBUG(5,("set_posix_lock_windows_flavour: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count, posix_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))
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) {
+ increment_windows_lock_ref_count(fsp);
return True;
+ }
/*
- * We treat this as one unlock request for POSIX accounting purposes even
- * if it may have been split into multiple smaller POSIX unlock ranges.
- */
-
- num_entries = delete_posix_lock_entry(fsp, offset, count, &deleted_lock);
+ * Windows is very strange. It allows read locks to be overlayed
+ * (even over a write lock), but leaves the write lock in force until the first
+ * unlock. It also reference counts the locks. This means the following sequence :
+ *
+ * process1 process2
+ * ------------------------------------------------------------------------
+ * WRITE LOCK : start = 2, len = 10
+ * READ LOCK: start =0, len = 10 - FAIL
+ * READ LOCK : start = 0, len = 14
+ * READ LOCK: start =0, len = 10 - FAIL
+ * UNLOCK : start = 2, len = 10
+ * READ LOCK: start =0, len = 10 - OK
+ *
+ * Under POSIX, the same sequence in steps 1 and 2 would not be reference counted, but
+ * would leave a single read lock over the 0-14 region.
+ */
+
+ if ((l_ctx = talloc_init("set_posix_lock")) == NULL) {
+ DEBUG(0,("set_posix_lock_windows_flavour: unable to init talloc context.\n"));
+ return False;
+ }
- if (num_entries == -1) {
- smb_panic("release_posix_lock: unable find entry to delete !\n");
+ if ((ll = TALLOC_P(l_ctx, struct lock_list)) == NULL) {
+ DEBUG(0,("set_posix_lock_windows_flavour: unable to talloc unlock list.\n"));
+ talloc_destroy(l_ctx);
+ return False;
}
/*
- * If num_entries is > 0, and the lock_type we just deleted from the tdb was
- * a POSIX write lock, then rather than doing an unlock we need to downgrade
- * the POSIX lock to a read lock.
+ * Create the initial list entry containing the
+ * lock we want to add.
+ */
+
+ ZERO_STRUCTP(ll);
+ ll->start = offset;
+ ll->size = count;
+
+ DLIST_ADD(llist, ll);
+
+ /*
+ * The following call calculates if there are any
+ * overlapping locks held by this process on
+ * fd's open on the same file and splits this list
+ * into a list of lock ranges that do not overlap with existing
+ * POSIX locks.
+ */
+
+ llist = posix_lock_list(l_ctx,
+ llist,
+ lock_ctx, /* Lock context llist belongs to. */
+ fsp,
+ plocks,
+ num_locks);
+
+ /*
+ * Add the POSIX locks on the list of ranges returned.
+ * As the lock is supposed to be added atomically, we need to
+ * back out all the locks if any one of these calls fail.
*/
- if (num_entries > 0 && deleted_lock.lock_type == F_WRLCK) {
- return posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK);
+ for (lock_count = 0, ll = llist; ll; ll = ll->next, lock_count++) {
+ offset = ll->start;
+ count = ll->size;
+
+ DEBUG(5,("set_posix_lock_windows_flavour: Real lock: Type = %s: offset = %.0f, count = %.0f\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+ *errno_ret = errno;
+ DEBUG(5,("set_posix_lock_windows_flavour: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
+ ret = False;
+ break;
+ }
+ }
+
+ if (!ret) {
+
+ /*
+ * Back out all the POSIX locks we have on fail.
+ */
+
+ for (ll = llist; lock_count; ll = ll->next, lock_count--) {
+ offset = ll->start;
+ count = ll->size;
+
+ DEBUG(5,("set_posix_lock_windows_flavour: Backing out locks: Type = %s: offset = %.0f, count = %.0f\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
+
+ posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK);
+ }
+ } else {
+ /* Remember the number of Windows locks we have on this dev/ino pair. */
+ increment_windows_lock_ref_count(fsp);
}
+ talloc_destroy(l_ctx);
+ return ret;
+}
+
+/****************************************************************************
+ POSIX function to release a lock. Returns True if the
+ lock could be released, False if not.
+****************************************************************************/
+
+bool release_posix_lock_windows_flavour(files_struct *fsp,
+ SMB_BIG_UINT u_offset,
+ SMB_BIG_UINT u_count,
+ enum brl_type deleted_lock_type,
+ const struct lock_context *lock_ctx,
+ const struct lock_struct *plocks,
+ int num_locks)
+{
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ bool ret = True;
+ TALLOC_CTX *ul_ctx = NULL;
+ struct lock_list *ulist = NULL;
+ struct lock_list *ul = NULL;
+
+ DEBUG(5,("release_posix_lock_windows_flavour: File %s, offset = %.0f, count = %.0f\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count ));
+
+ /* Remember the number of Windows locks we have on this dev/ino pair. */
+ decrement_windows_lock_ref_count(fsp);
+
/*
- * Only do the POSIX unlock when the num_entries is now zero.
+ * If the requested lock won't fit in the POSIX range, we will
+ * pretend it was successful.
*/
- if (num_entries > 0) {
- DEBUG(10, ("release_posix_lock: num_entries = %d\n", num_entries ));
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) {
return True;
}
- if ((ul_ctx = talloc_init()) == NULL) {
- DEBUG(0,("release_posix_lock: unable to init talloc context.\n"));
- return True; /* Not a fatal error. */
+ if ((ul_ctx = talloc_init("release_posix_lock")) == NULL) {
+ DEBUG(0,("release_posix_lock_windows_flavour: unable to init talloc context.\n"));
+ return False;
}
- if ((ul = (struct unlock_list *)talloc(ul_ctx, sizeof(struct unlock_list))) == NULL) {
- DEBUG(0,("release_posix_lock: unable to talloc unlock list.\n"));
+ if ((ul = TALLOC_P(ul_ctx, struct lock_list)) == NULL) {
+ DEBUG(0,("release_posix_lock_windows_flavour: unable to talloc unlock list.\n"));
talloc_destroy(ul_ctx);
- return True; /* Not a fatal error. */
+ return False;
}
/*
* unlocks are performed.
*/
- ulist = posix_unlock_list(ul_ctx, ulist, fsp);
+ ulist = posix_lock_list(ul_ctx,
+ ulist,
+ lock_ctx, /* Lock context ulist belongs to. */
+ fsp,
+ plocks,
+ num_locks);
+
+ /*
+ * If there were any overlapped entries (list is > 1 or size or start have changed),
+ * and the lock_type we just deleted from
+ * the upper layer tdb was a write lock, then before doing the unlock we need to downgrade
+ * the POSIX lock to a read lock. This allows any overlapping read locks
+ * to be atomically maintained.
+ */
+
+ if (deleted_lock_type == WRITE_LOCK &&
+ (!ulist || ulist->next != NULL || ulist->start != offset || ulist->size != count)) {
+
+ DEBUG(5,("release_posix_lock_windows_flavour: downgrading lock to READ: offset = %.0f, count = %.0f\n",
+ (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK)) {
+ DEBUG(0,("release_posix_lock_windows_flavour: downgrade of lock failed with error %s !\n", strerror(errno) ));
+ talloc_destroy(ul_ctx);
+ return False;
+ }
+ }
/*
* Release the POSIX locks on the list of ranges returned.
offset = ulist->start;
count = ulist->size;
- if(u_count == 0) {
-
- /*
- * This lock must overlap with an existing lock.
- * Don't do any POSIX call.
- */
-
- continue;
- }
-
- DEBUG(5,("release_posix_lock: Real unlock: offset = %.0f, count = %.0f\n",
+ DEBUG(5,("release_posix_lock_windows_flavour: Real unlock: offset = %.0f, count = %.0f\n",
(double)offset, (double)count ));
- if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK))
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) {
ret = False;
+ }
}
talloc_destroy(ul_ctx);
-
return ret;
}
/****************************************************************************
- Remove all lock entries for a specific dev/inode pair from the tdb.
+ Next - the functions that deal with mapping CIFS POSIX locks onto
+ the underlying system POSIX locks.
****************************************************************************/
-static void delete_posix_lock_entries(files_struct *fsp)
-{
- TDB_DATA kbuf = locking_key_fsp(fsp);
-
- if (tdb_delete(posix_lock_tdb, kbuf) == -1)
- DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
-}
-
/****************************************************************************
- Debug function.
+ POSIX function to acquire a lock. Returns True if the
+ lock could be granted, False if not.
+ As POSIX locks don't stack or conflict (they just overwrite)
+ we can map the requested lock directly onto a system one. We
+ know it doesn't conflict with locks on other contexts as the
+ upper layer would have refused it.
****************************************************************************/
-static void dump_entry(struct posix_lock *pl)
+bool set_posix_lock_posix_flavour(files_struct *fsp,
+ SMB_BIG_UINT u_offset,
+ SMB_BIG_UINT u_count,
+ enum brl_type lock_type,
+ int *errno_ret)
{
- DEBUG(10,("entry: start=%.0f, size=%.0f, type=%d, fd=%i\n",
- (double)pl->start, (double)pl->size, (int)pl->lock_type, pl->fd ));
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+
+ DEBUG(5,("set_posix_lock_posix_flavour: File %s, offset = %.0f, count = %.0f, type = %s\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count, posix_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)) {
+ return True;
+ }
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+ *errno_ret = errno;
+ DEBUG(5,("set_posix_lock_posix_flavour: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
+ posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
+ return False;
+ }
+ return True;
}
/****************************************************************************
- Remove any locks on this fd. Called from file_close().
+ POSIX function to release a lock. Returns True if the
+ lock could be released, False if not.
+ We are given a complete lock state from the upper layer which is what the lock
+ state should be after the unlock has already been done, so what
+ we do is punch out holes in the unlock range where locks owned by this process
+ have a different lock context.
****************************************************************************/
-void posix_locking_close_file(files_struct *fsp)
+bool release_posix_lock_posix_flavour(files_struct *fsp,
+ SMB_BIG_UINT u_offset,
+ SMB_BIG_UINT u_count,
+ const struct lock_context *lock_ctx,
+ const struct lock_struct *plocks,
+ int num_locks)
{
- struct posix_lock *entries = NULL;
- size_t count, i;
+ bool ret = True;
+ SMB_OFF_T offset;
+ SMB_OFF_T count;
+ TALLOC_CTX *ul_ctx = NULL;
+ struct lock_list *ulist = NULL;
+ struct lock_list *ul = NULL;
+
+ DEBUG(5,("release_posix_lock_posix_flavour: File %s, offset = %.0f, count = %.0f\n",
+ fsp->fsp_name, (double)u_offset, (double)u_count ));
/*
- * 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 remove all the entries in the tdb and allow the
- * close to remove the real locks.
+ * If the requested lock won't fit in the POSIX range, we will
+ * pretend it was successful.
*/
- count = get_posix_lock_entries(fsp, &entries);
-
- if (count == 0) {
- DEBUG(10,("posix_locking_close_file: file %s has no outstanding locks.\n", fsp->fsp_name ));
- return;
+ if(!posix_lock_in_range(&offset, &count, u_offset, u_count)) {
+ return True;
}
- for (i = 0; i < count; i++) {
- if (entries[i].fd != fsp->fd )
- break;
-
- dump_entry(&entries[i]);
+ if ((ul_ctx = talloc_init("release_posix_lock")) == NULL) {
+ DEBUG(0,("release_posix_lock_windows_flavour: unable to init talloc context.\n"));
+ return False;
}
- if (i == count) {
- /* All locks are ours. */
- DEBUG(10,("posix_locking_close_file: file %s has %u outstanding locks, but all on one fd.\n",
- fsp->fsp_name, (unsigned int)count ));
- free((char *)entries);
- delete_posix_lock_entries(fsp);
- return;
+ if ((ul = TALLOC_P(ul_ctx, struct lock_list)) == NULL) {
+ DEBUG(0,("release_posix_lock_windows_flavour: unable to talloc unlock list.\n"));
+ talloc_destroy(ul_ctx);
+ return False;
}
/*
- * Difficult case. We need to delete all our locks, whilst leaving
- * all other POSIX locks in place.
+ * Create the initial list entry containing the
+ * lock we want to remove.
*/
- for (i = 0; i < count; i++) {
- struct posix_lock *pl = &entries[i];
- release_posix_lock(fsp, (SMB_BIG_UINT)pl->start, (SMB_BIG_UINT)pl->size );
- }
- free((char *)entries);
-}
+ ZERO_STRUCTP(ul);
+ ul->start = offset;
+ ul->size = count;
-/*******************************************************************
- Create the in-memory POSIX lock databases.
-********************************************************************/
+ DLIST_ADD(ulist, ul);
-BOOL posix_locking_init(void)
-{
- if (posix_lock_tdb && posix_pending_close_tdb)
- return True;
+ /*
+ * Walk the given array creating a linked list
+ * of unlock requests.
+ */
- if (!posix_lock_tdb)
- posix_lock_tdb = tdb_open(NULL, 0, TDB_INTERNAL,
- O_RDWR|O_CREAT, 0644);
- if (!posix_lock_tdb) {
- DEBUG(0,("Failed to open POSIX byte range locking database.\n"));
- return False;
- }
- if (!posix_pending_close_tdb)
- posix_pending_close_tdb = tdb_open(NULL, 0, TDB_INTERNAL,
- O_RDWR|O_CREAT, 0644);
- if (!posix_pending_close_tdb) {
- DEBUG(0,("Failed to open POSIX pending close database.\n"));
- return False;
- }
+ ulist = posix_lock_list(ul_ctx,
+ ulist,
+ lock_ctx, /* Lock context ulist belongs to. */
+ fsp,
+ plocks,
+ num_locks);
- return True;
-}
+ /*
+ * Release the POSIX locks on the list of ranges returned.
+ */
-/*******************************************************************
- Delete the in-memory POSIX lock databases.
-********************************************************************/
+ for(; ulist; ulist = ulist->next) {
+ offset = ulist->start;
+ count = ulist->size;
-BOOL posix_locking_end(void)
-{
- if (posix_lock_tdb && tdb_close(posix_lock_tdb) != 0)
- return False;
- if (posix_pending_close_tdb && tdb_close(posix_pending_close_tdb) != 0)
- return False;
- return True;
+ DEBUG(5,("release_posix_lock_posix_flavour: Real unlock: offset = %.0f, count = %.0f\n",
+ (double)offset, (double)count ));
+
+ if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) {
+ ret = False;
+ }
+ }
+
+ talloc_destroy(ul_ctx);
+ return ret;
}