2 Unix SMB/Netbios implementation.
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 */
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
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
48 struct lock_context context
;
52 enum brl_type lock_type
;
55 /* The key used in the brlock database. */
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
;
75 memset(&key
, '\0', sizeof(key
));
78 kbuf
.dptr
= (char *)&key
;
79 kbuf
.dsize
= sizeof(key
);
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
)
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
;
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
;
123 tdb_chainlock(tdb
, kbuf
);
125 locks
= (struct lock_struct
*)dbuf
.dptr
;
127 count
= dbuf
.dsize
/ sizeof(*locks
);
128 for (i
=0; i
<count
; i
++) {
129 struct lock_struct
*lock
= &locks
[i
];
131 if (process_exists(lock
->context
.pid
)) continue;
133 if (count
> 1 && i
< count
-1) {
134 memmove(&locks
[i
], &locks
[i
+1],
135 sizeof(*locks
)*((count
-1) - i
));
142 tdb_delete(tdb
, kbuf
);
143 } else if (count
< (dbuf
.dsize
/ sizeof(*locks
))) {
144 dbuf
.dsize
= count
* sizeof(*locks
);
145 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
148 tdb_chainunlock(tdb
, kbuf
);
152 /****************************************************************************
153 Open up the brlock.tdb database.
154 ****************************************************************************/
155 void brl_init(int read_only
)
158 tdb
= tdb_open(lock_path("brlock.tdb"), 0, TDB_CLEAR_IF_FIRST
,
159 read_only
?O_RDONLY
:(O_RDWR
|O_CREAT
), 0644);
161 DEBUG(0,("Failed to open byte range locking database\n"));
165 /* delete any dead locks */
167 tdb_traverse(tdb
, delete_fn
, NULL
);
172 /****************************************************************************
173 Lock a range of bytes.
174 ****************************************************************************/
176 BOOL
brl_lock(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
177 uint16 smbpid
, pid_t pid
, uint16 tid
,
178 br_off start
, br_off size
,
179 enum brl_type lock_type
)
183 struct lock_struct lock
, *locks
;
185 kbuf
= locking_key(dev
,ino
);
189 tdb_chainlock(tdb
, kbuf
);
190 dbuf
= tdb_fetch(tdb
, kbuf
);
192 lock
.context
.smbpid
= smbpid
;
193 lock
.context
.pid
= pid
;
194 lock
.context
.tid
= tid
;
198 lock
.lock_type
= lock_type
;
201 /* there are existing locks - make sure they don't conflict */
202 locks
= (struct lock_struct
*)dbuf
.dptr
;
203 count
= dbuf
.dsize
/ sizeof(*locks
);
204 for (i
=0; i
<count
; i
++) {
205 if (brl_conflict(&locks
[i
], &lock
)) {
211 /* no conflicts - add it to the list of locks */
212 dbuf
.dptr
= Realloc(dbuf
.dptr
, dbuf
.dsize
+ sizeof(*locks
));
213 if (!dbuf
.dptr
) goto fail
;
214 memcpy(dbuf
.dptr
+ dbuf
.dsize
, &lock
, sizeof(lock
));
215 dbuf
.dsize
+= sizeof(lock
);
216 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
219 tdb_chainunlock(tdb
, kbuf
);
223 if (dbuf
.dptr
) free(dbuf
.dptr
);
224 tdb_chainunlock(tdb
, kbuf
);
228 /****************************************************************************
229 Unlock a range of bytes.
230 ****************************************************************************/
232 BOOL
brl_unlock(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
233 uint16 smbpid
, pid_t pid
, uint16 tid
,
234 br_off start
, br_off size
)
238 struct lock_struct
*locks
;
239 struct lock_context context
;
241 kbuf
= locking_key(dev
,ino
);
245 tdb_chainlock(tdb
, kbuf
);
246 dbuf
= tdb_fetch(tdb
, kbuf
);
249 DEBUG(10,("brl_unlock: tdb_fetch failed !\n"));
253 context
.smbpid
= smbpid
;
257 /* there are existing locks - find a match */
258 locks
= (struct lock_struct
*)dbuf
.dptr
;
259 count
= dbuf
.dsize
/ sizeof(*locks
);
260 for (i
=0; i
<count
; i
++) {
262 struct lock_struct
*lock
= &locks
[i
];
265 /* JRATEST - DEBUGGING INFO */
266 if(!brl_same_context(&lock
->context
, &context
)) {
267 DEBUG(10,("brl_unlock: Not same context. l_smbpid = %u, l_pid = %u, l_tid = %u: \
268 smbpid = %u, pid = %u, tid = %u\n",
269 lock
->context
.smbpid
, lock
->context
.pid
, lock
->context
.tid
,
270 context
.smbpid
, context
.pid
, context
.tid
));
276 if (brl_same_context(&lock
->context
, &context
) &&
277 lock
->fnum
== fnum
&&
278 lock
->start
== start
&&
279 lock
->size
== size
) {
280 /* found it - delete it */
282 tdb_delete(tdb
, kbuf
);
285 memmove(&locks
[i
], &locks
[i
+1],
286 sizeof(*locks
)*((count
-1) - i
));
288 dbuf
.dsize
-= sizeof(*locks
);
289 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
293 tdb_chainunlock(tdb
, kbuf
);
298 /* we didn't find it */
301 if (dbuf
.dptr
) free(dbuf
.dptr
);
302 tdb_chainunlock(tdb
, kbuf
);
306 /****************************************************************************
307 Test if we could add a lock if we wanted to.
308 ****************************************************************************/
310 BOOL
brl_locktest(SMB_DEV_T dev
, SMB_INO_T ino
, int fnum
,
311 uint16 smbpid
, pid_t pid
, uint16 tid
,
312 br_off start
, br_off size
,
313 enum brl_type lock_type
)
317 struct lock_struct lock
, *locks
;
319 kbuf
= locking_key(dev
,ino
);
323 tdb_chainlock(tdb
, kbuf
);
324 dbuf
= tdb_fetch(tdb
, kbuf
);
326 lock
.context
.smbpid
= smbpid
;
327 lock
.context
.pid
= pid
;
328 lock
.context
.tid
= tid
;
332 lock
.lock_type
= lock_type
;
335 /* there are existing locks - make sure they don't conflict */
336 locks
= (struct lock_struct
*)dbuf
.dptr
;
337 count
= dbuf
.dsize
/ sizeof(*locks
);
338 for (i
=0; i
<count
; i
++) {
339 if (brl_conflict(&locks
[i
], &lock
)) {
345 /* no conflicts - we could have added it */
347 tdb_chainunlock(tdb
, kbuf
);
351 if (dbuf
.dptr
) free(dbuf
.dptr
);
352 tdb_chainunlock(tdb
, kbuf
);
356 /****************************************************************************
357 Remove any locks associated with a open file.
358 ****************************************************************************/
360 void brl_close(SMB_DEV_T dev
, SMB_INO_T ino
, pid_t pid
, int tid
, int fnum
)
363 int count
, i
, dcount
=0;
364 struct lock_struct
*locks
;
366 kbuf
= locking_key(dev
,ino
);
370 tdb_chainlock(tdb
, kbuf
);
371 dbuf
= tdb_fetch(tdb
, kbuf
);
373 if (!dbuf
.dptr
) goto fail
;
375 /* there are existing locks - remove any for this fnum */
376 locks
= (struct lock_struct
*)dbuf
.dptr
;
377 count
= dbuf
.dsize
/ sizeof(*locks
);
378 for (i
=0; i
<count
; i
++) {
379 struct lock_struct
*lock
= &locks
[i
];
381 if (lock
->context
.tid
== tid
&&
382 lock
->context
.pid
== pid
&&
383 lock
->fnum
== fnum
) {
384 /* found it - delete it */
385 if (count
> 1 && i
< count
-1) {
386 memmove(&locks
[i
], &locks
[i
+1],
387 sizeof(*locks
)*((count
-1) - i
));
396 tdb_delete(tdb
, kbuf
);
397 } else if (count
< (dbuf
.dsize
/ sizeof(*locks
))) {
398 dbuf
.dsize
-= dcount
* sizeof(*locks
);
399 tdb_store(tdb
, kbuf
, dbuf
, TDB_REPLACE
);
402 /* we didn't find it */
404 if (dbuf
.dptr
) free(dbuf
.dptr
);
405 tdb_chainunlock(tdb
, kbuf
);
408 /****************************************************************************
409 Traverse the whole database with this function, calling traverse_callback
411 ****************************************************************************/
413 static int traverse_fn(TDB_CONTEXT
*ttdb
, TDB_DATA kbuf
, TDB_DATA dbuf
, void *state
)
415 struct lock_struct
*locks
;
416 struct lock_key
*key
;
419 BRLOCK_FN(traverse_callback
) = (BRLOCK_FN_CAST())state
;
421 locks
= (struct lock_struct
*)dbuf
.dptr
;
422 key
= (struct lock_key
*)kbuf
.dptr
;
424 for (i
=0;i
<dbuf
.dsize
/sizeof(*locks
);i
++) {
425 traverse_callback(key
->device
, key
->inode
,
426 locks
[i
].context
.pid
,
434 /*******************************************************************
435 Call the specified function on each lock in the database.
436 ********************************************************************/
438 int brl_forall(BRLOCK_FN(fn
))
441 return tdb_traverse(tdb
, traverse_fn
, (void *)fn
);