5b282e82e12e37fad6ff6a08803a2a884705fe4b
[kai/samba-autobuild/.git] / source3 / lib / g_lock.c
1 /*
2    Unix SMB/CIFS implementation.
3    global locks based on dbwrap and messaging
4    Copyright (C) 2009 by Volker Lendecke
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "lib/util/server_id.h"
23 #include "dbwrap/dbwrap.h"
24 #include "dbwrap/dbwrap_open.h"
25 #include "dbwrap/dbwrap_watch.h"
26 #include "g_lock.h"
27 #include "util_tdb.h"
28 #include "../lib/util/tevent_ntstatus.h"
29 #include "messages.h"
30 #include "serverid.h"
31
32 struct g_lock_ctx {
33         struct db_context *db;
34         struct messaging_context *msg;
35 };
36
37 /*
38  * The "g_lock.tdb" file contains records, indexed by the 0-terminated
39  * lockname. The record contains an array of "struct g_lock_rec"
40  * structures.
41  */
42
43 struct g_lock_rec {
44         enum g_lock_type lock_type;
45         struct server_id pid;
46 };
47
48 struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx,
49                                    struct messaging_context *msg)
50 {
51         struct g_lock_ctx *result;
52         struct db_context *backend;
53         char *db_path;
54
55         result = talloc(mem_ctx, struct g_lock_ctx);
56         if (result == NULL) {
57                 return NULL;
58         }
59         result->msg = msg;
60
61         db_path = lock_path("g_lock.tdb");
62         if (db_path == NULL) {
63                 TALLOC_FREE(result);
64                 return NULL;
65         }
66
67         backend = db_open(result, db_path, 0,
68                           TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
69                           O_RDWR|O_CREAT, 0600,
70                           DBWRAP_LOCK_ORDER_2,
71                           DBWRAP_FLAG_NONE);
72         TALLOC_FREE(db_path);
73         if (backend == NULL) {
74                 DEBUG(1, ("g_lock_init: Could not open g_lock.tdb\n"));
75                 TALLOC_FREE(result);
76                 return NULL;
77         }
78
79         result->db = db_open_watched(result, backend, msg);
80         if (result->db == NULL) {
81                 DBG_WARNING("g_lock_init: db_open_watched failed\n");
82                 TALLOC_FREE(result);
83                 return NULL;
84         }
85         return result;
86 }
87
88 static bool g_lock_conflicts(enum g_lock_type l1, enum g_lock_type l2)
89 {
90         /*
91          * Only tested write locks so far. Very likely this routine
92          * needs to be fixed for read locks....
93          */
94         if ((l1 == G_LOCK_READ) && (l2 == G_LOCK_READ)) {
95                 return false;
96         }
97         return true;
98 }
99
100 static bool g_lock_parse(TALLOC_CTX *mem_ctx, TDB_DATA data,
101                          unsigned *pnum_locks, struct g_lock_rec **plocks)
102 {
103         unsigned num_locks;
104         struct g_lock_rec *locks;
105
106         if ((data.dsize % sizeof(struct g_lock_rec)) != 0) {
107                 DEBUG(1, ("invalid lock record length %zu\n", data.dsize));
108                 return false;
109         }
110         num_locks = data.dsize / sizeof(struct g_lock_rec);
111         locks = talloc_memdup(mem_ctx, data.dptr, data.dsize);
112         if (locks == NULL) {
113                 DEBUG(1, ("talloc_memdup failed\n"));
114                 return false;
115         }
116         *plocks = locks;
117         *pnum_locks = num_locks;
118         return true;
119 }
120
121 static NTSTATUS g_lock_trylock(struct db_record *rec, struct server_id self,
122                                enum g_lock_type type,
123                                struct server_id *blocker)
124 {
125         TDB_DATA data;
126         unsigned i, num_locks;
127         struct g_lock_rec *locks, *tmp;
128         NTSTATUS status;
129         bool modified = false;
130
131         data = dbwrap_record_get_value(rec);
132
133         if (!g_lock_parse(talloc_tos(), data, &num_locks, &locks)) {
134                 return NT_STATUS_INTERNAL_ERROR;
135         }
136
137         for (i=0; i<num_locks; i++) {
138                 if (serverid_equal(&self, &locks[i].pid)) {
139                         status = NT_STATUS_INTERNAL_ERROR;
140                         goto done;
141                 }
142                 if (g_lock_conflicts(type, locks[i].lock_type)) {
143                         struct server_id pid = locks[i].pid;
144
145                         /*
146                          * As the serverid_exists might recurse into
147                          * the g_lock code, we use
148                          * SERVERID_UNIQUE_ID_NOT_TO_VERIFY to avoid the loop
149                          */
150                         pid.unique_id = SERVERID_UNIQUE_ID_NOT_TO_VERIFY;
151
152                         if (serverid_exists(&pid)) {
153                                 status = NT_STATUS_LOCK_NOT_GRANTED;
154                                 *blocker = locks[i].pid;
155                                 goto done;
156                         }
157
158                         /*
159                          * Delete stale conflicting entry
160                          */
161                         locks[i] = locks[num_locks-1];
162                         num_locks -= 1;
163                         modified = true;
164                 }
165         }
166
167         tmp = talloc_realloc(talloc_tos(), locks, struct g_lock_rec,
168                              num_locks+1);
169         if (tmp == NULL) {
170                 status = NT_STATUS_NO_MEMORY;
171                 goto done;
172         }
173         locks = tmp;
174
175         ZERO_STRUCT(locks[num_locks]);
176         locks[num_locks].pid = self;
177         locks[num_locks].lock_type = type;
178         num_locks += 1;
179         modified = true;
180
181         status = NT_STATUS_OK;
182 done:
183         if (modified) {
184                 NTSTATUS store_status;
185
186                 data = make_tdb_data((uint8_t *)locks, num_locks * sizeof(*locks));
187                 store_status = dbwrap_record_store(rec, data, 0);
188                 if (!NT_STATUS_IS_OK(store_status)) {
189                         DEBUG(1, ("rec->store failed: %s\n",
190                                   nt_errstr(store_status)));
191                         status = store_status;
192                 }
193         }
194         TALLOC_FREE(locks);
195         return status;
196 }
197
198 struct g_lock_lock_state {
199         struct tevent_context *ev;
200         struct g_lock_ctx *ctx;
201         const char *name;
202         enum g_lock_type type;
203 };
204
205 static void g_lock_lock_retry(struct tevent_req *subreq);
206
207 struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
208                                     struct tevent_context *ev,
209                                     struct g_lock_ctx *ctx,
210                                     const char *name,
211                                     enum g_lock_type type)
212 {
213         struct tevent_req *req, *subreq;
214         struct g_lock_lock_state *state;
215         struct db_record *rec;
216         struct server_id self, blocker;
217         NTSTATUS status;
218
219         req = tevent_req_create(mem_ctx, &state, struct g_lock_lock_state);
220         if (req == NULL) {
221                 return NULL;
222         }
223         state->ev = ev;
224         state->ctx = ctx;
225         state->name = name;
226         state->type = type;
227
228         rec = dbwrap_fetch_locked(ctx->db, talloc_tos(),
229                                   string_term_tdb_data(state->name));
230         if (rec == NULL) {
231                 DEBUG(10, ("fetch_locked(\"%s\") failed\n", name));
232                 tevent_req_nterror(req, NT_STATUS_LOCK_NOT_GRANTED);
233                 return tevent_req_post(req, ev);
234         }
235
236         self = messaging_server_id(state->ctx->msg);
237
238         status = g_lock_trylock(rec, self, state->type, &blocker);
239         if (NT_STATUS_IS_OK(status)) {
240                 TALLOC_FREE(rec);
241                 tevent_req_done(req);
242                 return tevent_req_post(req, ev);
243         }
244         if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
245                 TALLOC_FREE(rec);
246                 tevent_req_nterror(req, status);
247                 return tevent_req_post(req, ev);
248         }
249         subreq = dbwrap_watched_watch_send(state, state->ev, rec, blocker);
250         TALLOC_FREE(rec);
251         if (tevent_req_nomem(subreq, req)) {
252                 return tevent_req_post(req, ev);
253         }
254         if (!tevent_req_set_endtime(
255                     subreq, state->ev,
256                     timeval_current_ofs(5 + sys_random() % 5, 0))) {
257                 tevent_req_oom(req);
258                 return tevent_req_post(req, ev);
259         }
260         tevent_req_set_callback(subreq, g_lock_lock_retry, req);
261         return req;
262 }
263
264 static void g_lock_lock_retry(struct tevent_req *subreq)
265 {
266         struct tevent_req *req = tevent_req_callback_data(
267                 subreq, struct tevent_req);
268         struct g_lock_lock_state *state = tevent_req_data(
269                 req, struct g_lock_lock_state);
270         struct server_id self = messaging_server_id(state->ctx->msg);
271         struct server_id blocker;
272         struct db_record *rec;
273         NTSTATUS status;
274
275         status = dbwrap_watched_watch_recv(subreq, talloc_tos(), &rec, NULL,
276                                            NULL);
277         TALLOC_FREE(subreq);
278
279         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
280                 rec = dbwrap_fetch_locked(
281                         state->ctx->db, talloc_tos(),
282                         string_term_tdb_data(state->name));
283                 if (rec == NULL) {
284                         status = map_nt_error_from_unix(errno);
285                 } else {
286                         status = NT_STATUS_OK;
287                 }
288         }
289
290         if (tevent_req_nterror(req, status)) {
291                 return;
292         }
293         status = g_lock_trylock(rec, self, state->type, &blocker);
294         if (NT_STATUS_IS_OK(status)) {
295                 TALLOC_FREE(rec);
296                 tevent_req_done(req);
297                 return;
298         }
299         if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
300                 TALLOC_FREE(rec);
301                 tevent_req_nterror(req, status);
302                 return;
303         }
304         subreq = dbwrap_watched_watch_send(state, state->ev, rec, blocker);
305         TALLOC_FREE(rec);
306         if (tevent_req_nomem(subreq, req)) {
307                 return;
308         }
309         if (!tevent_req_set_endtime(
310                     subreq, state->ev,
311                     timeval_current_ofs(5 + sys_random() % 5, 0))) {
312                 tevent_req_oom(req);
313                 return;
314         }
315         tevent_req_set_callback(subreq, g_lock_lock_retry, req);
316         return;
317
318 }
319
320 NTSTATUS g_lock_lock_recv(struct tevent_req *req)
321 {
322         return tevent_req_simple_recv_ntstatus(req);
323 }
324
325 NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, const char *name,
326                      enum g_lock_type type, struct timeval timeout)
327 {
328         TALLOC_CTX *frame = talloc_stackframe();
329         struct tevent_context *ev;
330         struct tevent_req *req;
331         struct timeval end;
332         NTSTATUS status = NT_STATUS_NO_MEMORY;
333
334         ev = samba_tevent_context_init(frame);
335         if (ev == NULL) {
336                 goto fail;
337         }
338         req = g_lock_lock_send(frame, ev, ctx, name, type);
339         if (req == NULL) {
340                 goto fail;
341         }
342         end = timeval_current_ofs(timeout.tv_sec, timeout.tv_usec);
343         if (!tevent_req_set_endtime(req, ev, end)) {
344                 goto fail;
345         }
346         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
347                 goto fail;
348         }
349         status = g_lock_lock_recv(req);
350  fail:
351         TALLOC_FREE(frame);
352         return status;
353 }
354
355 NTSTATUS g_lock_unlock(struct g_lock_ctx *ctx, const char *name)
356 {
357         struct server_id self = messaging_server_id(ctx->msg);
358         struct db_record *rec = NULL;
359         struct g_lock_rec *locks = NULL;
360         unsigned i, num_locks;
361         NTSTATUS status;
362         TDB_DATA value;
363
364         rec = dbwrap_fetch_locked(ctx->db, talloc_tos(),
365                                   string_term_tdb_data(name));
366         if (rec == NULL) {
367                 DEBUG(10, ("fetch_locked(\"%s\") failed\n", name));
368                 status = NT_STATUS_INTERNAL_ERROR;
369                 goto done;
370         }
371
372         value = dbwrap_record_get_value(rec);
373
374         if (!g_lock_parse(talloc_tos(), value, &num_locks, &locks)) {
375                 DEBUG(10, ("g_lock_parse for %s failed\n", name));
376                 status = NT_STATUS_FILE_INVALID;
377                 goto done;
378         }
379         for (i=0; i<num_locks; i++) {
380                 if (serverid_equal(&self, &locks[i].pid)) {
381                         break;
382                 }
383         }
384         if (i == num_locks) {
385                 DBG_DEBUG("Lock not found, num_locks=%u\n", num_locks);
386                 status = NT_STATUS_NOT_FOUND;
387                 goto done;
388         }
389
390         locks[i] = locks[num_locks-1];
391         num_locks -= 1;
392
393         if (num_locks == 0) {
394                 status = dbwrap_record_delete(rec);
395         } else {
396                 TDB_DATA data;
397                 data = make_tdb_data((uint8_t *)locks,
398                                      sizeof(struct g_lock_rec) * num_locks);
399                 status = dbwrap_record_store(rec, data, 0);
400         }
401         if (!NT_STATUS_IS_OK(status)) {
402                 DBG_WARNING("Could not store record: %s\n", nt_errstr(status));
403                 goto done;
404         }
405
406         status = NT_STATUS_OK;
407 done:
408         TALLOC_FREE(rec);
409         TALLOC_FREE(locks);
410         return status;
411 }
412
413 struct g_lock_locks_state {
414         int (*fn)(const char *name, void *private_data);
415         void *private_data;
416 };
417
418 static int g_lock_locks_fn(struct db_record *rec, void *priv)
419 {
420         TDB_DATA key;
421         struct g_lock_locks_state *state = (struct g_lock_locks_state *)priv;
422
423         key = dbwrap_record_get_key(rec);
424         if ((key.dsize == 0) || (key.dptr[key.dsize-1] != 0)) {
425                 DEBUG(1, ("invalid key in g_lock.tdb, ignoring\n"));
426                 return 0;
427         }
428         return state->fn((char *)key.dptr, state->private_data);
429 }
430
431 int g_lock_locks(struct g_lock_ctx *ctx,
432                  int (*fn)(const char *name, void *private_data),
433                  void *private_data)
434 {
435         struct g_lock_locks_state state;
436         NTSTATUS status;
437         int count;
438
439         state.fn = fn;
440         state.private_data = private_data;
441
442         status = dbwrap_traverse_read(ctx->db, g_lock_locks_fn, &state, &count);
443         if (!NT_STATUS_IS_OK(status)) {
444                 return -1;
445         } else {
446                 return count;
447         }
448 }
449
450 NTSTATUS g_lock_dump(struct g_lock_ctx *ctx, const char *name,
451                      int (*fn)(struct server_id pid,
452                                enum g_lock_type lock_type,
453                                void *private_data),
454                      void *private_data)
455 {
456         TDB_DATA data;
457         unsigned i, num_locks;
458         struct g_lock_rec *locks = NULL;
459         bool ret;
460         NTSTATUS status;
461
462         status = dbwrap_fetch_bystring(ctx->db, talloc_tos(), name, &data);
463         if (!NT_STATUS_IS_OK(status)) {
464                 return status;
465         }
466
467         if ((data.dsize == 0) || (data.dptr == NULL)) {
468                 return NT_STATUS_OK;
469         }
470
471         ret = g_lock_parse(talloc_tos(), data, &num_locks, &locks);
472
473         TALLOC_FREE(data.dptr);
474
475         if (!ret) {
476                 DEBUG(10, ("g_lock_parse for %s failed\n", name));
477                 return NT_STATUS_INTERNAL_ERROR;
478         }
479
480         for (i=0; i<num_locks; i++) {
481                 if (fn(locks[i].pid, locks[i].lock_type, private_data) != 0) {
482                         break;
483                 }
484         }
485         TALLOC_FREE(locks);
486         return NT_STATUS_OK;
487 }
488
489 struct g_lock_get_state {
490         bool found;
491         struct server_id *pid;
492 };
493
494 static int g_lock_get_fn(struct server_id pid, enum g_lock_type lock_type,
495                          void *priv)
496 {
497         struct g_lock_get_state *state = (struct g_lock_get_state *)priv;
498         state->found = true;
499         *state->pid = pid;
500         return 1;
501 }
502
503 NTSTATUS g_lock_get(struct g_lock_ctx *ctx, const char *name,
504                     struct server_id *pid)
505 {
506         struct g_lock_get_state state;
507         NTSTATUS status;
508
509         state.found = false;
510         state.pid = pid;
511
512         status = g_lock_dump(ctx, name, g_lock_get_fn, &state);
513         if (!NT_STATUS_IS_OK(status)) {
514                 return status;
515         }
516         if (!state.found) {
517                 return NT_STATUS_NOT_FOUND;
518         }
519         return NT_STATUS_OK;
520 }
521
522 static bool g_lock_init_all(TALLOC_CTX *mem_ctx,
523                             struct tevent_context **pev,
524                             struct messaging_context **pmsg,
525                             struct g_lock_ctx **pg_ctx)
526 {
527         struct tevent_context *ev = NULL;
528         struct messaging_context *msg = NULL;
529         struct g_lock_ctx *g_ctx = NULL;
530
531         ev = samba_tevent_context_init(mem_ctx);
532         if (ev == NULL) {
533                 d_fprintf(stderr, "ERROR: could not init event context\n");
534                 goto fail;
535         }
536         msg = messaging_init(mem_ctx, ev);
537         if (msg == NULL) {
538                 d_fprintf(stderr, "ERROR: could not init messaging context\n");
539                 goto fail;
540         }
541         g_ctx = g_lock_ctx_init(mem_ctx, msg);
542         if (g_ctx == NULL) {
543                 d_fprintf(stderr, "ERROR: could not init g_lock context\n");
544                 goto fail;
545         }
546
547         *pev = ev;
548         *pmsg = msg;
549         *pg_ctx = g_ctx;
550         return true;
551 fail:
552         TALLOC_FREE(g_ctx);
553         TALLOC_FREE(msg);
554         TALLOC_FREE(ev);
555         return false;
556 }
557
558 NTSTATUS g_lock_do(const char *name, enum g_lock_type lock_type,
559                    struct timeval timeout,
560                    void (*fn)(void *private_data), void *private_data)
561 {
562         struct tevent_context *ev = NULL;
563         struct messaging_context *msg = NULL;
564         struct g_lock_ctx *g_ctx = NULL;
565         NTSTATUS status;
566
567         if (!g_lock_init_all(talloc_tos(), &ev, &msg, &g_ctx)) {
568                 status = NT_STATUS_ACCESS_DENIED;
569                 goto done;
570         }
571
572         status = g_lock_lock(g_ctx, name, lock_type, timeout);
573         if (!NT_STATUS_IS_OK(status)) {
574                 goto done;
575         }
576         fn(private_data);
577         g_lock_unlock(g_ctx, name);
578
579 done:
580         TALLOC_FREE(g_ctx);
581         TALLOC_FREE(msg);
582         TALLOC_FREE(ev);
583         return status;
584 }