1 /*
2  * File Server Remote VSS Protocol (FSRVP) persistent server state
3  *
4  * Copyright (C) David Disseldorp	2012-2015
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 #include "source3/include/includes.h"
21 #include <fcntl.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"
28 
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/"
35 
fss_state_smap_store(TALLOC_CTX * mem_ctx,struct db_context * db,const char * sc_key_str,struct fss_sc_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)
40 {
41 	NTSTATUS status;
42 	TDB_DATA val;
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;
47 
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,
50 				       FSS_DB_KEY_PFX_SMAP,
51 				       smap->sc_share_name);
52 	if (smap_key_str == NULL) {
53 		return NT_STATUS_NO_MEMORY;
54 	}
55 
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;
61 	} else {
62 		smap_state.sc_share_comment = "";
63 	}
64 	smap_state.is_exposed = smap->is_exposed;
65 
66 	ndr_ret = ndr_push_struct_blob(&smap_state_blob, mem_ctx,
67 				       &smap_state,
68 				(ndr_push_flags_fn_t)ndr_push_fsrvp_state_smap);
69 	if (ndr_ret != NDR_ERR_SUCCESS) {
70 		return NT_STATUS_INTERNAL_ERROR;
71 	}
72 
73 	val.dsize = smap_state_blob.length;
74 	val.dptr = smap_state_blob.data;
75 
76 	status = dbwrap_store(db, string_term_tdb_data(smap_key_str), val, 0);
77 	if (!NT_STATUS_IS_OK(status)) {
78 		return status;
79 	}
80 
81 	return NT_STATUS_OK;
82 }
83 
fss_state_sc_store(TALLOC_CTX * mem_ctx,struct db_context * db,const char * sc_set_key_str,struct fss_sc * sc)84 static NTSTATUS fss_state_sc_store(TALLOC_CTX *mem_ctx,
85 				   struct db_context *db,
86 				   const char *sc_set_key_str,
87 				   struct fss_sc *sc)
88 {
89 	NTSTATUS status;
90 	TDB_DATA val;
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;
96 
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;
102 	}
103 
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;
110 
111 	ndr_ret = ndr_push_struct_blob(&sc_state_blob, mem_ctx,
112 				       &sc_state,
113 				(ndr_push_flags_fn_t)ndr_push_fsrvp_state_sc);
114 	if (ndr_ret != NDR_ERR_SUCCESS) {
115 		return NT_STATUS_INTERNAL_ERROR;
116 	}
117 
118 	val.dsize = sc_state_blob.length;
119 	val.dptr = sc_state_blob.data;
120 
121 	status = dbwrap_store(db, string_term_tdb_data(sc_key_str), val, 0);
122 	if (!NT_STATUS_IS_OK(status)) {
123 		return status;
124 	}
125 
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)) {
129 			return status;
130 		}
131 	}
132 
133 	return NT_STATUS_OK;
134 }
135 
fss_state_sc_set_store(TALLOC_CTX * mem_ctx,struct db_context * db,struct fss_sc_set * sc_set)136 static NTSTATUS fss_state_sc_set_store(TALLOC_CTX *mem_ctx,
137 				       struct db_context *db,
138 				       struct fss_sc_set *sc_set)
139 {
140 	NTSTATUS status;
141 	TDB_DATA val;
142 	const char *sc_set_key_str;
143 	struct fss_sc *sc;
144 	struct fsrvp_state_sc_set sc_set_state;
145 	DATA_BLOB sc_set_state_blob;
146 	enum ndr_err_code ndr_ret;
147 
148 	sc_set_key_str = talloc_asprintf(mem_ctx, "%s%s",
149 					 FSS_DB_KEY_PFX_SC_SET,
150 					 sc_set->id_str);
151 	if (sc_set_key_str == NULL) {
152 		return NT_STATUS_NO_MEMORY;
153 	}
154 
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;
159 
160 	ndr_ret = ndr_push_struct_blob(&sc_set_state_blob, mem_ctx,
161 				       &sc_set_state,
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;
165 	}
166 
167 	val.dsize = sc_set_state_blob.length;
168 	val.dptr = sc_set_state_blob.data;
169 
170 	status = dbwrap_store(db, string_term_tdb_data(sc_set_key_str), val, 0);
171 	if (!NT_STATUS_IS_OK(status)) {
172 		return status;
173 	}
174 
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)) {
178 			return status;
179 		}
180 	}
181 
182 	return NT_STATUS_OK;
183 }
184 
185 /*
186  * write out the current fsrvp server state to a TDB. This clears any content
187  * currently written to the TDB.
188  */
fss_state_store(TALLOC_CTX * mem_ctx,struct fss_sc_set * sc_sets,uint32_t sc_sets_count,const char * db_path)189 _PRIVATE_ NTSTATUS fss_state_store(TALLOC_CTX *mem_ctx,
190 			 struct fss_sc_set *sc_sets,
191 			 uint32_t sc_sets_count,
192 			 const char *db_path)
193 {
194 	TALLOC_CTX *tmp_ctx;
195 	struct db_context *db;
196 	NTSTATUS status;
197 	int ret;
198 	struct fss_sc_set *sc_set;
199 
200 	tmp_ctx = talloc_new(mem_ctx);
201 	if (tmp_ctx == NULL) {
202 		return NT_STATUS_NO_MEMORY;
203 	}
204 
205 	db = db_open(tmp_ctx, db_path, 0, TDB_DEFAULT,  O_RDWR | O_CREAT,
206 		     0600, DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
207 	if (db == NULL) {
208 		DEBUG(0, ("Failed to open fss state database %s\n", db_path));
209 		status = NT_STATUS_ACCESS_DENIED;
210 		goto err_ctx_free;
211 	}
212 
213 	ret = dbwrap_wipe(db);
214 	if (ret != 0) {
215 		status = NT_STATUS_UNSUCCESSFUL;
216 		goto err_db_free;
217 	}
218 
219 	status = dbwrap_store_int32_bystring(db, FSS_DB_KEY_VERSION,
220 					     FSRVP_STATE_DB_VERSION);
221 	if (!NT_STATUS_IS_OK(status)) {
222 		goto err_db_free;
223 	}
224 
225 	ret = dbwrap_transaction_start(db);
226 	if (ret != 0) {
227 		status = NT_STATUS_UNSUCCESSFUL;
228 		goto err_db_free;
229 	}
230 
231 	status = dbwrap_store_int32_bystring(db, FSS_DB_KEY_SC_SET_COUNT,
232 					     sc_sets_count);
233 	if (!NT_STATUS_IS_OK(status)) {
234 		status = NT_STATUS_UNSUCCESSFUL;
235 		goto err_trans_cancel;
236 	}
237 
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;
242 		}
243 	}
244 
245 	ret = dbwrap_transaction_commit(db);
246 	if (ret != 0) {
247 		status = NT_STATUS_UNSUCCESSFUL;
248 		goto err_trans_cancel;
249 	}
250 
251 	talloc_free(db);
252 	talloc_free(tmp_ctx);
253 	return NT_STATUS_OK;
254 
255 err_trans_cancel:
256 	dbwrap_transaction_cancel(db);
257 err_db_free:
258 	talloc_free(db);
259 err_ctx_free:
260 	talloc_free(tmp_ctx);
261 	return status;
262 }
263 
fss_state_smap_retrieve(TALLOC_CTX * mem_ctx,TDB_DATA * key,TDB_DATA * val,struct fss_sc_smap ** smap_out)264 static NTSTATUS fss_state_smap_retrieve(TALLOC_CTX *mem_ctx,
265 					TDB_DATA *key,
266 					TDB_DATA *val,
267 					struct fss_sc_smap **smap_out)
268 {
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;
273 
274 	smap_state_blob.length = val->dsize;
275 	smap_state_blob.data = val->dptr;
276 
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;
281 	}
282 
283 	smap = talloc_zero(mem_ctx, struct fss_sc_smap);
284 	if (smap == NULL) {
285 		return NT_STATUS_NO_MEMORY;
286 	}
287 
288 	smap->share_name = talloc_strdup(smap, smap_state.share_name);
289 	if (smap->share_name == NULL) {
290 		return NT_STATUS_NO_MEMORY;
291 	}
292 
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;
297 	}
298 
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;
305 		}
306 	}
307 
308 	smap->is_exposed = smap_state.is_exposed;
309 
310 	*smap_out = smap;
311 	return NT_STATUS_OK;
312 }
313 
fss_state_sc_retrieve(TALLOC_CTX * mem_ctx,TDB_DATA * key,TDB_DATA * val,struct fss_sc ** sc_out)314 static NTSTATUS fss_state_sc_retrieve(TALLOC_CTX *mem_ctx,
315 				      TDB_DATA *key,
316 				      TDB_DATA *val,
317 				      struct fss_sc **sc_out)
318 {
319 	struct fss_sc *sc;
320 	struct fsrvp_state_sc sc_state;
321 	DATA_BLOB sc_state_blob;
322 	enum ndr_err_code ndr_ret;
323 
324 	sc_state_blob.length = val->dsize;
325 	sc_state_blob.data = val->dptr;
326 
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;
331 	}
332 
333 	sc = talloc_zero(mem_ctx, struct fss_sc);
334 	if (sc == NULL) {
335 		return NT_STATUS_NO_MEMORY;
336 	}
337 
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;
342 	}
343 
344 	sc->volume_name = talloc_strdup(sc, sc_state.volume_name);
345 	if (sc->volume_name == NULL) {
346 		return NT_STATUS_NO_MEMORY;
347 	}
348 
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;
354 		}
355 	}
356 	sc->create_ts = sc_state.create_ts;
357 	sc->smaps_count = sc_state.smaps_count;
358 
359 	*sc_out = sc;
360 	return NT_STATUS_OK;
361 }
362 
fss_state_sc_set_retrieve(TALLOC_CTX * mem_ctx,TDB_DATA * key,TDB_DATA * val,struct fss_sc_set ** sc_set_out)363 static NTSTATUS fss_state_sc_set_retrieve(TALLOC_CTX *mem_ctx,
364 					  TDB_DATA *key,
365 					  TDB_DATA *val,
366 					  struct fss_sc_set **sc_set_out)
367 {
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;
372 
373 	sc_set_state_blob.length = val->dsize;
374 	sc_set_state_blob.data = val->dptr;
375 
376 	ndr_ret = ndr_pull_struct_blob(&sc_set_state_blob, mem_ctx,
377 				       &sc_set_state,
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;
381 	}
382 
383 	sc_set = talloc_zero(mem_ctx, struct fss_sc_set);
384 	if (sc_set == NULL) {
385 		return NT_STATUS_NO_MEMORY;
386 	}
387 
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;
392 	}
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;
396 
397 	*sc_set_out = sc_set;
398 	return NT_STATUS_OK;
399 }
400 
401 struct fss_traverse_state {
402 	TALLOC_CTX *mem_ctx;
403 	struct fss_sc_smap *smaps;
404 	uint32_t smaps_count;
405 	struct fss_sc *scs;
406 	uint32_t scs_count;
407 	struct fss_sc_set *sc_sets;
408 	uint32_t sc_sets_count;
409 	NTSTATUS (*smap_retrieve)(TALLOC_CTX *mem_ctx,
410 				  TDB_DATA *key,
411 				  TDB_DATA *val,
412 				  struct fss_sc_smap **smap_out);
413 	NTSTATUS (*sc_retrieve)(TALLOC_CTX *mem_ctx,
414 				TDB_DATA *key,
415 				TDB_DATA *val,
416 				struct fss_sc **sc_out);
417 	NTSTATUS (*sc_set_retrieve)(TALLOC_CTX *mem_ctx,
418 				    TDB_DATA *key,
419 				    TDB_DATA *val,
420 				    struct fss_sc_set **sc_set_out);
421 };
422 
fss_state_retrieve_traverse(struct db_record * rec,void * private_data)423 static int fss_state_retrieve_traverse(struct db_record *rec,
424 				       void *private_data)
425 {
426 	NTSTATUS status;
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);
431 
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,
436 						  &key, &val, &smap);
437 		if (!NT_STATUS_IS_OK(status)) {
438 			return -1;
439 		}
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) {
443 		struct fss_sc *sc;
444 		status = trv_state->sc_retrieve(trv_state->mem_ctx,
445 						&key, &val, &sc);
446 		if (!NT_STATUS_IS_OK(status)) {
447 			return -1;
448 		}
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)) {
456 			return -1;
457 		}
458 		DLIST_ADD_END(trv_state->sc_sets, sc_set);
459 		trv_state->sc_sets_count++;
460 	} else {
461 		/* global context and db vers */
462 		DEBUG(4, ("Ignoring fss srv db entry with key %s\n", key.dptr));
463 	}
464 
465 	return 0;
466 }
467 
fss_state_smap_is_child(struct fss_sc * sc,struct fss_sc_smap * smap)468 static bool fss_state_smap_is_child(struct fss_sc *sc,
469 				    struct fss_sc_smap *smap)
470 {
471 	return (strstr(smap->sc_share_name, sc->id_str) != NULL);
472 }
473 
fss_state_hierarchize_smaps(struct fss_traverse_state * trv_state,struct fss_sc * sc)474 static NTSTATUS fss_state_hierarchize_smaps(struct fss_traverse_state *trv_state,
475 					    struct fss_sc *sc)
476 {
477 	struct fss_sc_smap *smap;
478 	struct fss_sc_smap *smap_n;
479 	uint32_t smaps_moved = 0;
480 
481 	for (smap = trv_state->smaps; smap; smap = smap_n) {
482 		smap_n = smap->next;
483 		if (!fss_state_smap_is_child(sc, smap))
484 			continue;
485 
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);
491 		smaps_moved++;
492 
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;
496 	}
497 
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;
502 	}
503 
504 	return NT_STATUS_OK;
505 }
506 
fss_state_sc_is_child(struct fss_sc_set * sc_set,struct fss_sc * sc)507 static bool fss_state_sc_is_child(struct fss_sc_set *sc_set,
508 				  struct fss_sc *sc)
509 {
510 	return (strstr(sc->id_str, sc_set->id_str) != NULL);
511 }
512 
fss_state_hierarchize_scs(struct fss_traverse_state * trv_state,struct fss_sc_set * sc_set)513 static NTSTATUS fss_state_hierarchize_scs(struct fss_traverse_state *trv_state,
514 					  struct fss_sc_set *sc_set)
515 {
516 	NTSTATUS status;
517 	struct fss_sc *sc;
518 	struct fss_sc *sc_n;
519 	uint32_t scs_moved = 0;
520 
521 	for (sc = trv_state->scs; sc; sc = sc_n) {
522 		sc_n = sc->next;
523 		if (!fss_state_sc_is_child(sc_set, sc))
524 			continue;
525 
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);
531 		scs_moved++;
532 
533 		sc->sc_set = sc_set;
534 
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;
538 
539 		status = GUID_from_string(sc->id_str, &sc->id);
540 		if (!NT_STATUS_IS_OK(status)) {
541 			goto err_out;
542 		}
543 
544 		status = fss_state_hierarchize_smaps(trv_state, sc);
545 		if (!NT_STATUS_IS_OK(status)) {
546 			goto err_out;
547 		}
548 	}
549 
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;
554 		goto err_out;
555 	}
556 
557 	return NT_STATUS_OK;
558 
559 err_out:
560 	return status;
561 }
562 
fss_state_hierarchize(struct fss_traverse_state * trv_state,struct fss_sc_set ** sc_sets,uint32_t * sc_sets_count)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)
566 {
567 	NTSTATUS status;
568 	struct fss_sc_set *sc_set;
569 	struct fss_sc_set *sc_set_n;
570 	uint32_t i = 0;
571 
572 	*sc_sets = NULL;
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);
579 		i++;
580 
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;
584 
585 		status = GUID_from_string(sc_set->id_str, &sc_set->id);
586 		if (!NT_STATUS_IS_OK(status)) {
587 			goto err_out;
588 		}
589 
590 		status = fss_state_hierarchize_scs(trv_state, sc_set);
591 		if (!NT_STATUS_IS_OK(status)) {
592 			goto err_out;
593 		}
594 	}
595 	*sc_sets_count = i;
596 	return NT_STATUS_OK;
597 
598 err_out:
599 	return status;
600 }
601 
fss_state_retrieve(TALLOC_CTX * mem_ctx,struct fss_sc_set ** sc_sets,uint32_t * sc_sets_count,const char * db_path)602 _PRIVATE_ NTSTATUS fss_state_retrieve(TALLOC_CTX *mem_ctx,
603 			    struct fss_sc_set **sc_sets,
604 			    uint32_t *sc_sets_count,
605 			    const char *db_path)
606 {
607 	struct db_context *db;
608 	NTSTATUS status;
609 	struct fss_traverse_state trv_state;
610 	int err;
611 	int rec_count;
612 	int vers;
613 	*sc_sets = NULL;
614 	*sc_sets_count = 0;
615 
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;
620 		goto err_out;
621 	}
622 
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;
627 
628 	db = db_open(trv_state.mem_ctx, db_path, 0, TDB_DEFAULT,
629 		     O_RDONLY, 0600, DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
630 	err = errno;
631 	if ((db == NULL) && (err == ENOENT)) {
632 		DEBUG(4, ("fss state TDB does not exist for retrieval\n"));
633 		status = NT_STATUS_OK;
634 		goto err_ts_free;
635 	} else if (db == NULL) {
636 		DEBUG(0, ("Failed to open fss state TDB: %s\n",
637 			  strerror(err)));
638 		status = NT_STATUS_ACCESS_DENIED;
639 		goto err_ts_free;
640 	}
641 
642 	status = dbwrap_fetch_int32_bystring(db, FSS_DB_KEY_VERSION,
643 					     &vers);
644 	if (!NT_STATUS_IS_OK(status)) {
645 		DEBUG(0, ("failed to fetch version from fss state tdb: %s\n",
646 			  nt_errstr(status)));
647 		goto err_db_free;
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;
652 		goto err_db_free;
653 	}
654 
655 	status = dbwrap_traverse_read(db,
656 				      fss_state_retrieve_traverse,
657 				      &trv_state,
658 				      &rec_count);
659 	if (!NT_STATUS_IS_OK(status)) {
660 		goto err_db_free;
661 	}
662 
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"));
666 		goto err_db_free;
667 	}
668 
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;
674 		goto err_db_free;
675 	}
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;
680 		goto err_db_free;
681 	}
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;
686 		goto err_db_free;
687 	}
688 	talloc_free(db);
689 
690 	return NT_STATUS_OK;
691 
692 err_db_free:
693 	talloc_free(db);
694 err_ts_free:
695 	talloc_free(trv_state.mem_ctx);
696 err_out:
697 	return status;
698 }
699