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