lib: Add lib/util/server_id.h
[samba.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                 DEBUG(10, ("g_lock_force_unlock: Lock not found\n"));
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                 DEBUG(1, ("g_lock_force_unlock: Could not store record: %s\n",
403                           nt_errstr(status)));
404                 goto done;
405         }
406
407         status = NT_STATUS_OK;
408 done:
409         TALLOC_FREE(rec);
410         TALLOC_FREE(locks);
411         return status;
412 }
413
414 struct g_lock_locks_state {
415         int (*fn)(const char *name, void *private_data);
416         void *private_data;
417 };
418
419 static int g_lock_locks_fn(struct db_record *rec, void *priv)
420 {
421         TDB_DATA key;
422         struct g_lock_locks_state *state = (struct g_lock_locks_state *)priv;
423
424         key = dbwrap_record_get_key(rec);
425         if ((key.dsize == 0) || (key.dptr[key.dsize-1] != 0)) {
426                 DEBUG(1, ("invalid key in g_lock.tdb, ignoring\n"));
427                 return 0;
428         }
429         return state->fn((char *)key.dptr, state->private_data);
430 }
431
432 int g_lock_locks(struct g_lock_ctx *ctx,
433                  int (*fn)(const char *name, void *private_data),
434                  void *private_data)
435 {
436         struct g_lock_locks_state state;
437         NTSTATUS status;
438         int count;
439
440         state.fn = fn;
441         state.private_data = private_data;
442
443         status = dbwrap_traverse_read(ctx->db, g_lock_locks_fn, &state, &count);
444         if (!NT_STATUS_IS_OK(status)) {
445                 return -1;
446         } else {
447                 return count;
448         }
449 }
450
451 NTSTATUS g_lock_dump(struct g_lock_ctx *ctx, const char *name,
452                      int (*fn)(struct server_id pid,
453                                enum g_lock_type lock_type,
454                                void *private_data),
455                      void *private_data)
456 {
457         TDB_DATA data;
458         unsigned i, num_locks;
459         struct g_lock_rec *locks = NULL;
460         bool ret;
461         NTSTATUS status;
462
463         status = dbwrap_fetch_bystring(ctx->db, talloc_tos(), name, &data);
464         if (!NT_STATUS_IS_OK(status)) {
465                 return status;
466         }
467
468         if ((data.dsize == 0) || (data.dptr == NULL)) {
469                 return NT_STATUS_OK;
470         }
471
472         ret = g_lock_parse(talloc_tos(), data, &num_locks, &locks);
473
474         TALLOC_FREE(data.dptr);
475
476         if (!ret) {
477                 DEBUG(10, ("g_lock_parse for %s failed\n", name));
478                 return NT_STATUS_INTERNAL_ERROR;
479         }
480
481         for (i=0; i<num_locks; i++) {
482                 if (fn(locks[i].pid, locks[i].lock_type, private_data) != 0) {
483                         break;
484                 }
485         }
486         TALLOC_FREE(locks);
487         return NT_STATUS_OK;
488 }
489
490 struct g_lock_get_state {
491         bool found;
492         struct server_id *pid;
493 };
494
495 static int g_lock_get_fn(struct server_id pid, enum g_lock_type lock_type,
496                          void *priv)
497 {
498         struct g_lock_get_state *state = (struct g_lock_get_state *)priv;
499         state->found = true;
500         *state->pid = pid;
501         return 1;
502 }
503
504 NTSTATUS g_lock_get(struct g_lock_ctx *ctx, const char *name,
505                     struct server_id *pid)
506 {
507         struct g_lock_get_state state;
508         NTSTATUS status;
509
510         state.found = false;
511         state.pid = pid;
512
513         status = g_lock_dump(ctx, name, g_lock_get_fn, &state);
514         if (!NT_STATUS_IS_OK(status)) {
515                 return status;
516         }
517         if (!state.found) {
518                 return NT_STATUS_NOT_FOUND;
519         }
520         return NT_STATUS_OK;
521 }
522
523 static bool g_lock_init_all(TALLOC_CTX *mem_ctx,
524                             struct tevent_context **pev,
525                             struct messaging_context **pmsg,
526                             struct g_lock_ctx **pg_ctx)
527 {
528         struct tevent_context *ev = NULL;
529         struct messaging_context *msg = NULL;
530         struct g_lock_ctx *g_ctx = NULL;
531
532         ev = samba_tevent_context_init(mem_ctx);
533         if (ev == NULL) {
534                 d_fprintf(stderr, "ERROR: could not init event context\n");
535                 goto fail;
536         }
537         msg = messaging_init(mem_ctx, ev);
538         if (msg == NULL) {
539                 d_fprintf(stderr, "ERROR: could not init messaging context\n");
540                 goto fail;
541         }
542         g_ctx = g_lock_ctx_init(mem_ctx, msg);
543         if (g_ctx == NULL) {
544                 d_fprintf(stderr, "ERROR: could not init g_lock context\n");
545                 goto fail;
546         }
547
548         *pev = ev;
549         *pmsg = msg;
550         *pg_ctx = g_ctx;
551         return true;
552 fail:
553         TALLOC_FREE(g_ctx);
554         TALLOC_FREE(msg);
555         TALLOC_FREE(ev);
556         return false;
557 }
558
559 NTSTATUS g_lock_do(const char *name, enum g_lock_type lock_type,
560                    struct timeval timeout,
561                    void (*fn)(void *private_data), void *private_data)
562 {
563         struct tevent_context *ev = NULL;
564         struct messaging_context *msg = NULL;
565         struct g_lock_ctx *g_ctx = NULL;
566         NTSTATUS status;
567
568         if (!g_lock_init_all(talloc_tos(), &ev, &msg, &g_ctx)) {
569                 status = NT_STATUS_ACCESS_DENIED;
570                 goto done;
571         }
572
573         status = g_lock_lock(g_ctx, name, lock_type, timeout);
574         if (!NT_STATUS_IS_OK(status)) {
575                 goto done;
576         }
577         fn(private_data);
578         g_lock_unlock(g_ctx, name);
579
580 done:
581         TALLOC_FREE(g_ctx);
582         TALLOC_FREE(msg);
583         TALLOC_FREE(ev);
584         return status;
585 }