s3: modules: Fix *allocate* calls to follow POSIX error return convention.
[Samba.git] / source3 / locking / leases_db.c
blob7e000aa07521f490d3d4a3146f3889dc481684af
1 /*
2 Unix SMB/CIFS implementation.
3 Map lease keys to file ids
4 Copyright (C) Volker Lendecke 2013
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.
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.
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/>.
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"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_LOCKING
33 /* the leases database handle */
34 static struct db_context *leases_db;
36 bool leases_db_init(bool read_only)
38 if (leases_db) {
39 return true;
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);
48 if (leases_db == NULL) {
49 DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
50 return false;
53 return true;
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)
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;
67 if (DEBUGLEVEL >= 10) {
68 DEBUG(10, ("%s:\n", __func__));
69 NDR_PRINT_DEBUG(leases_db_key, &db_key);
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;
81 *key = make_tdb_data(blob.data, blob.length);
82 return true;
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)
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;
100 if (!leases_db_init(false)) {
101 return NT_STATUS_INTERNAL_ERROR;
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;
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;
116 db_value = dbwrap_record_get_value(rec);
117 if (db_value.dsize != 0) {
118 uint32_t i;
120 DEBUG(10, ("%s: record exists\n", __func__));
122 value = talloc(talloc_tos(), struct leases_db_value);
123 if (value == NULL) {
124 status = NT_STATUS_NO_MEMORY;
125 goto out;
128 blob.data = db_value.dptr;
129 blob.length = db_value.dsize;
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;
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;
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;
155 value->ids[value->num_file_ids] = *id;
156 value->num_file_ids += 1;
158 } else {
159 DEBUG(10, ("%s: new record\n", __func__));
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,
167 value = &new_value;
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;
180 if (DEBUGLEVEL >= 10) {
181 DEBUG(10, ("%s:\n", __func__));
182 NDR_PRINT_DEBUG(leases_db_value, value);
185 db_value = make_tdb_data(blob.data, blob.length);
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)));
193 out:
195 if (value != &new_value) {
196 TALLOC_FREE(value);
198 TALLOC_FREE(rec);
199 return status;
202 NTSTATUS leases_db_del(const struct GUID *client_guid,
203 const struct smb2_lease_key *lease_key,
204 const struct file_id *id)
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;
215 if (!leases_db_init(false)) {
216 return NT_STATUS_INTERNAL_ERROR;
219 ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
220 if (!ok) {
221 return NT_STATUS_NO_MEMORY;
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;
229 db_value = dbwrap_record_get_value(rec);
230 if (db_value.dsize == 0) {
231 status = NT_STATUS_INTERNAL_ERROR;
232 goto out;
235 value = talloc(talloc_tos(), struct leases_db_value);
236 if (value == NULL) {
237 status = NT_STATUS_NO_MEMORY;
238 goto out;
241 blob.data = db_value.dptr;
242 blob.length = db_value.dsize;
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;
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;
261 if (i == value->num_file_ids) {
262 status = NT_STATUS_NOT_FOUND;
263 goto out;
266 value->ids[i] = value->ids[value->num_file_ids-1];
267 value->num_file_ids -= 1;
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;
284 if (DEBUGLEVEL >= 10) {
285 DEBUG(10, ("%s:\n", __func__));
286 NDR_PRINT_DEBUG(leases_db_value, value);
289 db_value = make_tdb_data(blob.data, blob.length);
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)));
298 out:
300 TALLOC_FREE(value);
301 TALLOC_FREE(rec);
302 return status;
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;
313 static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
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;
321 value = talloc(talloc_tos(), struct leases_db_value);
322 if (value == NULL) {
323 state->status = NT_STATUS_NO_MEMORY;
324 return;
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;
338 if (DEBUGLEVEL >= 10) {
339 DEBUG(10, ("%s:\n", __func__));
340 NDR_PRINT_DEBUG(leases_db_value, value);
343 state->parser(value->num_file_ids,
344 value->ids, value->filename, value->stream_name,
345 state->private_data);
347 TALLOC_FREE(value);
348 state->status = NT_STATUS_OK;
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)
360 TDB_DATA db_key;
361 struct leases_db_fetch_state state;
362 NTSTATUS status;
363 bool ok;
365 if (!leases_db_init(true)) {
366 return NT_STATUS_INTERNAL_ERROR;
369 ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
370 if (!ok) {
371 return NT_STATUS_NO_MEMORY;
374 state = (struct leases_db_fetch_state) {
375 .parser = parser,
376 .private_data = private_data,
377 .status = NT_STATUS_OK
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;
386 return state.status;
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 *filename_new,
393 const char *stream_name_new)
395 NTSTATUS status;
397 status = leases_db_del(client_guid,
398 lease_key,
399 id);
400 if (!NT_STATUS_IS_OK(status)) {
401 return status;
404 return leases_db_add(client_guid,
405 lease_key,
407 filename_new,
408 stream_name_new);