2 Unix SMB/CIFS implementation.
3 byte range locking code
4 Updated to handle range splits/merges.
6 Copyright (C) Andrew Tridgell 1992-2000
7 Copyright (C) Jeremy Allison 1992-2000
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 /* This module implements a tdb based byte range locking service,
25 replacing the fcntl() based byte range locking previously
26 used. This allows us to provide the same semantics as NT */
32 /* This contains elements that differentiate locks. The smbpid is a
33 client supplied pid, and is essentially the locking context for
42 /* The data in brlock records is an unsorted linear array of these
43 records. It is unnecessary to store the count as tdb provides the
47 struct lock_context context
;
51 enum brl_type lock_type
;
54 /* The key used in the brlock database. */
61 /* The open brlock.tdb database. */
63 static TDB_CONTEXT
*tdb
;
65 /****************************************************************************
66 Create a locking key - ensuring zero filled for pad purposes.
67 ****************************************************************************/
69 static TDB_DATA
locking_key(SMB_DEV_T dev
, SMB_INO_T inode
)
71 static struct lock_key key
;
74 memset(&key
, '\0', sizeof(key
));
77 kbuf
.dptr
= (char *)&key
;
78 kbuf
.dsize
= sizeof(key
);
82 /****************************************************************************
83 See if two locking contexts are equal.
84 ****************************************************************************/
86 static BOOL
brl_same_context(struct lock_context
*ctx1
,
87 struct lock_context
*ctx2
)
89 return (ctx1
->pid
== ctx2
->pid
) &&
90 (ctx1
->smbpid
== ctx2
->smbpid
) &&
91 (ctx1
->tid
== ctx2
->tid
);
94 /****************************************************************************
95 See if lock2 can be added when lock1 is in place.
96 ****************************************************************************/
98 static BOOL
brl_conflict(struct lock_struct
*lck1
,
99 struct lock_struct
*lck2
)
101 if (lck1
->lock_type
== READ_LOCK
&& lck2
->lock_type
== READ_LOCK
) {
105 if (brl_same_context(&lck1
->context
, &lck2
->context
) &&
106 lck2
->lock_type
== READ_LOCK
&& lck1
->fnum
== lck2
->fnum
) {
110 if (lck1
->start
>= (lck2
->start
+ lck2
->size
) ||
111 lck2
->start
>= (lck1
->start
+ lck1
->size
)) {
119 static BOOL
brl_conflict1(struct lock_struct
*lck1
,
120 struct lock_struct
*lck2
)
122 if (lck1
->lock_type
== READ_LOCK
&& lck2
->lock_type
== READ_LOCK
) {
126 if (brl_same_context(&lck1
->context
, &lck2
->context
) &&
127 lck2
->lock_type
== READ_LOCK
&& lck1
->fnum
== lck2
->fnum
) {
131 if (lck2
->start
== 0 && lck2
->size
== 0 && lck1
->size
!= 0) {
135 if (lck1
->start
>= (lck2
->start
+ lck2
->size
) ||
136 lck2
->start
>= (lck1
->start
+ lck1
->size
)) {
144 /****************************************************************************
145 Check to see if this lock conflicts, but ignore our own locks on the
147 ****************************************************************************/
149 static BOOL
brl_conflict_other(struct lock_struct
*lck1
, struct lock_struct
*lck2
)
151 if (lck1
->lock_type
== READ_LOCK
&& lck2
->lock_type
== READ_LOCK
)
154 if (brl_same_context(&lck1
->context
, &lck2
->context
) &&
155 lck1
->fnum
== lck2
->fnum
)
158 if (lck1
->start
>= (lck2
->start
+ lck2
->size
) ||
159 lck2
->start
>= (lck1
->start
+ lck1
->size
)) return False
;
166 /* doing this traversal could kill solaris machines under high load (tridge) */
167 /* delete any dead locks */
169 /****************************************************************************
170 Delete a record if it is for a dead process, if check_self is true, then
171 delete any records belonging to this pid also (there shouldn't be any).
172 ****************************************************************************/
174 static int delete_fn(TDB_CONTEXT
*ttdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
176 struct lock_struct
*locks
;
178 BOOL check_self
= *(BOOL
*)state
;
179 pid_t mypid
= sys_getpid();
181 tdb_chainlock(tdb
, kbuf
);
183 locks
= (struct lock_struct
*)dbuf
.dptr
;
185 count
= dbuf
.dsize
/ sizeof(*locks
);
186 for (i
=0; i
<count
; i
++) {
187 struct lock_struct
*lock
= &locks
[i
];
189 /* If check_self is true we want to remove our own records. */
190 if (check_self
&& (mypid
== lock
->context
.pid
)) {
192 DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n",
193 (unsigned int)lock
->context
.pid
));
195 } else if (process_exists(lock
->context
.pid
)) {
197 DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock
->context
.pid
));
201 DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n",
202 (unsigned int)lock
->context
.pid
));
204 if (count
> 1 && i
< count
-1) {
205 memmove(&locks
[i
], &locks
[i
+1],
206 sizeof(*locks
)*((count
-1) - i
));
213 tdb_delete(tdb
, kbuf
);
214 } else if (count
< (dbuf
.dsize
/ sizeof(*locks
))) {
215 dbuf
.dsize
= count
* sizeof(*locks
);
216 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
219 tdb_chainunlock(tdb
, kbuf
);
224 /****************************************************************************
225 Open up the brlock.tdb database.
226 ****************************************************************************/
228 void brl_init(int read_only
)
232 tdb
= tdb_open_log(lock_path("brlock.tdb"), 0, TDB_DEFAULT
|(read_only
?0x0:TDB_CLEAR_IF_FIRST
),
233 read_only
?O_RDONLY
:(O_RDWR
|O_CREAT
), 0644);
235 DEBUG(0,("Failed to open byte range locking database\n"));
240 /* doing this traversal could kill solaris machines under high load (tridge) */
241 /* delete any dead locks */
243 BOOL check_self
= False
;
244 tdb_traverse(tdb
, delete_fn
, &check_self
);
249 /****************************************************************************
250 Close down the brlock.tdb database.
251 ****************************************************************************/
253 void brl_shutdown(int read_only
)
259 /* doing this traversal could kill solaris machines under high load (tridge) */
260 /* delete any dead locks */
262 BOOL check_self
= True
;
263 tdb_traverse(tdb
, delete_fn
, &check_self
);
271 /****************************************************************************
272 compare two locks for sorting
273 ****************************************************************************/
274 static int lock_compare(struct lock_struct
*lck1
,
275 struct lock_struct
*lck2
)
277 if (lck1
->start
!= lck2
->start
) return (lck1
->start
- lck2
->start
);
278 if (lck2
->size
!= lck1
->size
) {
279 return ((int)lck1
->size
- (int)lck2
->size
);
285 /****************************************************************************
286 Lock a range of bytes.
287 ****************************************************************************/
289 NTSTATUS
brl_lock(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
290 uint16 smbpid
, pid_t pid
, uint16 tid
,
291 br_off start
, br_off size
,
292 enum brl_type lock_type
)
296 struct lock_struct lock
, *locks
;
298 NTSTATUS status
= NT_STATUS_OK
;
299 static int last_failed
= -1;
300 static br_off last_failed_start
;
302 kbuf
= locking_key(dev
,ino
);
307 if (start
== 0 && size
== 0) {
308 DEBUG(0,("client sent 0/0 lock - please report this\n"));
312 tdb_chainlock(tdb
, kbuf
);
313 dbuf
= tdb_fetch(tdb
, kbuf
);
315 lock
.context
.smbpid
= smbpid
;
316 lock
.context
.pid
= pid
;
317 lock
.context
.tid
= tid
;
321 lock
.lock_type
= lock_type
;
324 /* there are existing locks - make sure they don't conflict */
325 locks
= (struct lock_struct
*)dbuf
.dptr
;
326 count
= dbuf
.dsize
/ sizeof(*locks
);
327 for (i
=0; i
<count
; i
++) {
328 if (brl_conflict(&locks
[i
], &lock
)) {
329 status
= NT_STATUS_LOCK_NOT_GRANTED
;
333 if (lock
.start
== 0 && lock
.size
== 0 &&
334 locks
[i
].size
== 0) {
341 /* no conflicts - add it to the list of locks */
342 tp
= Realloc(dbuf
.dptr
, dbuf
.dsize
+ sizeof(*locks
));
344 status
= NT_STATUS_NO_MEMORY
;
349 memcpy(dbuf
.dptr
+ dbuf
.dsize
, &lock
, sizeof(lock
));
350 dbuf
.dsize
+= sizeof(lock
);
353 /* sort the lock list */
354 qsort(dbuf
.dptr
, dbuf
.dsize
/sizeof(lock
), sizeof(lock
), lock_compare
);
357 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
359 SAFE_FREE(dbuf
.dptr
);
360 tdb_chainunlock(tdb
, kbuf
);
364 /* this is a nasty hack to try to simulate the lock result cache code in w2k.
365 It isn't completely accurate as I haven't yet worked out the correct
368 if (last_failed
== fnum
&&
369 last_failed_start
== start
&&
370 NT_STATUS_EQUAL(status
, NT_STATUS_LOCK_NOT_GRANTED
)) {
371 status
= NT_STATUS_FILE_LOCK_CONFLICT
;
374 last_failed_start
= start
;
376 SAFE_FREE(dbuf
.dptr
);
377 tdb_chainunlock(tdb
, kbuf
);
381 /****************************************************************************
382 Unlock a range of bytes.
383 ****************************************************************************/
385 BOOL
brl_unlock(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
386 uint16 smbpid
, pid_t pid
, uint16 tid
,
387 br_off start
, br_off size
)
391 struct lock_struct
*locks
;
392 struct lock_context context
;
394 kbuf
= locking_key(dev
,ino
);
398 tdb_chainlock(tdb
, kbuf
);
399 dbuf
= tdb_fetch(tdb
, kbuf
);
402 DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
406 context
.smbpid
= smbpid
;
410 /* there are existing locks - find a match */
411 locks
= (struct lock_struct
*)dbuf
.dptr
;
412 count
= dbuf
.dsize
/ sizeof(*locks
);
415 for (i
=0; i
<count
; i
++) {
416 struct lock_struct
*lock
= &locks
[i
];
418 if (lock
->lock_type
== WRITE_LOCK
&&
419 brl_same_context(&lock
->context
, &context
) &&
420 lock
->fnum
== fnum
&&
421 lock
->start
== start
&&
422 lock
->size
== size
) {
423 /* found it - delete it */
425 tdb_delete(tdb
, kbuf
);
428 memmove(&locks
[i
], &locks
[i
+1],
429 sizeof(*locks
)*((count
-1) - i
));
431 dbuf
.dsize
-= sizeof(*locks
);
432 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
435 SAFE_FREE(dbuf
.dptr
);
436 tdb_chainunlock(tdb
, kbuf
);
442 locks
= (struct lock_struct
*)dbuf
.dptr
;
443 count
= dbuf
.dsize
/ sizeof(*locks
);
444 for (i
=0; i
<count
; i
++) {
445 struct lock_struct
*lock
= &locks
[i
];
447 if (brl_same_context(&lock
->context
, &context
) &&
448 lock
->fnum
== fnum
&&
449 lock
->start
== start
&&
450 lock
->size
== size
) {
451 /* found it - delete it */
453 tdb_delete(tdb
, kbuf
);
456 memmove(&locks
[i
], &locks
[i
+1],
457 sizeof(*locks
)*((count
-1) - i
));
459 dbuf
.dsize
-= sizeof(*locks
);
460 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
463 SAFE_FREE(dbuf
.dptr
);
464 tdb_chainunlock(tdb
, kbuf
);
469 /* we didn't find it */
472 SAFE_FREE(dbuf
.dptr
);
473 tdb_chainunlock(tdb
, kbuf
);
478 /****************************************************************************
479 Test if we could add a lock if we wanted to.
480 ****************************************************************************/
482 BOOL
brl_locktest(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
483 uint16 smbpid
, pid_t pid
, uint16 tid
,
484 br_off start
, br_off size
,
485 enum brl_type lock_type
, int check_self
)
489 struct lock_struct lock
, *locks
;
491 kbuf
= locking_key(dev
,ino
);
495 tdb_chainlock(tdb
, kbuf
);
496 dbuf
= tdb_fetch(tdb
, kbuf
);
498 lock
.context
.smbpid
= smbpid
;
499 lock
.context
.pid
= pid
;
500 lock
.context
.tid
= tid
;
504 lock
.lock_type
= lock_type
;
507 /* there are existing locks - make sure they don't conflict */
508 locks
= (struct lock_struct
*)dbuf
.dptr
;
509 count
= dbuf
.dsize
/ sizeof(*locks
);
510 for (i
=0; i
<count
; i
++) {
512 if (brl_conflict(&locks
[i
], &lock
))
516 * Our own locks don't conflict.
518 if (brl_conflict_other(&locks
[i
], &lock
))
524 /* no conflicts - we could have added it */
525 SAFE_FREE(dbuf
.dptr
);
526 tdb_chainunlock(tdb
, kbuf
);
530 SAFE_FREE(dbuf
.dptr
);
531 tdb_chainunlock(tdb
, kbuf
);
535 /****************************************************************************
536 Remove any locks associated with a open file.
537 ****************************************************************************/
539 void brl_close(SMB_DEV_T dev
, SMB_INO_T ino
, pid_t pid
, int tid
, int fnum
)
542 int count
, i
, dcount
=0;
543 struct lock_struct
*locks
;
545 kbuf
= locking_key(dev
,ino
);
549 tdb_chainlock(tdb
, kbuf
);
550 dbuf
= tdb_fetch(tdb
, kbuf
);
552 if (!dbuf
.dptr
) goto fail
;
554 /* there are existing locks - remove any for this fnum */
555 locks
= (struct lock_struct
*)dbuf
.dptr
;
556 count
= dbuf
.dsize
/ sizeof(*locks
);
557 for (i
=0; i
<count
; i
++) {
558 struct lock_struct
*lock
= &locks
[i
];
560 if (lock
->context
.tid
== tid
&&
561 lock
->context
.pid
== pid
&&
562 lock
->fnum
== fnum
) {
563 /* found it - delete it */
564 if (count
> 1 && i
< count
-1) {
565 memmove(&locks
[i
], &locks
[i
+1],
566 sizeof(*locks
)*((count
-1) - i
));
575 tdb_delete(tdb
, kbuf
);
576 } else if (count
< (dbuf
.dsize
/ sizeof(*locks
))) {
577 dbuf
.dsize
-= dcount
* sizeof(*locks
);
578 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
581 /* we didn't find it */
583 SAFE_FREE(dbuf
.dptr
);
584 tdb_chainunlock(tdb
, kbuf
);
587 /****************************************************************************
588 Traverse the whole database with this function, calling traverse_callback
590 ****************************************************************************/
592 static int traverse_fn(TDB_CONTEXT
*ttdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
594 struct lock_struct
*locks
;
595 struct lock_key
*key
;
598 BRLOCK_FN(traverse_callback
) = (BRLOCK_FN_CAST())state
;
600 locks
= (struct lock_struct
*)dbuf
.dptr
;
601 key
= (struct lock_key
*)kbuf
.dptr
;
603 for (i
=0;i
<dbuf
.dsize
/sizeof(*locks
);i
++) {
604 traverse_callback(key
->device
, key
->inode
,
605 locks
[i
].context
.pid
,
613 /*******************************************************************
614 Call the specified function on each lock in the database.
615 ********************************************************************/
617 int brl_forall(BRLOCK_FN(fn
))
620 return tdb_traverse(tdb
, traverse_fn
, (void *)fn
);