4167ef713e1002e56cdc9010389e8eaab53189e0
[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("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 static bool leases_db_key(TALLOC_CTX *mem_ctx,
64                           const struct GUID *client_guid,
65                           const struct smb2_lease_key *lease_key,
66                           TDB_DATA *key)
67 {
68         struct leases_db_key db_key = {
69                 .client_guid = *client_guid,
70                 .lease_key = *lease_key };
71         DATA_BLOB blob;
72         enum ndr_err_code ndr_err;
73
74         if (DEBUGLEVEL >= 10) {
75                 DEBUG(10, ("%s:\n", __func__));
76                 NDR_PRINT_DEBUG(leases_db_key, &db_key);
77         }
78
79         ndr_err = ndr_push_struct_blob(
80                 &blob, mem_ctx, &db_key,
81                 (ndr_push_flags_fn_t)ndr_push_leases_db_key);
82         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
83                 DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
84                            __func__, ndr_errstr(ndr_err)));
85                 return false;
86         }
87
88         *key = make_tdb_data(blob.data, blob.length);
89         return true;
90 }
91
92 NTSTATUS leases_db_add(const struct GUID *client_guid,
93                        const struct smb2_lease_key *lease_key,
94                        const struct file_id *id,
95                        const char *servicepath,
96                        const char *base_name,
97                        const char *stream_name)
98 {
99         TDB_DATA db_key, db_value;
100         DATA_BLOB blob;
101         struct db_record *rec;
102         NTSTATUS status;
103         bool ok;
104         struct leases_db_value new_value;
105         struct leases_db_file new_file;
106         struct leases_db_value *value = NULL;
107         enum ndr_err_code ndr_err;
108
109         if (!leases_db_init(false)) {
110                 return NT_STATUS_INTERNAL_ERROR;
111         }
112
113         ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
114         if (!ok) {
115                 DEBUG(10, ("%s: leases_db_key failed\n", __func__));
116                 return NT_STATUS_NO_MEMORY;
117         }
118
119         rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
120         TALLOC_FREE(db_key.dptr);
121         if (rec == NULL) {
122                 return NT_STATUS_INTERNAL_ERROR;
123         }
124
125         db_value = dbwrap_record_get_value(rec);
126         if (db_value.dsize != 0) {
127                 uint32_t i;
128
129                 DEBUG(10, ("%s: record exists\n", __func__));
130
131                 value = talloc(talloc_tos(), struct leases_db_value);
132                 if (value == NULL) {
133                         status = NT_STATUS_NO_MEMORY;
134                         goto out;
135                 }
136
137                 blob.data = db_value.dptr;
138                 blob.length = db_value.dsize;
139
140                 ndr_err = ndr_pull_struct_blob_all(
141                         &blob, value, value,
142                         (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
143                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
144                         DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
145                                    __func__, ndr_errstr(ndr_err)));
146                         status = ndr_map_error2ntstatus(ndr_err);
147                         goto out;
148                 }
149
150                 /* id must be unique. */
151                 for (i = 0; i < value->num_files; i++) {
152                         if (file_id_equal(id, &value->files[i].id)) {
153                                 status = NT_STATUS_OBJECT_NAME_COLLISION;
154                                 goto out;
155                         }
156                 }
157
158                 value->files = talloc_realloc(value, value->files,
159                                         struct leases_db_file,
160                                         value->num_files + 1);
161                 if (value->files == NULL) {
162                         status = NT_STATUS_NO_MEMORY;
163                         goto out;
164                 }
165                 value->files[value->num_files].id = *id;
166                 value->files[value->num_files].servicepath = servicepath;
167                 value->files[value->num_files].base_name = base_name;
168                 value->files[value->num_files].stream_name = stream_name;
169                 value->num_files += 1;
170
171         } else {
172                 DEBUG(10, ("%s: new record\n", __func__));
173
174                 new_file = (struct leases_db_file) {
175                         .id = *id,
176                         .servicepath = servicepath,
177                         .base_name = base_name,
178                         .stream_name = stream_name,
179                 };
180
181                 new_value = (struct leases_db_value) {
182                         .num_files = 1,
183                         .files = &new_file,
184                 };
185                 value = &new_value;
186         }
187
188         ndr_err = ndr_push_struct_blob(
189                 &blob, talloc_tos(), value,
190                 (ndr_push_flags_fn_t)ndr_push_leases_db_value);
191         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
192                 DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
193                            __func__, ndr_errstr(ndr_err)));
194                 status = ndr_map_error2ntstatus(ndr_err);
195                 goto out;
196         }
197
198         if (DEBUGLEVEL >= 10) {
199                 DEBUG(10, ("%s:\n", __func__));
200                 NDR_PRINT_DEBUG(leases_db_value, value);
201         }
202
203         db_value = make_tdb_data(blob.data, blob.length);
204
205         status = dbwrap_record_store(rec, db_value, 0);
206         if (!NT_STATUS_IS_OK(status)) {
207                 DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
208                            __func__, nt_errstr(status)));
209         }
210
211   out:
212
213         if (value != &new_value) {
214                 TALLOC_FREE(value);
215         }
216         TALLOC_FREE(rec);
217         return status;
218 }
219
220 NTSTATUS leases_db_del(const struct GUID *client_guid,
221                        const struct smb2_lease_key *lease_key,
222                        const struct file_id *id)
223 {
224         TDB_DATA db_key, db_value;
225         struct db_record *rec;
226         NTSTATUS status;
227         struct leases_db_value *value;
228         enum ndr_err_code ndr_err;
229         DATA_BLOB blob;
230         uint32_t i;
231         bool ok;
232
233         if (!leases_db_init(false)) {
234                 return NT_STATUS_INTERNAL_ERROR;
235         }
236
237         ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
238         if (!ok) {
239                 return NT_STATUS_NO_MEMORY;
240         }
241
242         rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
243         TALLOC_FREE(db_key.dptr);
244         if (rec == NULL) {
245                 return NT_STATUS_NOT_FOUND;
246         }
247         db_value = dbwrap_record_get_value(rec);
248         if (db_value.dsize == 0) {
249                 status = NT_STATUS_INTERNAL_ERROR;
250                 goto out;
251         }
252
253         value = talloc(rec, struct leases_db_value);
254         if (value == NULL) {
255                 status = NT_STATUS_NO_MEMORY;
256                 goto out;
257         }
258
259         blob.data = db_value.dptr;
260         blob.length = db_value.dsize;
261
262         ndr_err = ndr_pull_struct_blob_all(
263                 &blob, value, value,
264                 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
265         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
266                 DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
267                            __func__, ndr_errstr(ndr_err)));
268                 status = ndr_map_error2ntstatus(ndr_err);
269                 goto out;
270         }
271
272         /* id must exist. */
273         for (i = 0; i < value->num_files; i++) {
274                 if (file_id_equal(id, &value->files[i].id)) {
275                         break;
276                 }
277         }
278
279         if (i == value->num_files) {
280                 status = NT_STATUS_NOT_FOUND;
281                 goto out;
282         }
283
284         value->files[i] = value->files[value->num_files-1];
285         value->num_files -= 1;
286
287         if (value->num_files == 0) {
288                 DEBUG(10, ("%s: deleting record\n", __func__));
289                 status = dbwrap_record_delete(rec);
290         } else {
291                 DEBUG(10, ("%s: updating record\n", __func__));
292                 ndr_err = ndr_push_struct_blob(
293                         &blob, rec, value,
294                         (ndr_push_flags_fn_t)ndr_push_leases_db_value);
295                 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
296                         DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
297                                    __func__, ndr_errstr(ndr_err)));
298                         status = ndr_map_error2ntstatus(ndr_err);
299                         goto out;
300                 }
301
302                 if (DEBUGLEVEL >= 10) {
303                         DEBUG(10, ("%s:\n", __func__));
304                         NDR_PRINT_DEBUG(leases_db_value, value);
305                 }
306
307                 db_value = make_tdb_data(blob.data, blob.length);
308
309                 status = dbwrap_record_store(rec, db_value, 0);
310                 if (!NT_STATUS_IS_OK(status)) {
311                         DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
312                                    __func__, nt_errstr(status)));
313                 }
314         }
315
316   out:
317
318         TALLOC_FREE(rec);
319         return status;
320 }
321
322 struct leases_db_fetch_state {
323         void (*parser)(uint32_t num_files,
324                         const struct leases_db_file *files,
325                         void *private_data);
326         void *private_data;
327         NTSTATUS status;
328 };
329
330 static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
331 {
332         struct leases_db_fetch_state *state =
333                 (struct leases_db_fetch_state *)private_data;
334         DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
335         enum ndr_err_code ndr_err;
336         struct leases_db_value *value;
337
338         value = talloc(talloc_tos(), struct leases_db_value);
339         if (value == NULL) {
340                 state->status = NT_STATUS_NO_MEMORY;
341                 return;
342         }
343
344         ndr_err = ndr_pull_struct_blob_all(
345                 &blob, value, value,
346                 (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
347         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
348                 DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
349                            __func__, ndr_errstr(ndr_err)));
350                 TALLOC_FREE(value);
351                 state->status = ndr_map_error2ntstatus(ndr_err);
352                 return;
353         }
354
355         if (DEBUGLEVEL >= 10) {
356                 DEBUG(10, ("%s:\n", __func__));
357                 NDR_PRINT_DEBUG(leases_db_value, value);
358         }
359
360         state->parser(value->num_files,
361                         value->files,
362                         state->private_data);
363
364         TALLOC_FREE(value);
365         state->status = NT_STATUS_OK;
366 }
367
368 NTSTATUS leases_db_parse(const struct GUID *client_guid,
369                          const struct smb2_lease_key *lease_key,
370                          void (*parser)(uint32_t num_files,
371                                         const struct leases_db_file *files,
372                                         void *private_data),
373                          void *private_data)
374 {
375         TDB_DATA db_key;
376         struct leases_db_fetch_state state;
377         NTSTATUS status;
378         bool ok;
379
380         if (!leases_db_init(true)) {
381                 return NT_STATUS_INTERNAL_ERROR;
382         }
383
384         ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
385         if (!ok) {
386                 return NT_STATUS_NO_MEMORY;
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         TALLOC_FREE(db_key.dptr);
398         if (!NT_STATUS_IS_OK(status)) {
399                 return status;
400         }
401         return state.status;
402 }
403
404 NTSTATUS leases_db_rename(const struct GUID *client_guid,
405                        const struct smb2_lease_key *lease_key,
406                        const struct file_id *id,
407                        const char *servicename_new,
408                        const char *filename_new,
409                        const char *stream_name_new)
410 {
411         NTSTATUS status;
412
413         status = leases_db_del(client_guid,
414                                 lease_key,
415                                 id);
416         if (!NT_STATUS_IS_OK(status)) {
417                 return status;
418         }
419
420         return leases_db_add(client_guid,
421                                 lease_key,
422                                 id,
423                                 servicename_new,
424                                 filename_new,
425                                 stream_name_new);
426 }
427
428 NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx,
429                         uint32_t num_files,
430                         const struct leases_db_file *files,
431                         struct file_id **pp_ids)
432 {
433         uint32_t i;
434         struct file_id *ids = talloc_array(mem_ctx,
435                                 struct file_id,
436                                 num_files);
437         if (ids == NULL) {
438                 return NT_STATUS_NO_MEMORY;
439         }
440
441         for (i = 0; i < num_files; i++) {
442                 ids[i] = files[i].id;
443         }
444         *pp_ids = ids;
445         return NT_STATUS_OK;
446 }