596f7bfc95f458843380e3c6ac90b42b3c6e8610
[ira/wip.git] / source / lib / tdb / common / lock.c
1  /* 
2    Unix SMB/CIFS implementation.
3
4    trivial database library
5
6    Copyright (C) Andrew Tridgell              1999-2005
7    Copyright (C) Paul `Rusty' Russell              2000
8    Copyright (C) Jeremy Allison                    2000-2003
9    
10      ** NOTE! The following LGPL license applies to the tdb
11      ** library. This does NOT imply that all of Samba is released
12      ** under the LGPL
13    
14    This library is free software; you can redistribute it and/or
15    modify it under the terms of the GNU Lesser General Public
16    License as published by the Free Software Foundation; either
17    version 2 of the License, or (at your option) any later version.
18
19    This library is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    Lesser General Public License for more details.
23
24    You should have received a copy of the GNU Lesser General Public
25    License along with this library; if not, write to the Free Software
26    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27 */
28
29 #include "tdb_private.h"
30
31 /* a byte range locking function - return 0 on success
32    this functions locks/unlocks 1 byte at the specified offset.
33
34    On error, errno is also set so that errors are passed back properly
35    through tdb_open(). */
36 int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, 
37                int rw_type, int lck_type, int probe)
38 {
39         struct flock fl;
40         int ret;
41
42         if (tdb->flags & TDB_NOLOCK)
43                 return 0;
44         if ((rw_type == F_WRLCK) && (tdb->read_only)) {
45                 errno = EACCES;
46                 return -1;
47         }
48
49         fl.l_type = rw_type;
50         fl.l_whence = SEEK_SET;
51         fl.l_start = offset;
52         fl.l_len = 1;
53         fl.l_pid = 0;
54
55         do {
56                 ret = fcntl(tdb->fd,lck_type,&fl);
57         } while (ret == -1 && errno == EINTR);
58
59         if (ret == -1) {
60                 if (!probe && lck_type != F_SETLK) {
61                         /* Ensure error code is set for log fun to examine. */
62                         tdb->ecode = TDB_ERR_LOCK;
63                         TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", 
64                                  tdb->fd, offset, rw_type, lck_type));
65                 }
66                 /* Generic lock error. errno set by fcntl.
67                  * EAGAIN is an expected return from non-blocking
68                  * locks. */
69                 if (errno != EAGAIN) {
70                 TDB_LOG((tdb, 5, "tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d: %s\n", 
71                                  tdb->fd, offset, rw_type, lck_type, 
72                                  strerror(errno)));
73                 }
74                 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
75         }
76         return 0;
77 }
78
79 /* lock a list in the database. list -1 is the alloc list */
80 int tdb_lock(struct tdb_context *tdb, int list, int ltype)
81 {
82         if (list < -1 || list >= (int)tdb->header.hash_size) {
83                 TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", 
84                            list, ltype));
85                 return -1;
86         }
87         if (tdb->flags & TDB_NOLOCK)
88                 return 0;
89
90         /* Since fcntl locks don't nest, we do a lock for the first one,
91            and simply bump the count for future ones */
92         if (tdb->locked[list+1].count == 0) {
93                 if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) {
94                         TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", 
95                                  list, ltype, strerror(errno)));
96                         return -1;
97                 }
98                 tdb->locked[list+1].ltype = ltype;
99         }
100         tdb->locked[list+1].count++;
101         return 0;
102 }
103
104 /* unlock the database: returns void because it's too late for errors. */
105         /* changed to return int it may be interesting to know there
106            has been an error  --simo */
107 int tdb_unlock(struct tdb_context *tdb, int list, int ltype)
108 {
109         int ret = -1;
110
111         if (tdb->flags & TDB_NOLOCK)
112                 return 0;
113
114         /* Sanity checks */
115         if (list < -1 || list >= (int)tdb->header.hash_size) {
116                 TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size));
117                 return ret;
118         }
119
120         if (tdb->locked[list+1].count==0) {
121                 TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n"));
122                 return ret;
123         }
124
125         if (tdb->locked[list+1].count == 1) {
126                 /* Down to last nested lock: unlock underneath */
127                 ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0);
128         } else {
129                 ret = 0;
130         }
131         tdb->locked[list+1].count--;
132
133         if (ret)
134                 TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); 
135         return ret;
136 }
137
138
139
140 /* lock/unlock entire database */
141 int tdb_lockall(struct tdb_context *tdb)
142 {
143         u32 i;
144
145         /* There are no locks on read-only dbs */
146         if (tdb->read_only)
147                 return TDB_ERRCODE(TDB_ERR_LOCK, -1);
148         for (i = 0; i < tdb->header.hash_size; i++) 
149                 if (tdb_lock(tdb, i, F_WRLCK))
150                         break;
151
152         /* If error, release locks we have... */
153         if (i < tdb->header.hash_size) {
154                 u32 j;
155
156                 for ( j = 0; j < i; j++)
157                         tdb_unlock(tdb, j, F_WRLCK);
158                 return TDB_ERRCODE(TDB_ERR_NOLOCK, -1);
159         }
160
161         return 0;
162 }
163 void tdb_unlockall(struct tdb_context *tdb)
164 {
165         u32 i;
166         for (i=0; i < tdb->header.hash_size; i++)
167                 tdb_unlock(tdb, i, F_WRLCK);
168 }
169
170 /* lock/unlock one hash chain. This is meant to be used to reduce
171    contention - it cannot guarantee how many records will be locked */
172 int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key)
173 {
174         return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
175 }
176
177 int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key)
178 {
179         return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK);
180 }
181
182 int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key)
183 {
184         return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
185 }
186
187 int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
188 {
189         return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK);
190 }
191
192
193
194 /* record lock stops delete underneath */
195 int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off)
196 {
197         return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0;
198 }
199
200 /*
201   Write locks override our own fcntl readlocks, so check it here.
202   Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
203   an error to fail to get the lock here.
204 */
205 int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off)
206 {
207         struct tdb_traverse_lock *i;
208         for (i = &tdb->travlocks; i; i = i->next)
209                 if (i->off == off)
210                         return -1;
211         return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1);
212 }
213
214 /*
215   Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not
216   an error to fail to get the lock here.
217 */
218 int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off)
219 {
220         return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0);
221 }
222
223 /* fcntl locks don't stack: avoid unlocking someone else's */
224 int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off)
225 {
226         struct tdb_traverse_lock *i;
227         u32 count = 0;
228
229         if (off == 0)
230                 return 0;
231         for (i = &tdb->travlocks; i; i = i->next)
232                 if (i->off == off)
233                         count++;
234         return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0);
235 }