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