b739d618c49579c40b1915ff2f57b0fc0e04f91d
[samba.git] / source3 / locking / leases_db.c
1 /*
2    Unix SMB/CIFS implementation.
3    Map lease keys to file ids
4    Copyright (C) Volker Lendecke 2013
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
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "locking/leases_db.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_open.h"
26 #include "util_tdb.h"
27 #include "ndr.h"
28 #include "librpc/gen_ndr/ndr_leases_db.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_LOCKING
32
33 /* the leases database handle */
34 static struct db_context *leases_db;
35
36 bool leases_db_init(bool read_only)
37 {
38         char *db_path;
39
40         if (leases_db) {
41                 return true;
42         }
43
44         db_path = lock_path(talloc_tos(), "leases.tdb");
45         if (db_path == NULL) {
46                 return false;
47         }
48
49         leases_db = db_open(NULL, db_path, 0,
50                             TDB_DEFAULT|
51                             TDB_VOLATILE|
52                             TDB_CLEAR_IF_FIRST|
53                             TDB_SEQNUM|
54                             TDB_INCOMPATIBLE_HASH,
55                             read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
56                             DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
57         TALLOC_FREE(db_path);
58         if (leases_db == NULL) {
59                 DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
60                 return false;
61         }
62
63         return true;
64 }
65
66 struct leases_db_key_buf {
67         uint8_t buf[32];
68 };
69
70 static TDB_DATA leases_db_key(struct leases_db_key_buf *buf,
71                               const struct GUID *client_guid,
72                               const struct smb2_lease_key *lease_key)
73 {
74         struct leases_db_key db_key = {
75                 .client_guid = *client_guid,
76                 .lease_key = *lease_key };
77         DATA_BLOB blob = { .data = buf->buf, .length = sizeof(buf->buf) };
78         enum ndr_err_code ndr_err;
79
80         if (DEBUGLEVEL >= 10) {
81                 DBG_DEBUG("\n");
82                 NDR_PRINT_DEBUG(leases_db_key, &db_key);
83         }
84
85         ndr_err = ndr_push_struct_into_fixed_blob(
86                 &blob, &db_key, (ndr_push_flags_fn_t)ndr_push_leases_db_key);
87         SMB_ASSERT(NDR_ERR_CODE_IS_SUCCESS(ndr_err));
88
89         return (TDB_DATA) { .dptr = buf->buf, .dsize = sizeof(buf->buf) };
90 }
91
92 struct leases_db_do_locked_state {
93         void (*fn)(struct leases_db_value *value,
94                    bool *modified,
95                    void *private_data);
96         void *private_data;
97         NTSTATUS status;
98 };
99
100 static void leases_db_do_locked_fn(struct db_record *rec, void *private_data)
101 {
102         struct leases_db_do_locked_state *state = private_data;
103         TDB_DATA db_value = dbwrap_record_get_value(rec);
104         DATA_BLOB blob = { .data = db_value.dptr, .length = db_value.dsize };
105         struct leases_db_value *value = NULL;
106         enum ndr_err_code ndr_err;
107         bool modified = false;
108
109         value = talloc_zero(talloc_tos(), struct leases_db_value);
110         if (value == NULL) {
111                 state->status = NT_STATUS_NO_MEMORY;
112                 goto done;
113         }
114
115         if (blob.length != 0) {
116                 ndr_err = ndr_pull_struct_blob_all(
117                         &blob,
118                         value,
119                         value,
120                         (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
121                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
122                         DBG_DEBUG("ndr_pull_struct_blob_failed: %s\n",
123                                   ndr_errstr(ndr_err));
124                         state->status = ndr_map_error2ntstatus(ndr_err);
125                         goto done;
126                 }
127         }
128
129         state->fn(value, &modified, state->private_data);
130
131         if (!modified) {
132                 goto done;
133         }
134
135         if (value->num_files == 0) {
136                 state->status = dbwrap_record_delete(rec);
137                 if (!NT_STATUS_IS_OK(state->status)) {
138                         DBG_DEBUG("dbwrap_record_delete returned %s\n",
139                                   nt_errstr(state->status));
140                 }
141                 goto done;
142         }
143
144         ndr_err = ndr_push_struct_blob(
145                 &blob,
146                 value,
147                 value,
148                 (ndr_push_flags_fn_t)ndr_push_leases_db_value);
149         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
150                 DBG_DEBUG("ndr_push_struct_blob_failed: %s\n",
151                           ndr_errstr(ndr_err));
152                 state->status = ndr_map_error2ntstatus(ndr_err);
153                 goto done;
154         }
155
156         if (DEBUGLEVEL >= 10) {
157                 DBG_DEBUG("\n");
158                 NDR_PRINT_DEBUG(leases_db_value, value);
159         }
160
161         db_value = make_tdb_data(blob.data, blob.length);
162
163         state->status = dbwrap_record_store(rec, db_value, 0);
164         if (!NT_STATUS_IS_OK(state->status)) {
165                 DBG_DEBUG("dbwrap_record_store returned %s\n",
166                           nt_errstr(state->status));
167         }
168
169 done:
170         TALLOC_FREE(value);
171 }
172
173 static NTSTATUS leases_db_do_locked(
174         const struct GUID *client_guid,
175         const struct smb2_lease_key *lease_key,
176         void (*fn)(struct leases_db_value *value,
177                    bool *modified,
178                    void *private_data),
179         void *private_data)
180 {
181         struct leases_db_key_buf keybuf;
182         TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
183         struct leases_db_do_locked_state state = {
184                 .fn = fn, .private_data = private_data,
185         };
186         NTSTATUS status;
187
188         if (!leases_db_init(false)) {
189                 return NT_STATUS_INTERNAL_ERROR;
190         }
191
192         status = dbwrap_do_locked(
193                 leases_db, db_key, leases_db_do_locked_fn, &state);
194         if (!NT_STATUS_IS_OK(status)) {
195                 return status;
196         }
197         return state.status;
198 }
199
200 struct leases_db_add_state {
201         const struct file_id *id;
202         uint32_t current_state;
203         uint16_t lease_version;
204         uint16_t epoch;
205         const char *servicepath;
206         const char *base_name;
207         const char *stream_name;
208         NTSTATUS status;
209 };
210
211 static void leases_db_add_fn(
212         struct leases_db_value *value, bool *modified, void *private_data)
213 {
214         struct leases_db_add_state *state = private_data;
215         struct leases_db_file *tmp = NULL;
216         uint32_t i;
217
218         /* id must be unique. */
219         for (i = 0; i < value->num_files; i++) {
220                 if (file_id_equal(state->id, &value->files[i].id)) {
221                         state->status = NT_STATUS_OBJECT_NAME_COLLISION;
222                         return;
223                 }
224         }
225
226         if (value->num_files == 0) {
227                 /* new record */
228                 value->current_state = state->current_state;
229                 value->lease_version = state->lease_version;
230                 value->epoch = state->epoch;
231         }
232
233         tmp = talloc_realloc(
234                 value,
235                 value->files,
236                 struct leases_db_file,
237                 value->num_files + 1);
238         if (tmp == NULL) {
239                 state->status = NT_STATUS_NO_MEMORY;
240                 return;
241         }
242         value->files = tmp;
243
244         value->files[value->num_files] = (struct leases_db_file) {
245                 .id = *state->id,
246                 .servicepath = state->servicepath,
247                 .base_name = state->base_name,
248                 .stream_name = state->stream_name,
249         };
250         value->num_files += 1;
251
252         *modified = true;
253 }
254
255 NTSTATUS leases_db_add(const struct GUID *client_guid,
256                        const struct smb2_lease_key *lease_key,
257                        const struct file_id *id,
258                        uint32_t current_state,
259                        uint16_t lease_version,
260                        uint16_t epoch,
261                        const char *servicepath,
262                        const char *base_name,
263                        const char *stream_name)
264 {
265         struct leases_db_add_state state = {
266                 .id = id,
267                 .current_state = current_state,
268                 .lease_version = lease_version,
269                 .epoch = epoch,
270                 .servicepath = servicepath,
271                 .base_name = base_name,
272                 .stream_name = stream_name,
273         };
274         NTSTATUS status;
275
276         status = leases_db_do_locked(
277                 client_guid, lease_key, leases_db_add_fn, &state);
278         if (!NT_STATUS_IS_OK(status)) {
279                 DBG_DEBUG("leases_db_do_locked failed: %s\n",
280                           nt_errstr(status));
281                 return status;
282         }
283         return state.status;
284 }
285
286 struct leases_db_del_state {
287         const struct file_id *id;
288         NTSTATUS status;
289 };
290
291 static void leases_db_del_fn(
292         struct leases_db_value *value, bool *modified, void *private_data)
293 {
294         struct leases_db_del_state *state = private_data;
295         uint32_t i;
296
297         for (i = 0; i < value->num_files; i++) {
298                 if (file_id_equal(state->id, &value->files[i].id)) {
299                         break;
300                 }
301         }
302         if (i == value->num_files) {
303                 state->status = NT_STATUS_NOT_FOUND;
304                 return;
305         }
306
307         value->files[i] = value->files[value->num_files-1];
308         value->num_files -= 1;
309
310         *modified = true;
311 }
312
313 NTSTATUS leases_db_del(const struct GUID *client_guid,
314                        const struct smb2_lease_key *lease_key,
315                        const struct file_id *id)
316 {
317         struct leases_db_del_state state = { .id = id };
318         NTSTATUS status;
319
320         status = leases_db_do_locked(
321                 client_guid, lease_key, leases_db_del_fn, &state);
322         if (!NT_STATUS_IS_OK(status)) {
323                 DBG_DEBUG("leases_db_do_locked failed: %s\n",
324                           nt_errstr(status));
325                 return status;
326         }
327         return state.status;
328 }
329
330 struct leases_db_fetch_state {
331         void (*parser)(uint32_t num_files,
332                         const struct leases_db_file *files,
333                         void *private_data);
334         void *private_data;
335         NTSTATUS status;
336 };
337
338 static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
339 {
340         struct leases_db_fetch_state *state =
341                 (struct leases_db_fetch_state *)private_data;
342         DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
343         enum ndr_err_code ndr_err;
344         struct leases_db_value *value;
345
346         value = talloc(talloc_tos(), struct leases_db_value);
347         if (value == NULL) {
348                 state->status = NT_STATUS_NO_MEMORY;
349                 return;
350         }
351
352         ndr_err = ndr_pull_struct_blob_all(
353                 &blob, value, value,
354                 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
355         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
356                 DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
357                            __func__, ndr_errstr(ndr_err)));
358                 TALLOC_FREE(value);
359                 state->status = ndr_map_error2ntstatus(ndr_err);
360                 return;
361         }
362
363         if (DEBUGLEVEL >= 10) {
364                 DEBUG(10, ("%s:\n", __func__));
365                 NDR_PRINT_DEBUG(leases_db_value, value);
366         }
367
368         state->parser(value->num_files,
369                         value->files,
370                         state->private_data);
371
372         TALLOC_FREE(value);
373         state->status = NT_STATUS_OK;
374 }
375
376 NTSTATUS leases_db_parse(const struct GUID *client_guid,
377                          const struct smb2_lease_key *lease_key,
378                          void (*parser)(uint32_t num_files,
379                                         const struct leases_db_file *files,
380                                         void *private_data),
381                          void *private_data)
382 {
383         struct leases_db_key_buf keybuf;
384         TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
385         struct leases_db_fetch_state state;
386         NTSTATUS status;
387
388         if (!leases_db_init(true)) {
389                 return NT_STATUS_INTERNAL_ERROR;
390         }
391
392         state = (struct leases_db_fetch_state) {
393                 .parser = parser,
394                 .private_data = private_data,
395                 .status = NT_STATUS_OK
396         };
397
398         status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
399                                      &state);
400         if (!NT_STATUS_IS_OK(status)) {
401                 return status;
402         }
403         return state.status;
404 }
405
406 struct leases_db_rename_state {
407         const struct file_id *id;
408         const char *servicename_new;
409         const char *filename_new;
410         const char *stream_name_new;
411         NTSTATUS status;
412 };
413
414 static void leases_db_rename_fn(
415         struct leases_db_value *value, bool *modified, void *private_data)
416 {
417         struct leases_db_rename_state *state = private_data;
418         struct leases_db_file *file = NULL;
419         uint32_t i;
420
421         /* id must exist. */
422         for (i = 0; i < value->num_files; i++) {
423                 if (file_id_equal(state->id, &value->files[i].id)) {
424                         break;
425                 }
426         }
427         if (i == value->num_files) {
428                 state->status = NT_STATUS_NOT_FOUND;
429                 return;
430         }
431
432         file = &value->files[i];
433         file->servicepath = state->servicename_new;
434         file->base_name = state->filename_new;
435         file->stream_name = state->stream_name_new;
436
437         *modified = true;
438 }
439
440 NTSTATUS leases_db_rename(const struct GUID *client_guid,
441                        const struct smb2_lease_key *lease_key,
442                        const struct file_id *id,
443                        const char *servicename_new,
444                        const char *filename_new,
445                        const char *stream_name_new)
446 {
447         struct leases_db_rename_state state = {
448                 .id = id,
449                 .servicename_new = servicename_new,
450                 .filename_new = filename_new,
451                 .stream_name_new = stream_name_new,
452         };
453         NTSTATUS status;
454
455         status = leases_db_do_locked(
456                 client_guid, lease_key, leases_db_rename_fn, &state);
457         if (!NT_STATUS_IS_OK(status)) {
458                 DBG_DEBUG("leases_db_do_locked failed: %s\n",
459                           nt_errstr(status));
460                 return status;
461         }
462         return state.status;
463 }
464
465 struct leases_db_set_state {
466         uint32_t current_state;
467         bool breaking;
468         uint32_t breaking_to_requested;
469         uint32_t breaking_to_required;
470         uint16_t lease_version;
471         uint16_t epoch;
472 };
473
474 static void leases_db_set_fn(
475         struct leases_db_value *value, bool *modified, void *private_data)
476 {
477         struct leases_db_set_state *state = private_data;
478
479         if (value->num_files == 0) {
480                 DBG_WARNING("leases_db_set on new entry\n");
481                 return;
482         }
483         value->current_state = state->current_state;
484         value->breaking = state->breaking;
485         value->breaking_to_requested = state->breaking_to_requested;
486         value->breaking_to_required = state->breaking_to_required;
487         value->lease_version = state->lease_version;
488         value->epoch = state->epoch;
489         *modified = true;
490 }
491
492 NTSTATUS leases_db_set(const struct GUID *client_guid,
493                        const struct smb2_lease_key *lease_key,
494                        uint32_t current_state,
495                        bool breaking,
496                        uint32_t breaking_to_requested,
497                        uint32_t breaking_to_required,
498                        uint16_t lease_version,
499                        uint16_t epoch)
500 {
501         struct leases_db_set_state state = {
502                 .current_state = current_state,
503                 .breaking = breaking,
504                 .breaking_to_requested = breaking_to_requested,
505                 .breaking_to_required = breaking_to_required,
506                 .lease_version = lease_version,
507                 .epoch = epoch,
508         };
509         NTSTATUS status;
510
511         status = leases_db_do_locked(
512                 client_guid, lease_key, leases_db_set_fn, &state);
513         if (!NT_STATUS_IS_OK(status)) {
514                 DBG_DEBUG("leases_db_do_locked failed: %s\n",
515                           nt_errstr(status));
516                 return status;
517         }
518         return NT_STATUS_OK;
519 }
520
521 struct leases_db_get_state {
522         const struct file_id *file_id;
523         uint32_t *current_state;
524         bool *breaking;
525         uint32_t *breaking_to_requested;
526         uint32_t *breaking_to_required;
527         uint16_t *lease_version;
528         uint16_t *epoch;
529         NTSTATUS status;
530 };
531
532 static void leases_db_get_fn(TDB_DATA key, TDB_DATA data, void *private_data)
533 {
534         struct leases_db_get_state *state = private_data;
535         DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
536         enum ndr_err_code ndr_err;
537         struct leases_db_value *value;
538         uint32_t i;
539
540         value = talloc(talloc_tos(), struct leases_db_value);
541         if (value == NULL) {
542                 state->status = NT_STATUS_NO_MEMORY;
543                 return;
544         }
545
546         ndr_err = ndr_pull_struct_blob_all(
547                 &blob, value, value,
548                 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
549         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
550                 DBG_DEBUG("ndr_pull_struct_blob_failed: %s\n",
551                           ndr_errstr(ndr_err));
552                 TALLOC_FREE(value);
553                 state->status = ndr_map_error2ntstatus(ndr_err);
554                 return;
555         }
556
557         if (DEBUGLEVEL >= 10) {
558                 DBG_DEBUG("\n");
559                 NDR_PRINT_DEBUG(leases_db_value, value);
560         }
561
562         /* id must exist. */
563         for (i = 0; i < value->num_files; i++) {
564                 if (file_id_equal(state->file_id, &value->files[i].id)) {
565                         break;
566                 }
567         }
568
569         if (i == value->num_files) {
570                 state->status = NT_STATUS_NOT_FOUND;
571                 TALLOC_FREE(value);
572                 return;
573         }
574
575         if (state->current_state != NULL) {
576                 *state->current_state = value->current_state;
577         };
578         if (state->breaking != NULL) {
579                 *state->breaking = value->breaking;
580         };
581         if (state->breaking_to_requested != NULL) {
582                 *state->breaking_to_requested = value->breaking_to_requested;
583         };
584         if (state->breaking_to_required != NULL) {
585                 *state->breaking_to_required = value->breaking_to_required;
586         };
587         if (state->lease_version != NULL) {
588                 *state->lease_version = value->lease_version;
589         };
590         if (state->epoch != NULL) {
591                 *state->epoch = value->epoch;
592         };
593
594         TALLOC_FREE(value);
595         state->status = NT_STATUS_OK;
596 }
597
598 NTSTATUS leases_db_get(const struct GUID *client_guid,
599                        const struct smb2_lease_key *lease_key,
600                        const struct file_id *file_id,
601                        uint32_t *current_state,
602                        bool *breaking,
603                        uint32_t *breaking_to_requested,
604                        uint32_t *breaking_to_required,
605                        uint16_t *lease_version,
606                        uint16_t *epoch)
607 {
608         struct leases_db_get_state state = {
609                 .file_id = file_id,
610                 .current_state = current_state,
611                 .breaking = breaking,
612                 .breaking_to_requested = breaking_to_requested,
613                 .breaking_to_required = breaking_to_required,
614                 .lease_version = lease_version,
615                 .epoch = epoch,
616         };
617         struct leases_db_key_buf keybuf;
618         TDB_DATA db_key = leases_db_key(&keybuf, client_guid, lease_key);
619         NTSTATUS status;
620
621         if (!leases_db_init(true)) {
622                 return NT_STATUS_INTERNAL_ERROR;
623         }
624
625         status = dbwrap_parse_record(
626                 leases_db, db_key, leases_db_get_fn, &state);
627         if (!NT_STATUS_IS_OK(status)) {
628                 return status;
629         }
630         return state.status;
631 }
632
633 struct leases_db_get_current_state_state {
634         int seqnum;
635         uint32_t current_state;
636         NTSTATUS status;
637 };
638
639 /*
640  * This function is an optimization that
641  * relies on the fact that the
642  * smb2_lease_state current_state
643  * (which is a uint32_t size)
644  * from struct leases_db_value is the first
645  * entry in the ndr-encoded struct leases_db_value.
646  * Read it without having to ndr decode all
647  * the values in struct leases_db_value.
648  */
649
650 static void leases_db_get_current_state_fn(
651         TDB_DATA key, TDB_DATA data, void *private_data)
652 {
653         struct leases_db_get_current_state_state *state = private_data;
654         struct ndr_pull ndr;
655         enum ndr_err_code ndr_err;
656
657         if (data.dsize < sizeof(uint32_t)) {
658                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
659                 return;
660         }
661
662         state->seqnum = dbwrap_get_seqnum(leases_db);
663
664         ndr = (struct ndr_pull) {
665                 .data = data.dptr, .data_size = data.dsize,
666         };
667         ndr_err = ndr_pull_uint32(&ndr, NDR_SCALARS, &state->current_state);
668         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
669                 state->status = ndr_map_error2ntstatus(ndr_err);
670         }
671 }
672
673 NTSTATUS leases_db_get_current_state(
674         const struct GUID *client_guid,
675         const struct smb2_lease_key *lease_key,
676         int *database_seqnum,
677         uint32_t *current_state)
678 {
679         struct leases_db_get_current_state_state state = { 0 };
680         struct leases_db_key_buf keybuf;
681         TDB_DATA db_key = { 0 };
682         NTSTATUS status;
683
684         if (!leases_db_init(true)) {
685                 return NT_STATUS_INTERNAL_ERROR;
686         }
687
688         state.seqnum = dbwrap_get_seqnum(leases_db);
689         if (*database_seqnum == state.seqnum) {
690                 return NT_STATUS_OK;
691         }
692
693         db_key = leases_db_key(&keybuf, client_guid, lease_key);
694
695         status = dbwrap_parse_record(
696                 leases_db, db_key, leases_db_get_current_state_fn, &state);
697         if (!NT_STATUS_IS_OK(status)) {
698                 return status;
699         }
700         *database_seqnum = state.seqnum;
701         *current_state = state.current_state;
702
703         return NT_STATUS_OK;
704 }
705
706 NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx,
707                         uint32_t num_files,
708                         const struct leases_db_file *files,
709                         struct file_id **pp_ids)
710 {
711         uint32_t i;
712         struct file_id *ids = talloc_array(mem_ctx,
713                                 struct file_id,
714                                 num_files);
715         if (ids == NULL) {
716                 return NT_STATUS_NO_MEMORY;
717         }
718
719         for (i = 0; i < num_files; i++) {
720                 ids[i] = files[i].id;
721         }
722         *pp_ids = ids;
723         return NT_STATUS_OK;
724 }