g_lock: unparse->put
[metze/samba/wip.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 #define G_LOCK_REC_LENGTH (SERVER_ID_BUF_LENGTH+1)
49
50 static void g_lock_rec_put(uint8_t buf[G_LOCK_REC_LENGTH],
51                            const struct g_lock_rec rec)
52 {
53         SCVAL(buf, 0, rec.lock_type);
54         server_id_put(buf+1, rec.pid);
55 }
56
57 static void g_lock_rec_get(struct g_lock_rec *rec,
58                            const uint8_t buf[G_LOCK_REC_LENGTH])
59 {
60         rec->lock_type = CVAL(buf, 0);
61         server_id_get(&rec->pid, buf+1);
62 }
63
64 struct g_lock_ctx *g_lock_ctx_init(TALLOC_CTX *mem_ctx,
65                                    struct messaging_context *msg)
66 {
67         struct g_lock_ctx *result;
68         struct db_context *backend;
69         char *db_path;
70
71         result = talloc(mem_ctx, struct g_lock_ctx);
72         if (result == NULL) {
73                 return NULL;
74         }
75         result->msg = msg;
76
77         db_path = lock_path("g_lock.tdb");
78         if (db_path == NULL) {
79                 TALLOC_FREE(result);
80                 return NULL;
81         }
82
83         backend = db_open(result, db_path, 0,
84                           TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
85                           O_RDWR|O_CREAT, 0600,
86                           DBWRAP_LOCK_ORDER_2,
87                           DBWRAP_FLAG_NONE);
88         TALLOC_FREE(db_path);
89         if (backend == NULL) {
90                 DEBUG(1, ("g_lock_init: Could not open g_lock.tdb\n"));
91                 TALLOC_FREE(result);
92                 return NULL;
93         }
94
95         result->db = db_open_watched(result, backend, msg);
96         if (result->db == NULL) {
97                 DBG_WARNING("g_lock_init: db_open_watched failed\n");
98                 TALLOC_FREE(result);
99                 return NULL;
100         }
101         return result;
102 }
103
104 static bool g_lock_conflicts(enum g_lock_type l1, enum g_lock_type l2)
105 {
106         /*
107          * Only tested write locks so far. Very likely this routine
108          * needs to be fixed for read locks....
109          */
110         if ((l1 == G_LOCK_READ) && (l2 == G_LOCK_READ)) {
111                 return false;
112         }
113         return true;
114 }
115
116 static bool g_lock_get(TALLOC_CTX *mem_ctx, TDB_DATA data,
117                        unsigned *pnum_locks, struct g_lock_rec **plocks)
118 {
119         size_t i, num_locks;
120         struct g_lock_rec *locks;
121
122         if ((data.dsize % G_LOCK_REC_LENGTH) != 0) {
123                 DEBUG(1, ("invalid lock record length %zu\n", data.dsize));
124                 return false;
125         }
126         num_locks = data.dsize / G_LOCK_REC_LENGTH;
127
128         locks = talloc_array(mem_ctx, struct g_lock_rec, num_locks);
129         if (locks == NULL) {
130                 DEBUG(1, ("talloc_memdup failed\n"));
131                 return false;
132         }
133
134         for (i=0; i<num_locks; i++) {
135                 g_lock_rec_get(&locks[i], data.dptr);
136                 data.dptr += G_LOCK_REC_LENGTH;
137         }
138
139         *plocks = locks;
140         *pnum_locks = num_locks;
141         return true;
142 }
143
144 static ssize_t g_lock_put(uint8_t *buf, size_t buflen,
145                           const struct g_lock_rec *locks,
146                           size_t num_locks)
147 {
148         size_t i, len, ofs;
149
150         if (num_locks > UINT32_MAX/G_LOCK_REC_LENGTH) {
151                 return -1;
152         }
153
154         len = num_locks * G_LOCK_REC_LENGTH;
155
156         if (len > buflen) {
157                 return len;
158         }
159
160         ofs = 0;
161
162         for (i=0; i<num_locks; i++) {
163                 g_lock_rec_put(buf+ofs, locks[i]);
164                 ofs += G_LOCK_REC_LENGTH;
165         }
166
167         return len;
168 }
169
170 static NTSTATUS g_lock_record_store(struct db_record *rec,
171                                     const struct g_lock_rec *locks,
172                                     size_t num_locks)
173 {
174         ssize_t len;
175         uint8_t *buf;
176         NTSTATUS status;
177
178         len = g_lock_put(NULL, 0, locks, num_locks);
179         if (len == -1) {
180                 return NT_STATUS_BUFFER_TOO_SMALL;
181         }
182
183         buf = talloc_array(rec, uint8_t, len);
184         if (buf == NULL) {
185                 return NT_STATUS_NO_MEMORY;
186         }
187
188         g_lock_put(buf, len, locks, num_locks);
189
190         status = dbwrap_record_store(
191                 rec, (TDB_DATA) { .dptr = buf, .dsize = len }, 0);
192
193         TALLOC_FREE(buf);
194
195         return status;
196 }
197
198 static NTSTATUS g_lock_trylock(struct db_record *rec, struct server_id self,
199                                enum g_lock_type type,
200                                struct server_id *blocker)
201 {
202         TDB_DATA data;
203         unsigned i, num_locks;
204         struct g_lock_rec *locks, *tmp;
205         NTSTATUS status;
206         bool modified = false;
207
208         data = dbwrap_record_get_value(rec);
209
210         if (!g_lock_get(talloc_tos(), data, &num_locks, &locks)) {
211                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
212         }
213
214         for (i=0; i<num_locks; i++) {
215                 if (serverid_equal(&self, &locks[i].pid)) {
216                         status = NT_STATUS_INTERNAL_ERROR;
217                         goto done;
218                 }
219                 if (g_lock_conflicts(type, locks[i].lock_type)) {
220                         struct server_id pid = locks[i].pid;
221
222                         /*
223                          * As the serverid_exists might recurse into
224                          * the g_lock code, we use
225                          * SERVERID_UNIQUE_ID_NOT_TO_VERIFY to avoid the loop
226                          */
227                         pid.unique_id = SERVERID_UNIQUE_ID_NOT_TO_VERIFY;
228
229                         if (serverid_exists(&pid)) {
230                                 status = NT_STATUS_LOCK_NOT_GRANTED;
231                                 *blocker = locks[i].pid;
232                                 goto done;
233                         }
234
235                         /*
236                          * Delete stale conflicting entry
237                          */
238                         locks[i] = locks[num_locks-1];
239                         num_locks -= 1;
240                         modified = true;
241                 }
242         }
243
244         tmp = talloc_realloc(talloc_tos(), locks, struct g_lock_rec,
245                              num_locks+1);
246         if (tmp == NULL) {
247                 status = NT_STATUS_NO_MEMORY;
248                 goto done;
249         }
250         locks = tmp;
251
252         ZERO_STRUCT(locks[num_locks]);
253         locks[num_locks].pid = self;
254         locks[num_locks].lock_type = type;
255         num_locks += 1;
256         modified = true;
257
258         status = NT_STATUS_OK;
259 done:
260         if (modified) {
261                 NTSTATUS store_status;
262                 store_status = g_lock_record_store(rec, locks, num_locks);
263                 if (!NT_STATUS_IS_OK(store_status)) {
264                         DBG_WARNING("g_lock_record_store failed: %s\n",
265                                     nt_errstr(store_status));
266                         status = store_status;
267                 }
268         }
269         TALLOC_FREE(locks);
270         return status;
271 }
272
273 struct g_lock_lock_state {
274         struct tevent_context *ev;
275         struct g_lock_ctx *ctx;
276         const char *name;
277         enum g_lock_type type;
278 };
279
280 static void g_lock_lock_retry(struct tevent_req *subreq);
281
282 struct tevent_req *g_lock_lock_send(TALLOC_CTX *mem_ctx,
283                                     struct tevent_context *ev,
284                                     struct g_lock_ctx *ctx,
285                                     const char *name,
286                                     enum g_lock_type type)
287 {
288         struct tevent_req *req, *subreq;
289         struct g_lock_lock_state *state;
290         struct db_record *rec;
291         struct server_id self, blocker;
292         NTSTATUS status;
293
294         req = tevent_req_create(mem_ctx, &state, struct g_lock_lock_state);
295         if (req == NULL) {
296                 return NULL;
297         }
298         state->ev = ev;
299         state->ctx = ctx;
300         state->name = name;
301         state->type = type;
302
303         rec = dbwrap_fetch_locked(ctx->db, talloc_tos(),
304                                   string_term_tdb_data(state->name));
305         if (rec == NULL) {
306                 DEBUG(10, ("fetch_locked(\"%s\") failed\n", name));
307                 tevent_req_nterror(req, NT_STATUS_LOCK_NOT_GRANTED);
308                 return tevent_req_post(req, ev);
309         }
310
311         self = messaging_server_id(state->ctx->msg);
312
313         status = g_lock_trylock(rec, self, state->type, &blocker);
314         if (NT_STATUS_IS_OK(status)) {
315                 TALLOC_FREE(rec);
316                 tevent_req_done(req);
317                 return tevent_req_post(req, ev);
318         }
319         if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
320                 TALLOC_FREE(rec);
321                 tevent_req_nterror(req, status);
322                 return tevent_req_post(req, ev);
323         }
324         subreq = dbwrap_watched_watch_send(state, state->ev, rec, blocker);
325         TALLOC_FREE(rec);
326         if (tevent_req_nomem(subreq, req)) {
327                 return tevent_req_post(req, ev);
328         }
329         if (!tevent_req_set_endtime(
330                     subreq, state->ev,
331                     timeval_current_ofs(5 + sys_random() % 5, 0))) {
332                 tevent_req_oom(req);
333                 return tevent_req_post(req, ev);
334         }
335         tevent_req_set_callback(subreq, g_lock_lock_retry, req);
336         return req;
337 }
338
339 static void g_lock_lock_retry(struct tevent_req *subreq)
340 {
341         struct tevent_req *req = tevent_req_callback_data(
342                 subreq, struct tevent_req);
343         struct g_lock_lock_state *state = tevent_req_data(
344                 req, struct g_lock_lock_state);
345         struct server_id self = messaging_server_id(state->ctx->msg);
346         struct server_id blocker;
347         struct db_record *rec;
348         NTSTATUS status;
349
350         status = dbwrap_watched_watch_recv(subreq, talloc_tos(), &rec, NULL,
351                                            NULL);
352         TALLOC_FREE(subreq);
353
354         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
355                 rec = dbwrap_fetch_locked(
356                         state->ctx->db, talloc_tos(),
357                         string_term_tdb_data(state->name));
358                 if (rec == NULL) {
359                         status = map_nt_error_from_unix(errno);
360                 } else {
361                         status = NT_STATUS_OK;
362                 }
363         }
364
365         if (tevent_req_nterror(req, status)) {
366                 return;
367         }
368         status = g_lock_trylock(rec, self, state->type, &blocker);
369         if (NT_STATUS_IS_OK(status)) {
370                 TALLOC_FREE(rec);
371                 tevent_req_done(req);
372                 return;
373         }
374         if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) {
375                 TALLOC_FREE(rec);
376                 tevent_req_nterror(req, status);
377                 return;
378         }
379         subreq = dbwrap_watched_watch_send(state, state->ev, rec, blocker);
380         TALLOC_FREE(rec);
381         if (tevent_req_nomem(subreq, req)) {
382                 return;
383         }
384         if (!tevent_req_set_endtime(
385                     subreq, state->ev,
386                     timeval_current_ofs(5 + sys_random() % 5, 0))) {
387                 tevent_req_oom(req);
388                 return;
389         }
390         tevent_req_set_callback(subreq, g_lock_lock_retry, req);
391         return;
392
393 }
394
395 NTSTATUS g_lock_lock_recv(struct tevent_req *req)
396 {
397         return tevent_req_simple_recv_ntstatus(req);
398 }
399
400 NTSTATUS g_lock_lock(struct g_lock_ctx *ctx, const char *name,
401                      enum g_lock_type type, struct timeval timeout)
402 {
403         TALLOC_CTX *frame = talloc_stackframe();
404         struct tevent_context *ev;
405         struct tevent_req *req;
406         struct timeval end;
407         NTSTATUS status = NT_STATUS_NO_MEMORY;
408
409         ev = samba_tevent_context_init(frame);
410         if (ev == NULL) {
411                 goto fail;
412         }
413         req = g_lock_lock_send(frame, ev, ctx, name, type);
414         if (req == NULL) {
415                 goto fail;
416         }
417         end = timeval_current_ofs(timeout.tv_sec, timeout.tv_usec);
418         if (!tevent_req_set_endtime(req, ev, end)) {
419                 goto fail;
420         }
421         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
422                 goto fail;
423         }
424         status = g_lock_lock_recv(req);
425  fail:
426         TALLOC_FREE(frame);
427         return status;
428 }
429
430 NTSTATUS g_lock_unlock(struct g_lock_ctx *ctx, const char *name)
431 {
432         struct server_id self = messaging_server_id(ctx->msg);
433         struct db_record *rec = NULL;
434         struct g_lock_rec *locks = NULL;
435         unsigned i, num_locks;
436         NTSTATUS status;
437         TDB_DATA value;
438
439         rec = dbwrap_fetch_locked(ctx->db, talloc_tos(),
440                                   string_term_tdb_data(name));
441         if (rec == NULL) {
442                 DEBUG(10, ("fetch_locked(\"%s\") failed\n", name));
443                 status = NT_STATUS_INTERNAL_ERROR;
444                 goto done;
445         }
446
447         value = dbwrap_record_get_value(rec);
448
449         if (!g_lock_get(talloc_tos(), value, &num_locks, &locks)) {
450                 DEBUG(10, ("g_lock_get for %s failed\n", name));
451                 status = NT_STATUS_FILE_INVALID;
452                 goto done;
453         }
454         for (i=0; i<num_locks; i++) {
455                 if (serverid_equal(&self, &locks[i].pid)) {
456                         break;
457                 }
458         }
459         if (i == num_locks) {
460                 DBG_DEBUG("Lock not found, num_locks=%u\n", num_locks);
461                 status = NT_STATUS_NOT_FOUND;
462                 goto done;
463         }
464
465         locks[i] = locks[num_locks-1];
466         num_locks -= 1;
467
468         if (num_locks == 0) {
469                 status = dbwrap_record_delete(rec);
470         } else {
471                 status = g_lock_record_store(rec, locks, num_locks);
472         }
473         if (!NT_STATUS_IS_OK(status)) {
474                 DBG_WARNING("Could not store record: %s\n", nt_errstr(status));
475                 goto done;
476         }
477
478         status = NT_STATUS_OK;
479 done:
480         TALLOC_FREE(rec);
481         TALLOC_FREE(locks);
482         return status;
483 }
484
485 struct g_lock_locks_state {
486         int (*fn)(const char *name, void *private_data);
487         void *private_data;
488 };
489
490 static int g_lock_locks_fn(struct db_record *rec, void *priv)
491 {
492         TDB_DATA key;
493         struct g_lock_locks_state *state = (struct g_lock_locks_state *)priv;
494
495         key = dbwrap_record_get_key(rec);
496         if ((key.dsize == 0) || (key.dptr[key.dsize-1] != 0)) {
497                 DEBUG(1, ("invalid key in g_lock.tdb, ignoring\n"));
498                 return 0;
499         }
500         return state->fn((char *)key.dptr, state->private_data);
501 }
502
503 int g_lock_locks(struct g_lock_ctx *ctx,
504                  int (*fn)(const char *name, void *private_data),
505                  void *private_data)
506 {
507         struct g_lock_locks_state state;
508         NTSTATUS status;
509         int count;
510
511         state.fn = fn;
512         state.private_data = private_data;
513
514         status = dbwrap_traverse_read(ctx->db, g_lock_locks_fn, &state, &count);
515         if (!NT_STATUS_IS_OK(status)) {
516                 return -1;
517         }
518         return count;
519 }
520
521 NTSTATUS g_lock_dump(struct g_lock_ctx *ctx, const char *name,
522                      int (*fn)(struct server_id pid,
523                                enum g_lock_type lock_type,
524                                void *private_data),
525                      void *private_data)
526 {
527         TDB_DATA data;
528         unsigned i, num_locks;
529         struct g_lock_rec *locks = NULL;
530         bool ret;
531         NTSTATUS status;
532
533         status = dbwrap_fetch_bystring(ctx->db, talloc_tos(), name, &data);
534         if (!NT_STATUS_IS_OK(status)) {
535                 return status;
536         }
537
538         if ((data.dsize == 0) || (data.dptr == NULL)) {
539                 return NT_STATUS_OK;
540         }
541
542         ret = g_lock_get(talloc_tos(), data, &num_locks, &locks);
543
544         TALLOC_FREE(data.dptr);
545
546         if (!ret) {
547                 DEBUG(10, ("g_lock_get for %s failed\n", name));
548                 return NT_STATUS_INTERNAL_ERROR;
549         }
550
551         for (i=0; i<num_locks; i++) {
552                 if (fn(locks[i].pid, locks[i].lock_type, private_data) != 0) {
553                         break;
554                 }
555         }
556         TALLOC_FREE(locks);
557         return NT_STATUS_OK;
558 }
559
560 static bool g_lock_init_all(TALLOC_CTX *mem_ctx,
561                             struct tevent_context **pev,
562                             struct messaging_context **pmsg,
563                             struct g_lock_ctx **pg_ctx)
564 {
565         struct tevent_context *ev = NULL;
566         struct messaging_context *msg = NULL;
567         struct g_lock_ctx *g_ctx = NULL;
568
569         ev = samba_tevent_context_init(mem_ctx);
570         if (ev == NULL) {
571                 d_fprintf(stderr, "ERROR: could not init event context\n");
572                 goto fail;
573         }
574         msg = messaging_init(mem_ctx, ev);
575         if (msg == NULL) {
576                 d_fprintf(stderr, "ERROR: could not init messaging context\n");
577                 goto fail;
578         }
579         g_ctx = g_lock_ctx_init(mem_ctx, msg);
580         if (g_ctx == NULL) {
581                 d_fprintf(stderr, "ERROR: could not init g_lock context\n");
582                 goto fail;
583         }
584
585         *pev = ev;
586         *pmsg = msg;
587         *pg_ctx = g_ctx;
588         return true;
589 fail:
590         TALLOC_FREE(g_ctx);
591         TALLOC_FREE(msg);
592         TALLOC_FREE(ev);
593         return false;
594 }
595
596 NTSTATUS g_lock_do(const char *name, enum g_lock_type lock_type,
597                    struct timeval timeout,
598                    void (*fn)(void *private_data), void *private_data)
599 {
600         struct tevent_context *ev = NULL;
601         struct messaging_context *msg = NULL;
602         struct g_lock_ctx *g_ctx = NULL;
603         NTSTATUS status;
604
605         if (!g_lock_init_all(talloc_tos(), &ev, &msg, &g_ctx)) {
606                 status = NT_STATUS_ACCESS_DENIED;
607                 goto done;
608         }
609
610         status = g_lock_lock(g_ctx, name, lock_type, timeout);
611         if (!NT_STATUS_IS_OK(status)) {
612                 goto done;
613         }
614         fn(private_data);
615         g_lock_unlock(g_ctx, name);
616
617 done:
618         TALLOC_FREE(g_ctx);
619         TALLOC_FREE(msg);
620         TALLOC_FREE(ev);
621         return status;
622 }