we now have all but the dreaded 0/0 lock working
[vlendec/samba-autobuild/.git] / source3 / locking / brlock.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    byte range locking code
5    Updated to handle range splits/merges.
6
7    Copyright (C) Andrew Tridgell 1992-2000
8    Copyright (C) Jeremy Allison 1992-2000
9    
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.
14    
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.
19    
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.
23 */
24
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 */
28
29 #include "includes.h"
30
31 extern int DEBUGLEVEL;
32
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 */
36
37 struct lock_context {
38         uint16 smbpid;
39         uint16 tid;
40         pid_t pid;
41 };
42
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 */
46
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;
53 };
54
55 /* The key used in the brlock database. */
56
57 struct lock_key {
58         SMB_DEV_T device;
59         SMB_INO_T inode;
60 };
61
62 /* The open brlock.tdb database. */
63
64 static TDB_CONTEXT *tdb;
65
66 /****************************************************************************
67  Create a locking key - ensuring zero filled for pad purposes.
68 ****************************************************************************/
69
70 static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
71 {
72         static struct lock_key key;
73         TDB_DATA kbuf;
74
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;
81 }
82
83 /****************************************************************************
84  See if two locking contexts are equal.
85 ****************************************************************************/
86
87 static BOOL brl_same_context(struct lock_context *ctx1, 
88                              struct lock_context *ctx2)
89 {
90         return (ctx1->pid == ctx2->pid) &&
91                 (ctx1->smbpid == ctx2->smbpid) &&
92                 (ctx1->tid == ctx2->tid);
93 }
94
95 /****************************************************************************
96  See if lock2 can be added when lock1 is in place.
97 ****************************************************************************/
98
99 static BOOL brl_conflict(struct lock_struct *lck1, 
100                          struct lock_struct *lck2)
101 {
102         if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
103                 return False;
104         }
105
106         if (brl_same_context(&lck1->context, &lck2->context) &&
107             lck2->lock_type == READ_LOCK && lck1->fnum == lck2->fnum) {
108                 return False;
109         }
110
111         if (lck1->start >= (lck2->start + lck2->size) ||
112             lck2->start >= (lck1->start + lck1->size)) {
113                 return False;
114         }
115             
116         return True;
117
118
119 /****************************************************************************
120  Check to see if this lock conflicts, but ignore our own locks on the
121  same fnum only.
122 ****************************************************************************/
123
124 static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
125 {
126         if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) 
127                 return False;
128
129         if (brl_same_context(&lck1->context, &lck2->context) &&
130                                 lck1->fnum == lck2->fnum)
131                 return False;
132
133         if (lck1->start >= (lck2->start + lck2->size) ||
134             lck2->start >= (lck1->start + lck1->size)) return False;
135             
136         return True;
137
138
139
140 /****************************************************************************
141  Delete a record if it is for a dead process, if check_self is true, then
142  delete any records belonging to this pid also (there shouldn't be any).
143 ****************************************************************************/
144
145 static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
146 {
147         struct lock_struct *locks;
148         int count, i;
149         BOOL check_self = *(BOOL *)state;
150         pid_t mypid = sys_getpid();
151
152         tdb_chainlock(tdb, kbuf);
153
154         locks = (struct lock_struct *)dbuf.dptr;
155
156         count = dbuf.dsize / sizeof(*locks);
157         for (i=0; i<count; i++) {
158                 struct lock_struct *lock = &locks[i];
159
160                 /* If check_self is true we want to remove our own records. */
161                 if (check_self && (mypid == lock->context.pid)) {
162
163                         DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n",
164                                         (unsigned int)lock->context.pid ));
165
166                 } else if (process_exists(lock->context.pid)) {
167
168                         DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock->context.pid ));
169                         continue;
170                 }
171
172                 DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n",
173                                 (unsigned int)lock->context.pid ));
174
175                 if (count > 1 && i < count-1) {
176                         memmove(&locks[i], &locks[i+1], 
177                                 sizeof(*locks)*((count-1) - i));
178                 }
179                 count--;
180                 i--;
181         }
182
183         if (count == 0) {
184                 tdb_delete(tdb, kbuf);
185         } else if (count < (dbuf.dsize / sizeof(*locks))) {
186                 dbuf.dsize = count * sizeof(*locks);
187                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
188         }
189
190         tdb_chainunlock(tdb, kbuf);
191         return 0;
192 }
193
194 /****************************************************************************
195  Open up the brlock.tdb database.
196 ****************************************************************************/
197
198 void brl_init(int read_only)
199 {
200         BOOL check_self = False;
201
202         if (tdb)
203                 return;
204         tdb = tdb_open_log(lock_path("brlock.tdb"), 0, TDB_CLEAR_IF_FIRST|USE_TDB_MMAP_FLAG, 
205                        read_only?O_RDONLY:(O_RDWR|O_CREAT), 0644);
206         if (!tdb) {
207                 DEBUG(0,("Failed to open byte range locking database\n"));
208                 return;
209         }
210
211         /* delete any dead locks */
212         if (!read_only)
213                 tdb_traverse(tdb, delete_fn, &check_self);
214 }
215
216 /****************************************************************************
217  Close down the brlock.tdb database.
218 ****************************************************************************/
219
220 void brl_shutdown(int read_only)
221 {
222         BOOL check_self = True;
223
224         if (!tdb)
225                 return;
226
227         /* delete any dead locks */
228         if (!read_only)
229                 tdb_traverse(tdb, delete_fn, &check_self);
230
231         tdb_close(tdb);
232 }
233
234 /****************************************************************************
235  Lock a range of bytes.
236 ****************************************************************************/
237
238 BOOL brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
239               uint16 smbpid, pid_t pid, uint16 tid,
240               br_off start, br_off size, 
241               enum brl_type lock_type)
242 {
243         TDB_DATA kbuf, dbuf;
244         int count, i;
245         struct lock_struct lock, *locks;
246         char *tp;
247
248         kbuf = locking_key(dev,ino);
249
250         dbuf.dptr = NULL;
251
252 #if 0
253         if (start == 0 && size == 0) {
254                 tdb_delete(tdb, kbuf);
255                 return True;
256         }
257 #endif
258
259         tdb_chainlock(tdb, kbuf);
260         dbuf = tdb_fetch(tdb, kbuf);
261
262         lock.context.smbpid = smbpid;
263         lock.context.pid = pid;
264         lock.context.tid = tid;
265         lock.start = start;
266         lock.size = size;
267         lock.fnum = fnum;
268         lock.lock_type = lock_type;
269
270         if (dbuf.dptr) {
271                 /* there are existing locks - make sure they don't conflict */
272                 locks = (struct lock_struct *)dbuf.dptr;
273                 count = dbuf.dsize / sizeof(*locks);
274                 for (i=0; i<count; i++) {
275                         if (brl_conflict(&locks[i], &lock)) {
276                                 goto fail;
277                         }
278                 }
279         }
280
281 #if 0
282         if (start == 0 && size == 0) {
283                 if (dbuf.dptr) free(dbuf.dptr);
284                 tdb_chainunlock(tdb, kbuf);             
285                 return True;
286         }
287 #endif
288
289         /* no conflicts - add it to the list of locks */
290         tp = Realloc(dbuf.dptr, dbuf.dsize + sizeof(*locks));
291         if (!tp) goto fail;
292         else dbuf.dptr = tp;
293         memcpy(dbuf.dptr + dbuf.dsize, &lock, sizeof(lock));
294         dbuf.dsize += sizeof(lock);
295         tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
296
297         free(dbuf.dptr);
298         tdb_chainunlock(tdb, kbuf);
299         return True;
300
301  fail:
302         if (dbuf.dptr) free(dbuf.dptr);
303         tdb_chainunlock(tdb, kbuf);
304         return False;
305 }
306
307 /****************************************************************************
308  Unlock a range of bytes.
309 ****************************************************************************/
310
311 BOOL brl_unlock(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
312                 uint16 smbpid, pid_t pid, uint16 tid,
313                 br_off start, br_off size)
314 {
315         TDB_DATA kbuf, dbuf;
316         int count, i;
317         struct lock_struct *locks;
318         struct lock_context context;
319
320         kbuf = locking_key(dev,ino);
321
322         dbuf.dptr = NULL;
323
324         tdb_chainlock(tdb, kbuf);
325         dbuf = tdb_fetch(tdb, kbuf);
326
327         if (!dbuf.dptr) {
328                 DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
329                 goto fail;
330         }
331
332         context.smbpid = smbpid;
333         context.pid = pid;
334         context.tid = tid;
335
336         /* there are existing locks - find a match */
337         locks = (struct lock_struct *)dbuf.dptr;
338         count = dbuf.dsize / sizeof(*locks);
339
340         for (i=0; i<count; i++) {
341                 struct lock_struct *lock = &locks[i];
342
343                 if (lock->lock_type == WRITE_LOCK &&
344                     brl_same_context(&lock->context, &context) &&
345                     lock->fnum == fnum &&
346                     lock->start == start &&
347                     lock->size == size) {
348                         /* found it - delete it */
349                         if (count == 1) {
350                                 tdb_delete(tdb, kbuf);
351                         } else {
352                                 if (i < count-1) {
353                                         memmove(&locks[i], &locks[i+1], 
354                                                 sizeof(*locks)*((count-1) - i));
355                                 }
356                                 dbuf.dsize -= sizeof(*locks);
357                                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
358                         }
359
360                         free(dbuf.dptr);
361                         tdb_chainunlock(tdb, kbuf);
362                         return True;
363                 }
364         }
365
366         locks = (struct lock_struct *)dbuf.dptr;
367         count = dbuf.dsize / sizeof(*locks);
368         for (i=0; i<count; i++) {
369                 struct lock_struct *lock = &locks[i];
370
371                 if (brl_same_context(&lock->context, &context) &&
372                     lock->fnum == fnum &&
373                     lock->start == start &&
374                     lock->size == size) {
375                         /* found it - delete it */
376                         if (count == 1) {
377                                 tdb_delete(tdb, kbuf);
378                         } else {
379                                 if (i < count-1) {
380                                         memmove(&locks[i], &locks[i+1], 
381                                                 sizeof(*locks)*((count-1) - i));
382                                 }
383                                 dbuf.dsize -= sizeof(*locks);
384                                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
385                         }
386
387                         free(dbuf.dptr);
388                         tdb_chainunlock(tdb, kbuf);
389                         return True;
390                 }
391         }
392
393         /* we didn't find it */
394
395  fail:
396         if (dbuf.dptr) free(dbuf.dptr);
397         tdb_chainunlock(tdb, kbuf);
398         return False;
399 }
400
401
402 /****************************************************************************
403  Test if we could add a lock if we wanted to.
404 ****************************************************************************/
405
406 BOOL brl_locktest(SMB_DEV_T dev, SMB_INO_T ino, int fnum,
407                   uint16 smbpid, pid_t pid, uint16 tid,
408                   br_off start, br_off size, 
409                   enum brl_type lock_type, int check_self)
410 {
411         TDB_DATA kbuf, dbuf;
412         int count, i;
413         struct lock_struct lock, *locks;
414
415         kbuf = locking_key(dev,ino);
416
417         dbuf.dptr = NULL;
418
419         tdb_chainlock(tdb, kbuf);
420         dbuf = tdb_fetch(tdb, kbuf);
421
422         lock.context.smbpid = smbpid;
423         lock.context.pid = pid;
424         lock.context.tid = tid;
425         lock.start = start;
426         lock.size = size;
427         lock.fnum = fnum;
428         lock.lock_type = lock_type;
429
430         if (dbuf.dptr) {
431                 /* there are existing locks - make sure they don't conflict */
432                 locks = (struct lock_struct *)dbuf.dptr;
433                 count = dbuf.dsize / sizeof(*locks);
434                 for (i=0; i<count; i++) {
435                         if (check_self) {
436                                 if (brl_conflict(&locks[i], &lock))
437                                         goto fail;
438                         } else {
439                                 /*
440                                  * Our own locks don't conflict.
441                                  */
442                                 if (brl_conflict_other(&locks[i], &lock))
443                                         goto fail;
444                         }
445                 }
446         }
447
448         /* no conflicts - we could have added it */
449         free(dbuf.dptr);
450         tdb_chainunlock(tdb, kbuf);
451         return True;
452
453  fail:
454         if (dbuf.dptr) free(dbuf.dptr);
455         tdb_chainunlock(tdb, kbuf);
456         return False;
457 }
458
459 /****************************************************************************
460  Remove any locks associated with a open file.
461 ****************************************************************************/
462
463 void brl_close(SMB_DEV_T dev, SMB_INO_T ino, pid_t pid, int tid, int fnum)
464 {
465         TDB_DATA kbuf, dbuf;
466         int count, i, dcount=0;
467         struct lock_struct *locks;
468
469         kbuf = locking_key(dev,ino);
470
471         dbuf.dptr = NULL;
472
473         tdb_chainlock(tdb, kbuf);
474         dbuf = tdb_fetch(tdb, kbuf);
475
476         if (!dbuf.dptr) goto fail;
477
478         /* there are existing locks - remove any for this fnum */
479         locks = (struct lock_struct *)dbuf.dptr;
480         count = dbuf.dsize / sizeof(*locks);
481         for (i=0; i<count; i++) {
482                 struct lock_struct *lock = &locks[i];
483
484                 if (lock->context.tid == tid &&
485                     lock->context.pid == pid &&
486                     lock->fnum == fnum) {
487                         /* found it - delete it */
488                         if (count > 1 && i < count-1) {
489                                 memmove(&locks[i], &locks[i+1], 
490                                         sizeof(*locks)*((count-1) - i));
491                         }
492                         count--;
493                         i--;
494                         dcount++;
495                 }
496         }
497
498         if (count == 0) {
499                 tdb_delete(tdb, kbuf);
500         } else if (count < (dbuf.dsize / sizeof(*locks))) {
501                 dbuf.dsize -= dcount * sizeof(*locks);
502                 tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
503         }
504
505         /* we didn't find it */
506  fail:
507         if (dbuf.dptr) free(dbuf.dptr);
508         tdb_chainunlock(tdb, kbuf);
509 }
510
511 /****************************************************************************
512  Traverse the whole database with this function, calling traverse_callback
513  on each lock.
514 ****************************************************************************/
515
516 static int traverse_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
517 {
518         struct lock_struct *locks;
519         struct lock_key *key;
520         int i;
521
522         BRLOCK_FN(traverse_callback) = (BRLOCK_FN_CAST())state;
523
524         locks = (struct lock_struct *)dbuf.dptr;
525         key = (struct lock_key *)kbuf.dptr;
526
527         for (i=0;i<dbuf.dsize/sizeof(*locks);i++) {
528                 traverse_callback(key->device, key->inode,
529                                   locks[i].context.pid,
530                                   locks[i].lock_type,
531                                   locks[i].start,
532                                   locks[i].size);
533         }
534         return 0;
535 }
536
537 /*******************************************************************
538  Call the specified function on each lock in the database.
539 ********************************************************************/
540
541 int brl_forall(BRLOCK_FN(fn))
542 {
543         if (!tdb) return 0;
544         return tdb_traverse(tdb, traverse_fn, (void *)fn);
545 }