dbuf.dptr = NULL;
- tdb_lockchain(posix_pending_close_tdb, kbuf);
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"));
- tdb_unlockchain(posix_pending_close_tdb, kbuf);
return False;
}
memcpy(dbuf.dptr + dbuf.dsize, &fsp->fd, sizeof(int));
}
free(dbuf.dptr);
- tdb_unlockchain(posix_pending_close_tdb, kbuf);
return True;
}
{
TDB_DATA kbuf = locking_key_fsp(fsp);
- tdb_lockchain(posix_pending_close_tdb, kbuf);
if (tdb_delete(posix_pending_close_tdb, kbuf) == -1)
DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
- tdb_unlockchain(posix_pending_close_tdb, kbuf);
}
/****************************************************************************
*entries = NULL;
dbuf.dptr = NULL;
- tdb_lockchain(posix_pending_close_tdb, kbuf);
dbuf = tdb_fetch(posix_pending_close_tdb, kbuf);
if (!dbuf.dptr) {
- tdb_unlockchain(posix_pending_close_tdb, kbuf);
return 0;
}
*entries = (int *)dbuf.dptr;
count = (size_t)(dbuf.dsize / sizeof(int));
- tdb_unlockchain(posix_pending_close_tdb, kbuf);
-
return count;
}
dbuf.dptr = NULL;
- tdb_lockchain(posix_lock_tdb, kbuf);
dbuf = tdb_fetch(posix_lock_tdb, kbuf);
if (!dbuf.dptr) {
- tdb_unlockchain(posix_lock_tdb, kbuf);
return 0;
}
*entries = (struct posix_lock *)dbuf.dptr;
count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
- tdb_unlockchain(posix_lock_tdb, kbuf);
-
return count;
}
/****************************************************************************
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.
****************************************************************************/
int fd_close_posix(struct connection_struct *conn, files_struct *fsp)
size_t count, i;
struct posix_lock *entries = NULL;
int *fd_array = NULL;
+ BOOL locks_on_other_fds = False;
if (!lp_posix_locking(SNUM(conn))) {
/*
*/
count = get_posix_lock_entries(fsp, &entries);
-
- if (count) {
+
+ /*
+ * Check if there are any outstanding locks belonging to
+ * other fd's. This should never be the case if posix_locking_close_file()
+ * has been called first, but it never hurts to be *sure*.
+ */
+
+ for (i = 0; i < count; i++) {
+ if (entries[i].fd != fsp->fd) {
+ locks_on_other_fds = True;
+ break;
+ }
+ }
+
+ if (locks_on_other_fds) {
/*
* There are outstanding locks on this dev/inode pair on other fds.
}
}
- if (fd_array)
- free((char *)fd_array);
-
/*
* Delete all fd's stored in the tdb
* for this dev/inode pair.
}
/****************************************************************************
- Add an entry into the POSIX locking tdb.
+ Add an entry into the POSIX locking tdb. Returns the number of records that
+ match the given start and size, or -1 on error.
****************************************************************************/
-static BOOL add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, int lock_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;
/*
- * Now setup the new record.
+ * 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;
+
+ dbuf = tdb_fetch(posix_lock_tdb, kbuf);
+
+ /*
+ * New record.
*/
pl.fd = fsp->fd;
pl.size = size;
pl.lock_type = lock_type;
- dbuf.dptr = NULL;
-
- tdb_lockchain(posix_lock_tdb, kbuf);
- dbuf = tdb_fetch(posix_lock_tdb, kbuf);
-
dbuf.dptr = Realloc(dbuf.dptr, dbuf.dsize + sizeof(pl));
if (!dbuf.dptr) {
DEBUG(0,("add_posix_lock_entry: Realloc fail !\n"));
memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
dbuf.dsize += sizeof(pl);
+ count = (size_t)(dbuf.dsize / sizeof(pl));
+ entries = (struct posix_lock *)dbuf.dptr;
+
+ for (i = 0; i < count; i++) {
+ struct posix_lock *entry = &entries[i];
+
+ if (fsp->fd == entry->fd &&
+ start == entry->start &&
+ size == entry->size)
+ num_records++;
+
+ }
+
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;
}
free(dbuf.dptr);
- tdb_unlockchain(posix_lock_tdb, kbuf);
- DEBUG(10,("add_posix_lock: File %s: type = %s: start=%.0f size=%.0f:dev=%.0f inode=%.0f\n",
- fsp->fsp_name, posix_lock_type_name(lock_type), (double)start, (double)size,
+ 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 ));
- return True;
+ return num_records;
fail:
if (dbuf.dptr)
free(dbuf.dptr);
- tdb_unlockchain(posix_lock_tdb, kbuf);
- return False;
+ return -1;
}
/****************************************************************************
- Delete an entry from the POSIX locking tdb.
+ 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.
****************************************************************************/
-static BOOL delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size)
+static int delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T size, struct posix_lock *pl)
{
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;
-
- tdb_lockchain(posix_lock_tdb, kbuf);
+
dbuf = tdb_fetch(posix_lock_tdb, kbuf);
if (!dbuf.dptr) {
locks = (struct posix_lock *)dbuf.dptr;
count = (size_t)(dbuf.dsize / sizeof(*locks));
+ /*
+ * Count the number of entries that match this
+ * unlock request.
+ */
+
+ 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++;
+ }
+ }
+
for (i=0; i<count; i++) {
- struct posix_lock *pl = &locks[i];
+ struct posix_lock *entry = &locks[i];
+
+ if (entry->fd == fsp->fd &&
+ entry->start == start &&
+ entry->size == size) {
+
+ num_records--; /* We're deleting one. */
+
+ /* Make a copy if requested. */
+ if (pl)
+ *pl = *entry;
+
+ DEBUG(10,("delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, num_records = %d\n",
+ posix_lock_type_name(entry->lock_type), (double)entry->start, (double)entry->size,
+ (unsigned int)num_records ));
- if (pl->fd == fsp->fd &&
- pl->start == start &&
- pl->size == size) {
/* Found it - delete it. */
if (count == 1) {
tdb_delete(posix_lock_tdb, kbuf);
}
free(dbuf.dptr);
- tdb_unlockchain(posix_lock_tdb, kbuf);
- return True;
+ return num_records;
}
}
fail:
if (dbuf.dptr)
free(dbuf.dptr);
- tdb_unlockchain(posix_lock_tdb, kbuf);
- return False;
+ return -1;
}
/****************************************************************************
return True;
}
+#if defined(LARGE_SMB_OFF_T)
+/****************************************************************************
+ Pathetically try and map a 64 bit lock offset into 31 bits. I hate Windows :-).
+****************************************************************************/
+
+static uint32 map_lock_offset(uint32 high, uint32 low)
+{
+ unsigned int i;
+ uint32 mask = 0;
+ uint32 highcopy = high;
+
+ /*
+ * Try and find out how many significant bits there are in high.
+ */
+
+ for(i = 0; highcopy; i++)
+ highcopy >>= 1;
+
+ /*
+ * We use 31 bits not 32 here as POSIX
+ * lock offsets may not be negative.
+ */
+
+ mask = (~0) << (31 - i);
+
+ if(low & mask)
+ return 0; /* Fail. */
+
+ high <<= (31 - i);
+
+ return (high|low);
+}
+#endif
+
+/****************************************************************************
+ Actual function that does POSIX locks. Copes with 64 -> 32 bit cruft and
+ broken NFS implementations.
+****************************************************************************/
+
+static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+ int ret;
+ struct connection_struct *conn = fsp->conn;
+
+#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 */
+
+ DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fd,op,(double)offset,(double)count,type));
+
+ ret = conn->vfs_ops.lock(fsp->fd,op,offset,count,type);
+
+ 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);
+ }
+
+ /* A lock query - just return. */
+ if (op == SMB_F_GETLK)
+ return ret;
+
+ /* 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)));
+
+ /* 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 */
+ }
+
+ return(False);
+ }
+
+ 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.
* fd. So we don't need to use map_lock_type here.
*/
- return fcntl_lock(fsp->fd,SMB_F_GETLK,offset,count,posix_lock_type);
+ return posix_fcntl_lock(fsp,SMB_F_GETLK,offset,count,posix_lock_type);
}
/****************************************************************************
SMB_OFF_T count;
BOOL ret = True;
int posix_lock_type = map_posix_lock_type(fsp,lock_type);
+ int ref_count;
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) ));
* 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. JRA.
+ * 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().
*/
- ret = fcntl_lock(fsp->fd,SMB_F_SETLK,offset,count,posix_lock_type);
+ ref_count = add_posix_lock_entry(fsp,offset,count,posix_lock_type);
+
+ 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)
- add_posix_lock_entry(fsp,offset,count,posix_lock_type);
+ /*
+ * Oops, POSIX lock failed, delete the tdb entry.
+ */
+ if (!ret)
+ delete_posix_lock_entry(fsp,offset,count,NULL);
+ }
return ret;
}
dbuf.dptr = NULL;
- tdb_lockchain(posix_lock_tdb, kbuf);
dbuf = tdb_fetch(posix_lock_tdb, kbuf);
if (!dbuf.dptr) {
- tdb_unlockchain(posix_lock_tdb, kbuf);
return ulhead;
}
(double)lock->start, (double)lock->size ));
if ( (ul_curr->start >= (lock->start + lock->size)) ||
- (lock->start > (ul_curr->start + ul_curr->size))) {
+ (lock->start >= (ul_curr->start + ul_curr->size))) {
/* No overlap with this lock - leave this range alone. */
/*********************************************
* 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: ul_curr: start = %.0f, size = %.0f : \
+lock: start = %.0f, size = %.0f\n", (double)ul_curr->start, (double)ul_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 (i=0; i<num_locks && ul_head; i++) */
- tdb_unlockchain(posix_lock_tdb, kbuf);
-
if (dbuf.dptr)
free(dbuf.dptr);
TALLOC_CTX *ul_ctx = NULL;
struct unlock_list *ulist = NULL;
struct unlock_list *ul = NULL;
+ struct posix_lock deleted_lock;
+ int num_entries;
DEBUG(5,("release_posix_lock: File %s, offset = %.0f, count = %.0f\n",
fsp->fsp_name, (double)u_offset, (double)u_count ));
* if it may have been split into multiple smaller POSIX unlock ranges.
*/
- delete_posix_lock_entry(fsp, offset, count);
+ num_entries = delete_posix_lock_entry(fsp, offset, count, &deleted_lock);
+
+ if (num_entries == -1) {
+ smb_panic("release_posix_lock: unable find entry to delete !\n");
+ }
+
+ /*
+ * 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.
+ */
+
+ if (num_entries > 0 && deleted_lock.lock_type == F_WRLCK) {
+ return posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK);
+ }
+
+ /*
+ * Only do the POSIX unlock when the num_entries is now zero.
+ */
+
+ if (num_entries > 0) {
+ DEBUG(10, ("release_posix_lock: num_entries = %d\n", num_entries ));
+ return True;
+ }
if ((ul_ctx = talloc_init()) == NULL) {
DEBUG(0,("release_posix_lock: unable to init talloc context.\n"));
DEBUG(5,("release_posix_lock: Real unlock: offset = %.0f, count = %.0f\n",
(double)offset, (double)count ));
- if (!fcntl_lock(fsp->fd,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);
+ talloc_destroy(ul_ctx);
return ret;
}
{
TDB_DATA kbuf = locking_key_fsp(fsp);
- tdb_lockchain(posix_lock_tdb, kbuf);
if (tdb_delete(posix_lock_tdb, kbuf) == -1)
DEBUG(0,("delete_close_entries: tdb_delete fail !\n"));
- tdb_unlockchain(posix_lock_tdb, kbuf);
+}
+
+/****************************************************************************
+ Debug function.
+****************************************************************************/
+
+static void dump_entry(struct posix_lock *pl)
+{
+ DEBUG(10,("entry: start=%.0f, size=%.0f, type=%d, fd=%i\n",
+ (double)pl->start, (double)pl->size, (int)pl->lock_type, pl->fd ));
}
/****************************************************************************
for (i = 0; i < count; i++) {
if (entries[i].fd != fsp->fd )
break;
+
+ dump_entry(&entries[i]);
}
if (i == count) {
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 );
+ if (pl->fd == fsp->fd)
+ release_posix_lock(fsp, (SMB_BIG_UINT)pl->start, (SMB_BIG_UINT)pl->size );
}
free((char *)entries);
}
return True;
if (!posix_lock_tdb)
- posix_lock_tdb = tdb_open(NULL, 0, TDB_CLEAR_IF_FIRST,
- O_RDWR|O_CREAT, 0644);
+ 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_CLEAR_IF_FIRST,
+ 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"));