summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/locking/posix.c113
1 files changed, 90 insertions, 23 deletions
diff --git a/source3/locking/posix.c b/source3/locking/posix.c
index ab58607172..ce9474fdda 100644
--- a/source3/locking/posix.c
+++ b/source3/locking/posix.c
@@ -50,6 +50,7 @@ struct posix_lock {
SMB_OFF_T start;
SMB_OFF_T size;
int lock_type;
+ size_t ref_count;
};
/*
@@ -290,29 +291,83 @@ static BOOL add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T s
{
TDB_DATA kbuf = locking_key_fsp(fsp);
TDB_DATA dbuf;
- struct posix_lock pl;
+ struct posix_lock *entries;
+ size_t count, i, ref_count;
/*
- * Now setup the new record.
+ * Windows is very strange. It allows a write lock to be downgraded to
+ * a read lock, but reference counts the locks. This means if you get
+ * the following sequence :
+ *
+ * WRITE LOCK : start = 0, len = 10
+ * READ LOCK : start = 0, len = 10
+ *
+ * then another process will be able to get a read lock, but the unlock
+ * sequence must be done twice to remove the read lock.
+ *
+ * Under POSIX, the same sequence 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 reference
+ * count the POSIX lock request, so that when multiple locks over a
+ * region is requested of the Samba POSIX lock code (this can only
+ * be an overlay of a read onto a write lock due to the checks already
+ * being done in brlock.c) then we map the lock onto POSIX, but just
+ * increment the reference count in the tdb. Unlocks decrement the
+ * reference count and only do the POSIX unlock call when the refcount
+ * reaches zero.
*/
-
- pl.fd = fsp->fd;
- pl.start = start;
- pl.size = size;
- pl.lock_type = lock_type;
-
+
dbuf.dptr = NULL;
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"));
- goto fail;
+ /*
+ * Look for an existing entry matching this region.
+ */
+
+ entries = (struct posix_lock *)dbuf.dptr;
+ count = (size_t)(dbuf.dsize / sizeof(struct posix_lock));
+
+ for (i = 0; i < count; i++) {
+ struct posix_lock *entry = &entries[i];
+
+ /*
+ * Just increment the ref_count on a matching entry.
+ * Note we don't take lock type into account here.
+ */
+
+ if ((entry->fd == fsp->fd) &&
+ (entry->start == start) &&
+ (entry->size == size)) {
+ /* Overwrite the lock type also. */
+ entry->lock_type = lock_type;
+ ref_count = ++entry->ref_count;
+ break;
+ }
}
- memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
- dbuf.dsize += sizeof(pl);
+ if (i == count) {
+ struct posix_lock pl;
+
+ /*
+ * New record needed.
+ */
+
+ pl.fd = fsp->fd;
+ pl.start = start;
+ pl.size = size;
+ pl.lock_type = lock_type;
+ ref_count = pl.ref_count = 1;
+
+ dbuf.dptr = Realloc(dbuf.dptr, dbuf.dsize + sizeof(pl));
+ if (!dbuf.dptr) {
+ DEBUG(0,("add_posix_lock_entry: Realloc fail !\n"));
+ goto fail;
+ }
+
+ memcpy(dbuf.dptr + dbuf.dsize, &pl, sizeof(pl));
+ dbuf.dsize += sizeof(pl);
+ }
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));
@@ -321,8 +376,8 @@ static BOOL add_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_T s
free(dbuf.dptr);
- 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, ref_count = %u :dev=%.0f inode=%.0f\n",
+ fsp->fsp_name, posix_lock_type_name(lock_type), (double)start, (double)size, (unsigned int)ref_count,
(double)fsp->dev, (double)fsp->inode ));
return True;
@@ -363,14 +418,26 @@ static BOOL delete_posix_lock_entry(files_struct *fsp, SMB_OFF_T start, SMB_OFF_
if (pl->fd == fsp->fd &&
pl->start == start &&
pl->size == size) {
- /* 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));
+
+ pl->ref_count--;
+
+ DEBUG(10,("delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, ref_count = %u\n",
+ posix_lock_type_name(pl->lock_type), (double)pl->start, (double)pl->size,
+ (unsigned int)pl->ref_count ));
+
+ if (pl->ref_count == 0) {
+ /* 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);
}
- dbuf.dsize -= sizeof(*locks);
+ } else {
+ /* Just re-store the new ref count. */
tdb_store(posix_lock_tdb, kbuf, dbuf, TDB_REPLACE);
}