2 * File Server Remote VSS Protocol (FSRVP) persistent server state
4 * Copyright (C) David Disseldorp 2012-2015
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/>.
20 #include "source3/include/includes.h"
22 #include "source3/include/util_tdb.h"
23 #include "lib/dbwrap/dbwrap.h"
24 #include "lib/dbwrap/dbwrap_open.h"
25 #include "librpc/ndr/libndr.h"
26 #include "librpc/gen_ndr/ndr_fsrvp_state.h"
27 #include "srv_fss_private.h"
29 #define FSS_DB_KEY_VERSION "db_version"
30 #define FSS_DB_KEY_CONTEXT "context"
31 #define FSS_DB_KEY_SC_SET_COUNT "sc_set_count"
32 #define FSS_DB_KEY_PFX_SC_SET "sc_set/"
33 #define FSS_DB_KEY_PFX_SC "sc/"
34 #define FSS_DB_KEY_PFX_SMAP "smap/"
36 static NTSTATUS
fss_state_smap_store(TALLOC_CTX
*mem_ctx
,
37 struct db_context
*db
,
38 const char *sc_key_str
,
39 struct fss_sc_smap
*smap
)
43 const char *smap_key_str
;
44 struct fsrvp_state_smap smap_state
;
45 enum ndr_err_code ndr_ret
;
46 DATA_BLOB smap_state_blob
;
48 /* becomes sc_set/@sc_set_id/sc/@sc_id/smap/@sc_share_name */
49 smap_key_str
= talloc_asprintf(mem_ctx
, "%s/%s%s", sc_key_str
,
52 if (smap_key_str
== NULL
) {
53 return NT_STATUS_NO_MEMORY
;
56 smap_state
.share_name
= smap
->share_name
;
57 smap_state
.sc_share_name
= smap
->sc_share_name
;
58 /* @smap->sc_share_comment may be null if not exposed. */
59 if (smap
->sc_share_comment
!= NULL
) {
60 smap_state
.sc_share_comment
= smap
->sc_share_comment
;
62 smap_state
.sc_share_comment
= "";
64 smap_state
.is_exposed
= smap
->is_exposed
;
66 ndr_ret
= ndr_push_struct_blob(&smap_state_blob
, mem_ctx
,
68 (ndr_push_flags_fn_t
)ndr_push_fsrvp_state_smap
);
69 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
70 return NT_STATUS_INTERNAL_ERROR
;
73 val
.dsize
= smap_state_blob
.length
;
74 val
.dptr
= smap_state_blob
.data
;
76 status
= dbwrap_store(db
, string_term_tdb_data(smap_key_str
), val
, 0);
77 if (!NT_STATUS_IS_OK(status
)) {
84 static NTSTATUS
fss_state_sc_store(TALLOC_CTX
*mem_ctx
,
85 struct db_context
*db
,
86 const char *sc_set_key_str
,
91 const char *sc_key_str
;
92 struct fsrvp_state_sc sc_state
;
93 struct fss_sc_smap
*smap
;
94 enum ndr_err_code ndr_ret
;
95 DATA_BLOB sc_state_blob
;
97 /* becomes sc_set/@sc_set.id/sc/@sc_id */
98 sc_key_str
= talloc_asprintf(mem_ctx
, "%s/%s%s", sc_set_key_str
,
99 FSS_DB_KEY_PFX_SC
, sc
->id_str
);
100 if (sc_key_str
== NULL
) {
101 return NT_STATUS_NO_MEMORY
;
104 sc_state
.id_str
= sc
->id_str
;
105 sc_state
.volume_name
= sc
->volume_name
;
106 /* @sc->sc_path may be null if not committed, store empty str */
107 sc_state
.sc_path
= (sc
->sc_path
? sc
->sc_path
: "");
108 sc_state
.create_ts
= sc
->create_ts
;
109 sc_state
.smaps_count
= sc
->smaps_count
;
111 ndr_ret
= ndr_push_struct_blob(&sc_state_blob
, mem_ctx
,
113 (ndr_push_flags_fn_t
)ndr_push_fsrvp_state_sc
);
114 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
115 return NT_STATUS_INTERNAL_ERROR
;
118 val
.dsize
= sc_state_blob
.length
;
119 val
.dptr
= sc_state_blob
.data
;
121 status
= dbwrap_store(db
, string_term_tdb_data(sc_key_str
), val
, 0);
122 if (!NT_STATUS_IS_OK(status
)) {
126 for (smap
= sc
->smaps
; smap
; smap
= smap
->next
) {
127 status
= fss_state_smap_store(mem_ctx
, db
, sc_key_str
, smap
);
128 if (!NT_STATUS_IS_OK(status
)) {
136 static NTSTATUS
fss_state_sc_set_store(TALLOC_CTX
*mem_ctx
,
137 struct db_context
*db
,
138 struct fss_sc_set
*sc_set
)
142 const char *sc_set_key_str
;
144 struct fsrvp_state_sc_set sc_set_state
;
145 DATA_BLOB sc_set_state_blob
;
146 enum ndr_err_code ndr_ret
;
148 sc_set_key_str
= talloc_asprintf(mem_ctx
, "%s%s",
149 FSS_DB_KEY_PFX_SC_SET
,
151 if (sc_set_key_str
== NULL
) {
152 return NT_STATUS_NO_MEMORY
;
155 sc_set_state
.id_str
= sc_set
->id_str
;
156 sc_set_state
.state
= sc_set
->state
;
157 sc_set_state
.context
= sc_set
->context
;
158 sc_set_state
.scs_count
= sc_set
->scs_count
;
160 ndr_ret
= ndr_push_struct_blob(&sc_set_state_blob
, mem_ctx
,
162 (ndr_push_flags_fn_t
)ndr_push_fsrvp_state_sc_set
);
163 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
164 return NT_STATUS_INTERNAL_ERROR
;
167 val
.dsize
= sc_set_state_blob
.length
;
168 val
.dptr
= sc_set_state_blob
.data
;
170 status
= dbwrap_store(db
, string_term_tdb_data(sc_set_key_str
), val
, 0);
171 if (!NT_STATUS_IS_OK(status
)) {
175 for (sc
= sc_set
->scs
; sc
; sc
= sc
->next
) {
176 status
= fss_state_sc_store(mem_ctx
, db
, sc_set_key_str
, sc
);
177 if (!NT_STATUS_IS_OK(status
)) {
186 * write out the current fsrvp server state to a TDB. This clears any content
187 * currently written to the TDB.
189 _PRIVATE_ NTSTATUS
fss_state_store(TALLOC_CTX
*mem_ctx
,
190 struct fss_sc_set
*sc_sets
,
191 uint32_t sc_sets_count
,
195 struct db_context
*db
;
198 struct fss_sc_set
*sc_set
;
200 tmp_ctx
= talloc_new(mem_ctx
);
201 if (tmp_ctx
== NULL
) {
202 return NT_STATUS_NO_MEMORY
;
205 db
= db_open(tmp_ctx
, db_path
, 0, TDB_DEFAULT
, O_RDWR
| O_CREAT
,
206 0600, DBWRAP_LOCK_ORDER_1
, DBWRAP_FLAG_NONE
);
208 DEBUG(0, ("Failed to open fss state database %s\n", db_path
));
209 status
= NT_STATUS_ACCESS_DENIED
;
213 ret
= dbwrap_wipe(db
);
215 status
= NT_STATUS_UNSUCCESSFUL
;
219 status
= dbwrap_store_int32_bystring(db
, FSS_DB_KEY_VERSION
,
220 FSRVP_STATE_DB_VERSION
);
221 if (!NT_STATUS_IS_OK(status
)) {
225 ret
= dbwrap_transaction_start(db
);
227 status
= NT_STATUS_UNSUCCESSFUL
;
231 status
= dbwrap_store_int32_bystring(db
, FSS_DB_KEY_SC_SET_COUNT
,
233 if (!NT_STATUS_IS_OK(status
)) {
234 status
= NT_STATUS_UNSUCCESSFUL
;
235 goto err_trans_cancel
;
238 for (sc_set
= sc_sets
; sc_set
; sc_set
= sc_set
->next
) {
239 status
= fss_state_sc_set_store(tmp_ctx
, db
, sc_set
);
240 if (!NT_STATUS_IS_OK(status
)) {
241 goto err_trans_cancel
;
245 ret
= dbwrap_transaction_commit(db
);
247 status
= NT_STATUS_UNSUCCESSFUL
;
248 goto err_trans_cancel
;
252 talloc_free(tmp_ctx
);
256 dbwrap_transaction_cancel(db
);
260 talloc_free(tmp_ctx
);
264 static NTSTATUS
fss_state_smap_retrieve(TALLOC_CTX
*mem_ctx
,
267 struct fss_sc_smap
**smap_out
)
269 struct fss_sc_smap
*smap
;
270 struct fsrvp_state_smap smap_state
;
271 DATA_BLOB smap_state_blob
;
272 enum ndr_err_code ndr_ret
;
274 smap_state_blob
.length
= val
->dsize
;
275 smap_state_blob
.data
= val
->dptr
;
277 ndr_ret
= ndr_pull_struct_blob(&smap_state_blob
, mem_ctx
, &smap_state
,
278 (ndr_pull_flags_fn_t
)ndr_pull_fsrvp_state_smap
);
279 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
280 return NT_STATUS_INTERNAL_ERROR
;
283 smap
= talloc_zero(mem_ctx
, struct fss_sc_smap
);
285 return NT_STATUS_NO_MEMORY
;
288 smap
->share_name
= talloc_strdup(smap
, smap_state
.share_name
);
289 if (smap
->share_name
== NULL
) {
290 return NT_STATUS_NO_MEMORY
;
293 /* store the full path so that the hierarchy can be rebuilt */
294 smap
->sc_share_name
= talloc_strdup(smap
, (char *)key
->dptr
);
295 if (smap
->sc_share_name
== NULL
) {
296 return NT_STATUS_NO_MEMORY
;
299 /* sc_share_comment may be empty, keep null in such a case */
300 if (strlen(smap_state
.sc_share_comment
) > 0) {
301 smap
->sc_share_comment
= talloc_strdup(smap
,
302 smap_state
.sc_share_comment
);
303 if (smap
->sc_share_comment
== NULL
) {
304 return NT_STATUS_NO_MEMORY
;
308 smap
->is_exposed
= smap_state
.is_exposed
;
314 static NTSTATUS
fss_state_sc_retrieve(TALLOC_CTX
*mem_ctx
,
317 struct fss_sc
**sc_out
)
320 struct fsrvp_state_sc sc_state
;
321 DATA_BLOB sc_state_blob
;
322 enum ndr_err_code ndr_ret
;
324 sc_state_blob
.length
= val
->dsize
;
325 sc_state_blob
.data
= val
->dptr
;
327 ndr_ret
= ndr_pull_struct_blob(&sc_state_blob
, mem_ctx
, &sc_state
,
328 (ndr_pull_flags_fn_t
)ndr_pull_fsrvp_state_sc
);
329 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
330 return NT_STATUS_INTERNAL_ERROR
;
333 sc
= talloc_zero(mem_ctx
, struct fss_sc
);
335 return NT_STATUS_NO_MEMORY
;
338 /* store the full path so that the hierarchy can be rebuilt */
339 sc
->id_str
= talloc_strdup(sc
, (char *)key
->dptr
);
340 if (sc
->id_str
== NULL
) {
341 return NT_STATUS_NO_MEMORY
;
344 sc
->volume_name
= talloc_strdup(sc
, sc_state
.volume_name
);
345 if (sc
->volume_name
== NULL
) {
346 return NT_STATUS_NO_MEMORY
;
349 /* sc_path may be empty, keep null in such a case */
350 if (strlen(sc_state
.sc_path
) > 0) {
351 sc
->sc_path
= talloc_strdup(sc
, sc_state
.sc_path
);
352 if (sc
->sc_path
== NULL
) {
353 return NT_STATUS_NO_MEMORY
;
356 sc
->create_ts
= sc_state
.create_ts
;
357 sc
->smaps_count
= sc_state
.smaps_count
;
363 static NTSTATUS
fss_state_sc_set_retrieve(TALLOC_CTX
*mem_ctx
,
366 struct fss_sc_set
**sc_set_out
)
368 struct fss_sc_set
*sc_set
;
369 struct fsrvp_state_sc_set sc_set_state
;
370 DATA_BLOB sc_set_state_blob
;
371 enum ndr_err_code ndr_ret
;
373 sc_set_state_blob
.length
= val
->dsize
;
374 sc_set_state_blob
.data
= val
->dptr
;
376 ndr_ret
= ndr_pull_struct_blob(&sc_set_state_blob
, mem_ctx
,
378 (ndr_pull_flags_fn_t
)ndr_pull_fsrvp_state_sc_set
);
379 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
380 return NT_STATUS_INTERNAL_ERROR
;
383 sc_set
= talloc_zero(mem_ctx
, struct fss_sc_set
);
384 if (sc_set
== NULL
) {
385 return NT_STATUS_NO_MEMORY
;
388 /* store the full path so that the hierarchy can be rebuilt */
389 sc_set
->id_str
= talloc_strdup(sc_set
, (char *)key
->dptr
);
390 if (sc_set
->id_str
== NULL
) {
391 return NT_STATUS_NO_MEMORY
;
393 sc_set
->state
= sc_set_state
.state
;
394 sc_set
->context
= sc_set_state
.context
;
395 sc_set
->scs_count
= sc_set_state
.scs_count
;
397 *sc_set_out
= sc_set
;
401 struct fss_traverse_state
{
403 struct fss_sc_smap
*smaps
;
404 uint32_t smaps_count
;
407 struct fss_sc_set
*sc_sets
;
408 uint32_t sc_sets_count
;
409 NTSTATUS (*smap_retrieve
)(TALLOC_CTX
*mem_ctx
,
412 struct fss_sc_smap
**smap_out
);
413 NTSTATUS (*sc_retrieve
)(TALLOC_CTX
*mem_ctx
,
416 struct fss_sc
**sc_out
);
417 NTSTATUS (*sc_set_retrieve
)(TALLOC_CTX
*mem_ctx
,
420 struct fss_sc_set
**sc_set_out
);
423 static int fss_state_retrieve_traverse(struct db_record
*rec
,
427 struct fss_traverse_state
*trv_state
428 = (struct fss_traverse_state
*)private_data
;
429 TDB_DATA key
= dbwrap_record_get_key(rec
);
430 TDB_DATA val
= dbwrap_record_get_value(rec
);
432 /* order of checking is important here */
433 if (strstr((char *)key
.dptr
, FSS_DB_KEY_PFX_SMAP
) != NULL
) {
434 struct fss_sc_smap
*smap
;
435 status
= trv_state
->smap_retrieve(trv_state
->mem_ctx
,
437 if (!NT_STATUS_IS_OK(status
)) {
440 DLIST_ADD_END(trv_state
->smaps
, smap
);
441 trv_state
->smaps_count
++;
442 } else if (strstr((char *)key
.dptr
, FSS_DB_KEY_PFX_SC
) != NULL
) {
444 status
= trv_state
->sc_retrieve(trv_state
->mem_ctx
,
446 if (!NT_STATUS_IS_OK(status
)) {
449 DLIST_ADD_END(trv_state
->scs
, sc
);
450 trv_state
->scs_count
++;
451 } else if (strstr((char *)key
.dptr
, FSS_DB_KEY_PFX_SC_SET
) != NULL
) {
452 struct fss_sc_set
*sc_set
;
453 status
= trv_state
->sc_set_retrieve(trv_state
->mem_ctx
,
454 &key
, &val
, &sc_set
);
455 if (!NT_STATUS_IS_OK(status
)) {
458 DLIST_ADD_END(trv_state
->sc_sets
, sc_set
);
459 trv_state
->sc_sets_count
++;
461 /* global context and db vers */
462 DEBUG(4, ("Ignoring fss srv db entry with key %s\n", key
.dptr
));
468 static bool fss_state_smap_is_child(struct fss_sc
*sc
,
469 struct fss_sc_smap
*smap
)
471 return (strstr(smap
->sc_share_name
, sc
->id_str
) != NULL
);
474 static NTSTATUS
fss_state_hierarchize_smaps(struct fss_traverse_state
*trv_state
,
477 struct fss_sc_smap
*smap
;
478 struct fss_sc_smap
*smap_n
;
479 uint32_t smaps_moved
= 0;
481 for (smap
= trv_state
->smaps
; smap
; smap
= smap_n
) {
483 if (!fss_state_smap_is_child(sc
, smap
))
486 /* smap mem should be owned by parent sc */
487 talloc_steal(sc
, smap
);
488 DLIST_REMOVE(trv_state
->smaps
, smap
);
489 trv_state
->smaps_count
--;
490 DLIST_ADD_END(sc
->smaps
, smap
);
493 /* last component of the tdb key path is the sc share name */
494 SMB_ASSERT(strrchr(smap
->sc_share_name
, '/') != NULL
);
495 smap
->sc_share_name
= strrchr(smap
->sc_share_name
, '/') + 1;
498 if (sc
->smaps_count
!= smaps_moved
) {
499 DEBUG(0, ("Inconsistent smaps_count, expected %u, moved %u\n",
500 sc
->smaps_count
, smaps_moved
));
501 return NT_STATUS_UNSUCCESSFUL
;
507 static bool fss_state_sc_is_child(struct fss_sc_set
*sc_set
,
510 return (strstr(sc
->id_str
, sc_set
->id_str
) != NULL
);
513 static NTSTATUS
fss_state_hierarchize_scs(struct fss_traverse_state
*trv_state
,
514 struct fss_sc_set
*sc_set
)
519 uint32_t scs_moved
= 0;
521 for (sc
= trv_state
->scs
; sc
; sc
= sc_n
) {
523 if (!fss_state_sc_is_child(sc_set
, sc
))
526 /* sc mem should be owned by parent sc_set */
527 talloc_steal(sc_set
, sc
);
528 DLIST_REMOVE(trv_state
->scs
, sc
);
529 trv_state
->scs_count
--;
530 DLIST_ADD_END(sc_set
->scs
, sc
);
535 /* last component of the tdb key path is the sc GUID str */
536 SMB_ASSERT(strrchr(sc
->id_str
, '/') != NULL
);
537 sc
->id_str
= strrchr(sc
->id_str
, '/') + 1;
539 status
= GUID_from_string(sc
->id_str
, &sc
->id
);
540 if (!NT_STATUS_IS_OK(status
)) {
544 status
= fss_state_hierarchize_smaps(trv_state
, sc
);
545 if (!NT_STATUS_IS_OK(status
)) {
550 if (sc_set
->scs_count
!= scs_moved
) {
551 DEBUG(0, ("Inconsistent scs_count, expected %u, moved %u\n",
552 sc_set
->scs_count
, scs_moved
));
553 status
= NT_STATUS_UNSUCCESSFUL
;
563 static NTSTATUS
fss_state_hierarchize(struct fss_traverse_state
*trv_state
,
564 struct fss_sc_set
**sc_sets
,
565 uint32_t *sc_sets_count
)
568 struct fss_sc_set
*sc_set
;
569 struct fss_sc_set
*sc_set_n
;
573 for (sc_set
= trv_state
->sc_sets
; sc_set
; sc_set
= sc_set_n
) {
574 sc_set_n
= sc_set
->next
;
575 /* sc_set mem already owned by trv_state->mem_ctx */
576 DLIST_REMOVE(trv_state
->sc_sets
, sc_set
);
577 trv_state
->sc_sets_count
--;
578 DLIST_ADD_END(*sc_sets
, sc_set
);
581 /* last component of the tdb key path is the sc_set GUID str */
582 SMB_ASSERT(strrchr(sc_set
->id_str
, '/') != NULL
);
583 sc_set
->id_str
= strrchr(sc_set
->id_str
, '/') + 1;
585 status
= GUID_from_string(sc_set
->id_str
, &sc_set
->id
);
586 if (!NT_STATUS_IS_OK(status
)) {
590 status
= fss_state_hierarchize_scs(trv_state
, sc_set
);
591 if (!NT_STATUS_IS_OK(status
)) {
602 _PRIVATE_ NTSTATUS
fss_state_retrieve(TALLOC_CTX
*mem_ctx
,
603 struct fss_sc_set
**sc_sets
,
604 uint32_t *sc_sets_count
,
607 struct db_context
*db
;
609 struct fss_traverse_state trv_state
;
616 memset(&trv_state
, 0, sizeof(trv_state
));
617 trv_state
.mem_ctx
= talloc_new(mem_ctx
);
618 if (trv_state
.mem_ctx
== NULL
) {
619 status
= NT_STATUS_NO_MEMORY
;
623 /* set callbacks for unmarshalling on-disk structures */
624 trv_state
.smap_retrieve
= fss_state_smap_retrieve
;
625 trv_state
.sc_retrieve
= fss_state_sc_retrieve
;
626 trv_state
.sc_set_retrieve
= fss_state_sc_set_retrieve
;
628 db
= db_open(trv_state
.mem_ctx
, db_path
, 0, TDB_DEFAULT
,
629 O_RDONLY
, 0600, DBWRAP_LOCK_ORDER_1
, DBWRAP_FLAG_NONE
);
631 if ((db
== NULL
) && (err
== ENOENT
)) {
632 DEBUG(4, ("fss state TDB does not exist for retrieval\n"));
633 status
= NT_STATUS_OK
;
635 } else if (db
== NULL
) {
636 DEBUG(0, ("Failed to open fss state TDB: %s\n",
638 status
= NT_STATUS_ACCESS_DENIED
;
642 status
= dbwrap_fetch_int32_bystring(db
, FSS_DB_KEY_VERSION
,
644 if (!NT_STATUS_IS_OK(status
)) {
645 DEBUG(0, ("failed to fetch version from fss state tdb: %s\n",
648 } else if (vers
!= FSRVP_STATE_DB_VERSION
) {
649 DEBUG(0, ("Unsupported fss tdb version %d, expected %d\n",
650 vers
, FSRVP_STATE_DB_VERSION
));
651 status
= NT_STATUS_UNSUCCESSFUL
;
655 status
= dbwrap_traverse_read(db
,
656 fss_state_retrieve_traverse
,
659 if (!NT_STATUS_IS_OK(status
)) {
663 status
= fss_state_hierarchize(&trv_state
, sc_sets
, sc_sets_count
);
664 if (!NT_STATUS_IS_OK(status
)) {
665 DEBUG(0, ("Failed to form fss state hierarchy\n"));
669 /* check whether anything was left without a parent */
670 if (trv_state
.sc_sets_count
!= 0) {
671 DEBUG(0, ("%d shadow copy set orphans in %s tdb\n",
672 trv_state
.sc_sets_count
, db_path
));
673 status
= NT_STATUS_UNSUCCESSFUL
;
676 if (trv_state
.scs_count
!= 0) {
677 DEBUG(0, ("%d shadow copy orphans in %s tdb\n",
678 trv_state
.scs_count
, db_path
));
679 status
= NT_STATUS_UNSUCCESSFUL
;
682 if (trv_state
.smaps_count
!= 0) {
683 DEBUG(0, ("%d share map orphans in %s tdb\n",
684 trv_state
.smaps_count
, db_path
));
685 status
= NT_STATUS_UNSUCCESSFUL
;
695 talloc_free(trv_state
.mem_ctx
);