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 lck1 and lck2 overlap.
96 ****************************************************************************/
98 static BOOL
brl_overlap(struct lock_struct
*lck1
,
99 struct lock_struct
*lck2
)
101 /* this extra check is not redundent - it copes with locks
102 that go beyond the end of 64 bit file space */
103 if (lck1
->size
!= 0 &&
104 lck1
->start
== lck2
->start
&&
105 lck1
->size
== lck2
->size
) {
109 if (lck1
->start
>= (lck2
->start
+lck2
->size
) ||
110 lck2
->start
>= (lck1
->start
+lck1
->size
)) {
116 /****************************************************************************
117 See if lock2 can be added when lock1 is in place.
118 ****************************************************************************/
120 static BOOL
brl_conflict(struct lock_struct
*lck1
,
121 struct lock_struct
*lck2
)
123 if (lck1
->lock_type
== PENDING_LOCK
|| lck2
->lock_type
== PENDING_LOCK
)
126 if (lck1
->lock_type
== READ_LOCK
&& lck2
->lock_type
== READ_LOCK
) {
130 if (brl_same_context(&lck1
->context
, &lck2
->context
) &&
131 lck2
->lock_type
== READ_LOCK
&& lck1
->fnum
== lck2
->fnum
) {
135 if (lck1
->start
>= (lck2
->start
+ lck2
->size
) ||
136 lck2
->start
>= (lck1
->start
+ lck1
->size
)) {
140 return brl_overlap(lck1
, lck2
);
144 static BOOL
brl_conflict1(struct lock_struct
*lck1
,
145 struct lock_struct
*lck2
)
147 if (lck1
->lock_type
== PENDING_LOCK
|| lck2
->lock_type
== PENDING_LOCK
)
150 if (lck1
->lock_type
== READ_LOCK
&& lck2
->lock_type
== READ_LOCK
) {
154 if (brl_same_context(&lck1
->context
, &lck2
->context
) &&
155 lck2
->lock_type
== READ_LOCK
&& lck1
->fnum
== lck2
->fnum
) {
159 if (lck2
->start
== 0 && lck2
->size
== 0 && lck1
->size
!= 0) {
163 if (lck1
->start
>= (lck2
->start
+ lck2
->size
) ||
164 lck2
->start
>= (lck1
->start
+ lck1
->size
)) {
172 /****************************************************************************
173 Check to see if this lock conflicts, but ignore our own locks on the
175 ****************************************************************************/
177 static BOOL
brl_conflict_other(struct lock_struct
*lck1
, struct lock_struct
*lck2
)
179 if (lck1
->lock_type
== PENDING_LOCK
|| lck2
->lock_type
== PENDING_LOCK
)
182 if (lck1
->lock_type
== READ_LOCK
&& lck2
->lock_type
== READ_LOCK
)
186 * Incoming WRITE locks conflict with existing READ locks even
187 * if the context is the same. JRA. See LOCKTEST7 in smbtorture.
190 if (!(lck2
->lock_type
== WRITE_LOCK
&& lck1
->lock_type
== READ_LOCK
)) {
191 if (brl_same_context(&lck1
->context
, &lck2
->context
) &&
192 lck1
->fnum
== lck2
->fnum
)
196 if (lck1
->start
>= (lck2
->start
+ lck2
->size
) ||
197 lck2
->start
>= (lck1
->start
+ lck1
->size
))
200 return brl_overlap(lck1
, lck2
);
203 /****************************************************************************
204 Amazingly enough, w2k3 "remembers" whether the last lock failure
205 is the same as this one and changes its error code. I wonder if any
206 app depends on this ?
207 ****************************************************************************/
209 static NTSTATUS
brl_lock_failed(struct lock_struct
*lock
)
211 static struct lock_struct last_lock_failure
;
213 if (brl_same_context(&lock
->context
, &last_lock_failure
.context
) &&
214 lock
->fnum
== last_lock_failure
.fnum
&&
215 lock
->start
== last_lock_failure
.start
&&
216 lock
->size
== last_lock_failure
.size
) {
217 return NT_STATUS_FILE_LOCK_CONFLICT
;
219 last_lock_failure
= *lock
;
220 if (lock
->start
>= 0xEF000000 &&
221 (lock
->start
>> 63) == 0) {
222 /* amazing the little things you learn with a test
223 suite. Locks beyond this offset (as a 64 bit
224 number!) always generate the conflict error code,
225 unless the top bit is set */
226 return NT_STATUS_FILE_LOCK_CONFLICT
;
228 return NT_STATUS_LOCK_NOT_GRANTED
;
232 /* doing this traversal could kill solaris machines under high load (tridge) */
233 /* delete any dead locks */
235 /****************************************************************************
236 Delete a record if it is for a dead process, if check_self is true, then
237 delete any records belonging to this pid also (there shouldn't be any).
238 ****************************************************************************/
240 static int delete_fn(TDB_CONTEXT
*ttdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
242 struct lock_struct
*locks
;
244 BOOL check_self
= *(BOOL
*)state
;
245 pid_t mypid
= sys_getpid();
247 tdb_chainlock(tdb
, kbuf
);
249 locks
= (struct lock_struct
*)dbuf
.dptr
;
251 count
= dbuf
.dsize
/ sizeof(*locks
);
252 for (i
=0; i
<count
; i
++) {
253 struct lock_struct
*lock
= &locks
[i
];
255 /* If check_self is true we want to remove our own records. */
256 if (check_self
&& (mypid
== lock
->context
.pid
)) {
258 DEBUG(0,("brlock : delete_fn. LOGIC ERROR ! Shutting down and a record for my pid (%u) exists !\n",
259 (unsigned int)lock
->context
.pid
));
261 } else if (process_exists(lock
->context
.pid
)) {
263 DEBUG(10,("brlock : delete_fn. pid %u exists.\n", (unsigned int)lock
->context
.pid
));
267 DEBUG(10,("brlock : delete_fn. Deleting record for process %u\n",
268 (unsigned int)lock
->context
.pid
));
270 if (count
> 1 && i
< count
-1) {
271 memmove(&locks
[i
], &locks
[i
+1],
272 sizeof(*locks
)*((count
-1) - i
));
279 tdb_delete(tdb
, kbuf
);
280 } else if (count
< (dbuf
.dsize
/ sizeof(*locks
))) {
281 dbuf
.dsize
= count
* sizeof(*locks
);
282 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
285 tdb_chainunlock(tdb
, kbuf
);
290 /****************************************************************************
291 Open up the brlock.tdb database.
292 ****************************************************************************/
294 void brl_init(int read_only
)
298 tdb
= tdb_open_log(lock_path("brlock.tdb"), 0, TDB_DEFAULT
|(read_only
?0x0:TDB_CLEAR_IF_FIRST
),
299 read_only
?O_RDONLY
:(O_RDWR
|O_CREAT
), 0644 );
301 DEBUG(0,("Failed to open byte range locking database\n"));
306 /* doing this traversal could kill solaris machines under high load (tridge) */
307 /* delete any dead locks */
309 BOOL check_self
= False
;
310 tdb_traverse(tdb
, delete_fn
, &check_self
);
315 /****************************************************************************
316 Close down the brlock.tdb database.
317 ****************************************************************************/
319 void brl_shutdown(int read_only
)
325 /* doing this traversal could kill solaris machines under high load (tridge) */
326 /* delete any dead locks */
328 BOOL check_self
= True
;
329 tdb_traverse(tdb
, delete_fn
, &check_self
);
337 /****************************************************************************
338 compare two locks for sorting
339 ****************************************************************************/
340 static int lock_compare(struct lock_struct
*lck1
,
341 struct lock_struct
*lck2
)
343 if (lck1
->start
!= lck2
->start
) return (lck1
->start
- lck2
->start
);
344 if (lck2
->size
!= lck1
->size
) {
345 return ((int)lck1
->size
- (int)lck2
->size
);
351 /****************************************************************************
352 Lock a range of bytes.
353 ****************************************************************************/
355 NTSTATUS
brl_lock(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
356 uint16 smbpid
, pid_t pid
, uint16 tid
,
357 br_off start
, br_off size
,
358 enum brl_type lock_type
, BOOL
*my_lock_ctx
)
362 struct lock_struct lock
, *locks
;
364 NTSTATUS status
= NT_STATUS_OK
;
366 *my_lock_ctx
= False
;
367 kbuf
= locking_key(dev
,ino
);
372 if (start
== 0 && size
== 0) {
373 DEBUG(0,("client sent 0/0 lock - please report this\n"));
377 tdb_chainlock(tdb
, kbuf
);
378 dbuf
= tdb_fetch(tdb
, kbuf
);
380 lock
.context
.smbpid
= smbpid
;
381 lock
.context
.pid
= pid
;
382 lock
.context
.tid
= tid
;
386 lock
.lock_type
= lock_type
;
389 /* there are existing locks - make sure they don't conflict */
390 locks
= (struct lock_struct
*)dbuf
.dptr
;
391 count
= dbuf
.dsize
/ sizeof(*locks
);
392 for (i
=0; i
<count
; i
++) {
393 if (brl_conflict(&locks
[i
], &lock
)) {
394 status
= brl_lock_failed(&lock
);;
395 /* Did we block ourselves ? */
396 if (brl_same_context(&locks
[i
].context
, &lock
.context
))
401 if (lock
.start
== 0 && lock
.size
== 0 &&
402 locks
[i
].size
== 0) {
409 /* no conflicts - add it to the list of locks */
410 tp
= Realloc(dbuf
.dptr
, dbuf
.dsize
+ sizeof(*locks
));
412 status
= NT_STATUS_NO_MEMORY
;
417 memcpy(dbuf
.dptr
+ dbuf
.dsize
, &lock
, sizeof(lock
));
418 dbuf
.dsize
+= sizeof(lock
);
421 /* sort the lock list */
422 qsort(dbuf
.dptr
, dbuf
.dsize
/sizeof(lock
), sizeof(lock
), lock_compare
);
425 if (tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
) != 0) {
426 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
430 SAFE_FREE(dbuf
.dptr
);
431 tdb_chainunlock(tdb
, kbuf
);
436 SAFE_FREE(dbuf
.dptr
);
437 tdb_chainunlock(tdb
, kbuf
);
441 /****************************************************************************
442 Check if an unlock overlaps a pending lock.
443 ****************************************************************************/
445 static BOOL
brl_pending_overlap(struct lock_struct
*lock
, struct lock_struct
*pend_lock
)
447 if ((lock
->start
<= pend_lock
->start
) && (lock
->start
+ lock
->size
> pend_lock
->start
))
449 if ((lock
->start
>= pend_lock
->start
) && (lock
->start
<= pend_lock
->start
+ pend_lock
->size
))
454 /****************************************************************************
455 Unlock a range of bytes.
456 ****************************************************************************/
458 BOOL
brl_unlock(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
459 uint16 smbpid
, pid_t pid
, uint16 tid
,
460 br_off start
, br_off size
,
461 BOOL remove_pending_locks_only
,
462 void (*pre_unlock_fn
)(void *),
463 void *pre_unlock_data
)
467 struct lock_struct
*locks
;
468 struct lock_context context
;
470 kbuf
= locking_key(dev
,ino
);
474 tdb_chainlock(tdb
, kbuf
);
475 dbuf
= tdb_fetch(tdb
, kbuf
);
478 DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
482 context
.smbpid
= smbpid
;
486 /* there are existing locks - find a match */
487 locks
= (struct lock_struct
*)dbuf
.dptr
;
488 count
= dbuf
.dsize
/ sizeof(*locks
);
491 for (i
=0; i
<count
; i
++) {
492 struct lock_struct
*lock
= &locks
[i
];
494 if (lock
->lock_type
== WRITE_LOCK
&&
495 brl_same_context(&lock
->context
, &context
) &&
496 lock
->fnum
== fnum
&&
497 lock
->start
== start
&&
498 lock
->size
== size
) {
501 (*pre_unlock_fn
)(pre_unlock_data
);
503 /* found it - delete it */
505 tdb_delete(tdb
, kbuf
);
508 memmove(&locks
[i
], &locks
[i
+1],
509 sizeof(*locks
)*((count
-1) - i
));
511 dbuf
.dsize
-= sizeof(*locks
);
512 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
515 SAFE_FREE(dbuf
.dptr
);
516 tdb_chainunlock(tdb
, kbuf
);
522 locks
= (struct lock_struct
*)dbuf
.dptr
;
523 count
= dbuf
.dsize
/ sizeof(*locks
);
524 for (i
=0; i
<count
; i
++) {
525 struct lock_struct
*lock
= &locks
[i
];
527 if (brl_same_context(&lock
->context
, &context
) &&
528 lock
->fnum
== fnum
&&
529 lock
->start
== start
&&
530 lock
->size
== size
) {
532 if (remove_pending_locks_only
&& lock
->lock_type
!= PENDING_LOCK
)
535 if (lock
->lock_type
!= PENDING_LOCK
) {
537 /* Do any POSIX unlocks needed. */
539 (*pre_unlock_fn
)(pre_unlock_data
);
541 /* Send unlock messages to any pending waiters that overlap. */
542 for (j
=0; j
<count
; j
++) {
543 struct lock_struct
*pend_lock
= &locks
[j
];
545 /* Ignore non-pending locks. */
546 if (pend_lock
->lock_type
!= PENDING_LOCK
)
549 /* We could send specific lock info here... */
550 if (brl_pending_overlap(lock
, pend_lock
)) {
551 DEBUG(10,("brl_unlock: sending unlock message to pid %u\n",
552 (unsigned int)pend_lock
->context
.pid
));
554 message_send_pid(pend_lock
->context
.pid
,
561 /* found it - delete it */
563 tdb_delete(tdb
, kbuf
);
566 memmove(&locks
[i
], &locks
[i
+1],
567 sizeof(*locks
)*((count
-1) - i
));
569 dbuf
.dsize
-= sizeof(*locks
);
570 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
573 SAFE_FREE(dbuf
.dptr
);
574 tdb_chainunlock(tdb
, kbuf
);
579 /* we didn't find it */
582 SAFE_FREE(dbuf
.dptr
);
583 tdb_chainunlock(tdb
, kbuf
);
588 /****************************************************************************
589 Test if we could add a lock if we wanted to.
590 ****************************************************************************/
592 BOOL
brl_locktest(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
593 uint16 smbpid
, pid_t pid
, uint16 tid
,
594 br_off start
, br_off size
,
595 enum brl_type lock_type
, int check_self
)
599 struct lock_struct lock
, *locks
;
601 kbuf
= locking_key(dev
,ino
);
605 dbuf
= tdb_fetch(tdb
, kbuf
);
607 lock
.context
.smbpid
= smbpid
;
608 lock
.context
.pid
= pid
;
609 lock
.context
.tid
= tid
;
613 lock
.lock_type
= lock_type
;
616 /* there are existing locks - make sure they don't conflict */
617 locks
= (struct lock_struct
*)dbuf
.dptr
;
618 count
= dbuf
.dsize
/ sizeof(*locks
);
619 for (i
=0; i
<count
; i
++) {
621 if (brl_conflict(&locks
[i
], &lock
))
625 * Our own locks don't conflict.
627 if (brl_conflict_other(&locks
[i
], &lock
))
633 /* no conflicts - we could have added it */
634 SAFE_FREE(dbuf
.dptr
);
638 SAFE_FREE(dbuf
.dptr
);
642 /****************************************************************************
643 Remove any locks associated with a open file.
644 ****************************************************************************/
646 void brl_close(SMB_DEV_T dev
, SMB_INO_T ino
, pid_t pid
, int tid
, int fnum
)
649 int count
, i
, j
, dcount
=0;
650 struct lock_struct
*locks
;
652 kbuf
= locking_key(dev
,ino
);
656 tdb_chainlock(tdb
, kbuf
);
657 dbuf
= tdb_fetch(tdb
, kbuf
);
659 if (!dbuf
.dptr
) goto fail
;
661 /* there are existing locks - remove any for this fnum */
662 locks
= (struct lock_struct
*)dbuf
.dptr
;
663 count
= dbuf
.dsize
/ sizeof(*locks
);
665 for (i
=0; i
<count
; i
++) {
666 struct lock_struct
*lock
= &locks
[i
];
668 if (lock
->context
.tid
== tid
&&
669 lock
->context
.pid
== pid
&&
670 lock
->fnum
== fnum
) {
672 /* Send unlock messages to any pending waiters that overlap. */
673 for (j
=0; j
<count
; j
++) {
674 struct lock_struct
*pend_lock
= &locks
[j
];
676 /* Ignore our own or non-pending locks. */
677 if (pend_lock
->lock_type
!= PENDING_LOCK
)
680 if (pend_lock
->context
.tid
== tid
&&
681 pend_lock
->context
.pid
== pid
&&
682 pend_lock
->fnum
== fnum
)
685 /* We could send specific lock info here... */
686 if (brl_pending_overlap(lock
, pend_lock
))
687 message_send_pid(pend_lock
->context
.pid
,
692 /* found it - delete it */
693 if (count
> 1 && i
< count
-1) {
694 memmove(&locks
[i
], &locks
[i
+1],
695 sizeof(*locks
)*((count
-1) - i
));
704 tdb_delete(tdb
, kbuf
);
705 } else if (count
< (dbuf
.dsize
/ sizeof(*locks
))) {
706 dbuf
.dsize
-= dcount
* sizeof(*locks
);
707 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
710 /* we didn't find it */
712 SAFE_FREE(dbuf
.dptr
);
713 tdb_chainunlock(tdb
, kbuf
);
716 /****************************************************************************
717 Traverse the whole database with this function, calling traverse_callback
719 ****************************************************************************/
721 static int traverse_fn(TDB_CONTEXT
*ttdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
723 struct lock_struct
*locks
;
724 struct lock_key
*key
;
727 BRLOCK_FN(traverse_callback
) = (BRLOCK_FN_CAST())state
;
729 locks
= (struct lock_struct
*)dbuf
.dptr
;
730 key
= (struct lock_key
*)kbuf
.dptr
;
732 for (i
=0;i
<dbuf
.dsize
/sizeof(*locks
);i
++) {
733 traverse_callback(key
->device
, key
->inode
,
734 locks
[i
].context
.pid
,
742 /*******************************************************************
743 Call the specified function on each lock in the database.
744 ********************************************************************/
746 int brl_forall(BRLOCK_FN(fn
))
749 return tdb_traverse(tdb
, traverse_fn
, (void *)fn
);