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