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