Changed to sourceforge tdb code. This includes spinlocks (so we now have
[Samba/ekacnet.git] / source / locking / brlock.c
blob5c3dae02c02bbda2f696ef7591db27feffc6b7da
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 byte range locking code
5 Updated to handle range splits/merges.
7 Copyright (C) Andrew Tridgell 1992-2000
8 Copyright (C) Jeremy Allison 1992-2000
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 /* This module implements a tdb based byte range locking service,
26 replacing the fcntl() based byte range locking previously
27 used. This allows us to provide the same semantics as NT */
29 #include "includes.h"
31 extern int DEBUGLEVEL;
33 /* This contains elements that differentiate locks. The smbpid is a
34 client supplied pid, and is essentially the locking context for
35 this client */
37 struct lock_context {
38 uint16 smbpid;
39 uint16 tid;
40 pid_t pid;
43 /* The data in brlock records is an unsorted linear array of these
44 records. It is unnecessary to store the count as tdb provides the
45 size of the record */
47 struct lock_struct {
48 struct lock_context context;
49 br_off start;
50 br_off size;
51 int fnum;
52 enum brl_type lock_type;
55 /* The key used in the brlock database. */
57 struct lock_key {
58 SMB_DEV_T device;
59 SMB_INO_T inode;
62 /* The open brlock.tdb database. */
64 static TDB_CONTEXT *tdb;
66 /****************************************************************************
67 Create a locking key - ensuring zero filled for pad purposes.
68 ****************************************************************************/
70 static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
72 static struct lock_key key;
73 TDB_DATA kbuf;
75 memset(&key, '\0', sizeof(key));
76 key.device = dev;
77 key.inode = inode;
78 kbuf.dptr = (char *)&key;
79 kbuf.dsize = sizeof(key);
80 return kbuf;
83 /****************************************************************************
84 See if two locking contexts are equal.
85 ****************************************************************************/
87 static BOOL brl_same_context(struct lock_context *ctx1,
88 struct lock_context *ctx2)
90 return (ctx1->pid == ctx2->pid) &&
91 (ctx1->smbpid == ctx2->smbpid) &&
92 (ctx1->tid == ctx2->tid);
95 /****************************************************************************
96 See if lock2 can be added when lock1 is in place.
97 ****************************************************************************/
99 static BOOL brl_conflict(struct lock_struct *lck1,
100 struct lock_struct *lck2)
102 if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
103 return False;
105 if (brl_same_context(&lck1->context, &lck2->context) &&
106 lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) return False;
108 if (lck1->start >= (lck2->start + lck2->size) ||
109 lck2->start >= (lck1->start + lck1->size)) return False;
111 return True;
115 /****************************************************************************
116 delete a record if it is for a dead process
117 ****************************************************************************/
118 static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
120 struct lock_struct *locks;
121 struct lock_key *key;
122 int count, i;
124 tdb_chainlock(tdb, kbuf);
126 locks = (struct lock_struct *)dbuf.dptr;
127 key = (struct lock_key *)kbuf.dptr;
129 count = dbuf.dsize / sizeof(*locks);
130 for (i=0; i<count; i++) {
131 struct lock_struct *lock = &locks[i];
133 if (process_exists(lock->context.pid)) continue;
135 if (count > 1 && i < count-1) {
136 memmove(&locks[i], &locks[i+1],
137 sizeof(*locks)*((count-1) - i));
139 count--;
140 i--;
143 if (count == 0) {
144 tdb_delete(tdb, kbuf);
145 } else if (count < (dbuf.dsize / sizeof(*locks))) {
146 dbuf.dsize = count * sizeof(*locks);
147 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
150 tdb_chainunlock(tdb, kbuf);
151 return 0;
154 /****************************************************************************
155 Open up the brlock.tdb database.
156 ****************************************************************************/
157 void brl_init(int read_only)
159 if (tdb) return;
160 tdb = tdb_open(lock_path("brlock.tdb"), 0, TDB_CLEAR_IF_FIRST,
161 read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
162 if (!tdb) {
163 DEBUG(0,("Failed to open byte range locking database\n"));
164 return;
167 /* delete any dead locks */
168 if (!read_only) {
169 tdb_traverse(tdb, delete_fn, NULL);
174 /****************************************************************************
175 Lock a range of bytes.
176 ****************************************************************************/
178 BOOL brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
179 uint16 smbpid, pid_t pid, uint16 tid,
180 br_off start, br_off size,
181 enum brl_type lock_type)
183 TDB_DATA kbuf, dbuf;
184 int count, i;
185 struct lock_struct lock, *locks;
187 kbuf = locking_key(dev,ino);
189 dbuf.dptr = NULL;
191 tdb_chainlock(tdb, kbuf);
192 dbuf = tdb_fetch(tdb, kbuf);
194 lock.context.smbpid = smbpid;
195 lock.context.pid = pid;
196 lock.context.tid = tid;
197 lock.start = start;
198 lock.size = size;
199 lock.fnum = fnum;
200 lock.lock_type = lock_type;
202 if (dbuf.dptr) {
203 /* there are existing locks - make sure they don't conflict */
204 locks = (struct lock_struct *)dbuf.dptr;
205 count = dbuf.dsize / sizeof(*locks);
206 for (i=0; i<count; i++) {
207 if (brl_conflict(&locks[i], &lock)) {
208 goto fail;
213 /* no conflicts - add it to the list of locks */
214 dbuf.dptr = Realloc(dbuf.dptr, dbuf.dsize + sizeof(*locks));
215 if (!dbuf.dptr) goto fail;
216 memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock));
217 dbuf.dsize += sizeof(lock);
218 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
220 free(dbuf.dptr);
221 tdb_chainunlock(tdb, kbuf);
222 return True;
224 fail:
225 if (dbuf.dptr) free(dbuf.dptr);
226 tdb_chainunlock(tdb, kbuf);
227 return False;
230 /****************************************************************************
231 Unlock a range of bytes.
232 ****************************************************************************/
234 BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
235 uint16 smbpid, pid_t pid, uint16 tid,
236 br_off start, br_off size)
238 TDB_DATA kbuf, dbuf;
239 int count, i;
240 struct lock_struct *locks;
241 struct lock_context context;
243 kbuf = locking_key(dev,ino);
245 dbuf.dptr = NULL;
247 tdb_chainlock(tdb, kbuf);
248 dbuf = tdb_fetch(tdb, kbuf);
250 if (!dbuf.dptr) {
251 DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
252 goto fail;
255 context.smbpid = smbpid;
256 context.pid = pid;
257 context.tid = tid;
259 /* there are existing locks - find a match */
260 locks = (struct lock_struct *)dbuf.dptr;
261 count = dbuf.dsize / sizeof(*locks);
262 for (i=0; i<count; i++) {
264 struct lock_struct *lock = &locks[i];
266 #if 0
267 /* JRATEST - DEBUGGING INFO */
268 if(!brl_same_context(&lock->context, &context)) {
269 DEBUG(10,("brl_unlock: Not same context. l_smbpid = %u, l_pid = %u, l_tid = %u: \
270 smbpid = %u, pid = %u, tid = %u\n",
271 lock->context.smbpid, lock->context.pid, lock->context.tid,
272 context.smbpid, context.pid, context.tid ));
275 /* JRATEST */
276 #endif
278 if (brl_same_context(&lock->context, &context) &&
279 lock->fnum == fnum &&
280 lock->start == start &&
281 lock->size == size) {
282 /* found it - delete it */
283 if (count == 1) {
284 tdb_delete(tdb, kbuf);
285 } else {
286 if (i < count-1) {
287 memmove(&locks[i], &locks[i+1],
288 sizeof(*locks)*((count-1) - i));
290 dbuf.dsize -= sizeof(*locks);
291 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
294 free(dbuf.dptr);
295 tdb_chainunlock(tdb, kbuf);
296 return True;
300 /* we didn't find it */
302 fail:
303 if (dbuf.dptr) free(dbuf.dptr);
304 tdb_chainunlock(tdb, kbuf);
305 return False;
308 /****************************************************************************
309 Test if we could add a lock if we wanted to.
310 ****************************************************************************/
312 BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
313 uint16 smbpid, pid_t pid, uint16 tid,
314 br_off start, br_off size,
315 enum brl_type lock_type)
317 TDB_DATA kbuf, dbuf;
318 int count, i;
319 struct lock_struct lock, *locks;
321 kbuf = locking_key(dev,ino);
323 dbuf.dptr = NULL;
325 tdb_chainlock(tdb, kbuf);
326 dbuf = tdb_fetch(tdb, kbuf);
328 lock.context.smbpid = smbpid;
329 lock.context.pid = pid;
330 lock.context.tid = tid;
331 lock.start = start;
332 lock.size = size;
333 lock.fnum = fnum;
334 lock.lock_type = lock_type;
336 if (dbuf.dptr) {
337 /* there are existing locks - make sure they don't conflict */
338 locks = (struct lock_struct *)dbuf.dptr;
339 count = dbuf.dsize / sizeof(*locks);
340 for (i=0; i<count; i++) {
341 if (brl_conflict(&locks[i], &lock)) {
342 goto fail;
347 /* no conflicts - we could have added it */
348 free(dbuf.dptr);
349 tdb_chainunlock(tdb, kbuf);
350 return True;
352 fail:
353 if (dbuf.dptr) free(dbuf.dptr);
354 tdb_chainunlock(tdb, kbuf);
355 return False;
358 /****************************************************************************
359 Remove any locks associated with a open file.
360 ****************************************************************************/
362 void brl_close(SMB_DEV_T dev, SMB_INO_T ino, pid_t pid, int tid, int fnum)
364 TDB_DATA kbuf, dbuf;
365 int count, i, dcount=0;
366 struct lock_struct *locks;
368 kbuf = locking_key(dev,ino);
370 dbuf.dptr = NULL;
372 tdb_chainlock(tdb, kbuf);
373 dbuf = tdb_fetch(tdb, kbuf);
375 if (!dbuf.dptr) goto fail;
377 /* there are existing locks - remove any for this fnum */
378 locks = (struct lock_struct *)dbuf.dptr;
379 count = dbuf.dsize / sizeof(*locks);
380 for (i=0; i<count; i++) {
381 struct lock_struct *lock = &locks[i];
383 if (lock->context.tid == tid &&
384 lock->context.pid == pid &&
385 lock->fnum == fnum) {
386 /* found it - delete it */
387 if (count > 1 && i < count-1) {
388 memmove(&locks[i], &locks[i+1],
389 sizeof(*locks)*((count-1) - i));
391 count--;
392 i--;
393 dcount++;
397 if (count == 0) {
398 tdb_delete(tdb, kbuf);
399 } else if (count < (dbuf.dsize / sizeof(*locks))) {
400 dbuf.dsize -= dcount * sizeof(*locks);
401 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
404 /* we didn't find it */
405 fail:
406 if (dbuf.dptr) free(dbuf.dptr);
407 tdb_chainunlock(tdb, kbuf);
410 /****************************************************************************
411 Traverse the whole database with this function, calling traverse_callback
412 on each lock.
413 ****************************************************************************/
415 static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
417 struct lock_struct *locks;
418 struct lock_key *key;
419 int i;
421 BRLOCK_FN(traverse_callback) = (BRLOCK_FN_CAST())state;
423 locks = (struct lock_struct *)dbuf.dptr;
424 key = (struct lock_key *)kbuf.dptr;
426 for (i=0;i<dbuf.dsize/sizeof(*locks);i++) {
427 traverse_callback(key->device, key->inode,
428 locks[i].context.pid,
429 locks[i].lock_type,
430 locks[i].start,
431 locks[i].size);
433 return 0;
436 /*******************************************************************
437 Call the specified function on each lock in the database.
438 ********************************************************************/
440 int brl_forall(BRLOCK_FN(fn))
442 if (!tdb) return 0;
443 return tdb_traverse(tdb, traverse_fn, (void *)fn);