s3: Slightly simplify is_stat_open
[Samba/gebeck_regimport.git] / source4 / dsdb / kcc / kcc_periodic.c
blob8f705d7aa13acb899dbe6222a8ab7a1b7e0f8414
1 /*
2 Unix SMB/CIFS mplementation.
3 KCC service periodic handling
5 Copyright (C) Andrew Tridgell 2009
6 based on repl service code
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "lib/events/events.h"
25 #include "dsdb/samdb/samdb.h"
26 #include "auth/auth.h"
27 #include "smbd/service.h"
28 #include "lib/messaging/irpc.h"
29 #include "dsdb/kcc/kcc_connection.h"
30 #include "dsdb/kcc/kcc_service.h"
31 #include <ldb_errors.h>
32 #include "../lib/util/dlinklist.h"
33 #include "librpc/gen_ndr/ndr_misc.h"
34 #include "librpc/gen_ndr/ndr_drsuapi.h"
35 #include "librpc/gen_ndr/ndr_drsblobs.h"
36 #include "librpc/gen_ndr/ndr_irpc_c.h"
37 #include "param/param.h"
38 #include "dsdb/common/util.h"
41 * see if two repsFromToBlob blobs are for the same source DSA
43 static bool kccsrv_same_source_dsa(struct repsFromToBlob *r1, struct repsFromToBlob *r2)
45 return GUID_compare(&r1->ctr.ctr1.source_dsa_obj_guid,
46 &r2->ctr.ctr1.source_dsa_obj_guid) == 0;
50 * see if a repsFromToBlob is in a list
52 static bool reps_in_list(struct repsFromToBlob *r, struct repsFromToBlob *reps, uint32_t count)
54 uint32_t i;
55 for (i=0; i<count; i++) {
56 if (kccsrv_same_source_dsa(r, &reps[i])) {
57 return true;
60 return false;
64 make sure we only add repsFrom entries for DCs who are masters for
65 the partition
67 static bool check_MasterNC(struct kccsrv_partition *p, struct repsFromToBlob *r,
68 struct ldb_result *res)
70 struct repsFromTo1 *r1 = &r->ctr.ctr1;
71 struct GUID invocation_id = r1->source_dsa_invocation_id;
72 unsigned int i, j;
73 TALLOC_CTX *tmp_ctx;
75 /* we are expecting only version 1 */
76 SMB_ASSERT(r->version == 1);
78 tmp_ctx = talloc_new(p);
79 if (!tmp_ctx) {
80 return false;
83 for (i=0; i<res->count; i++) {
84 struct ldb_message *msg = res->msgs[i];
85 struct ldb_message_element *el;
86 struct ldb_dn *dn;
88 struct GUID id2 = samdb_result_guid(msg, "invocationID");
89 if (GUID_all_zero(&id2) ||
90 !GUID_equal(&invocation_id, &id2)) {
91 continue;
94 el = ldb_msg_find_element(msg, "msDS-hasMasterNCs");
95 if (!el || el->num_values == 0) {
96 el = ldb_msg_find_element(msg, "hasMasterNCs");
97 if (!el || el->num_values == 0) {
98 continue;
101 for (j=0; j<el->num_values; j++) {
102 dn = ldb_dn_from_ldb_val(tmp_ctx, p->service->samdb, &el->values[j]);
103 if (!ldb_dn_validate(dn)) {
104 talloc_free(dn);
105 continue;
107 if (ldb_dn_compare(dn, p->dn) == 0) {
108 DEBUG(5,("%s %s match on %s in %s\n",
109 r1->other_info->dns_name,
110 el->name,
111 ldb_dn_get_linearized(dn),
112 ldb_dn_get_linearized(msg->dn)));
113 talloc_free(tmp_ctx);
114 return true;
116 talloc_free(dn);
119 talloc_free(tmp_ctx);
120 return false;
123 struct kccsrv_notify_drepl_server_state {
124 struct dreplsrv_refresh r;
127 static void kccsrv_notify_drepl_server_done(struct tevent_req *subreq);
130 * Force dreplsrv to update its state as topology is changed
132 static void kccsrv_notify_drepl_server(struct kccsrv_service *s,
133 TALLOC_CTX *mem_ctx)
135 struct kccsrv_notify_drepl_server_state *state;
136 struct dcerpc_binding_handle *irpc_handle;
137 struct tevent_req *subreq;
139 state = talloc_zero(s, struct kccsrv_notify_drepl_server_state);
140 if (state == NULL) {
141 return;
144 irpc_handle = irpc_binding_handle_by_name(state, s->task->msg_ctx,
145 "dreplsrv", &ndr_table_irpc);
146 if (irpc_handle == NULL) {
147 /* dreplsrv is not running yet */
148 TALLOC_FREE(state);
149 return;
152 subreq = dcerpc_dreplsrv_refresh_r_send(state, s->task->event_ctx,
153 irpc_handle, &state->r);
154 if (subreq == NULL) {
155 TALLOC_FREE(state);
156 return;
158 tevent_req_set_callback(subreq, kccsrv_notify_drepl_server_done, state);
161 static void kccsrv_notify_drepl_server_done(struct tevent_req *subreq)
163 struct kccsrv_notify_drepl_server_state *state =
164 tevent_req_callback_data(subreq,
165 struct kccsrv_notify_drepl_server_state);
166 NTSTATUS status;
168 status = dcerpc_dreplsrv_refresh_r_recv(subreq, state);
169 TALLOC_FREE(subreq);
171 /* we don't care about errors */
172 TALLOC_FREE(state);
175 uint32_t kccsrv_replica_flags(struct kccsrv_service *s)
177 if (s->am_rodc) {
178 return DRSUAPI_DRS_INIT_SYNC |
179 DRSUAPI_DRS_PER_SYNC |
180 DRSUAPI_DRS_ADD_REF |
181 DRSUAPI_DRS_SPECIAL_SECRET_PROCESSING |
182 DRSUAPI_DRS_GET_ALL_GROUP_MEMBERSHIP |
183 DRSUAPI_DRS_NONGC_RO_REP;
185 return DRSUAPI_DRS_INIT_SYNC |
186 DRSUAPI_DRS_PER_SYNC |
187 DRSUAPI_DRS_ADD_REF |
188 DRSUAPI_DRS_WRIT_REP;
192 * add any missing repsFrom structures to our partitions
194 NTSTATUS kccsrv_add_repsFrom(struct kccsrv_service *s, TALLOC_CTX *mem_ctx,
195 struct repsFromToBlob *reps, uint32_t count,
196 struct ldb_result *res)
198 struct kccsrv_partition *p;
199 bool notify_dreplsrv = false;
200 uint32_t replica_flags = kccsrv_replica_flags(s);
202 /* update the repsFrom on all partitions */
203 for (p=s->partitions; p; p=p->next) {
204 struct repsFromToBlob *our_reps;
205 uint32_t our_count;
206 WERROR werr;
207 uint32_t i, j;
208 bool modified = false;
210 werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsFrom", &our_reps, &our_count);
211 if (!W_ERROR_IS_OK(werr)) {
212 DEBUG(0,(__location__ ": Failed to load repsFrom from %s - %s\n",
213 ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
214 return NT_STATUS_INTERNAL_DB_CORRUPTION;
217 /* see if the entry already exists */
218 for (i=0; i<count; i++) {
219 for (j=0; j<our_count; j++) {
220 if (kccsrv_same_source_dsa(&reps[i], &our_reps[j])) {
221 /* we already have this one -
222 check the replica_flags are right */
223 if (replica_flags != our_reps[j].ctr.ctr1.replica_flags) {
224 /* we need to update the old one with
225 * the new flags
227 our_reps[j].ctr.ctr1.replica_flags = replica_flags;
228 modified = true;
230 break;
233 if (j == our_count) {
234 /* we don't have the new one - add it
235 * if it is a master
237 if (res && !check_MasterNC(p, &reps[i], res)) {
238 /* its not a master, we don't
239 want to pull from it */
240 continue;
242 /* we need to add it to our repsFrom */
243 our_reps = talloc_realloc(mem_ctx, our_reps, struct repsFromToBlob, our_count+1);
244 NT_STATUS_HAVE_NO_MEMORY(our_reps);
245 our_reps[our_count] = reps[i];
246 our_reps[our_count].ctr.ctr1.replica_flags = replica_flags;
247 our_count++;
248 modified = true;
249 DEBUG(4,(__location__ ": Added repsFrom for %s\n",
250 reps[i].ctr.ctr1.other_info->dns_name));
254 /* remove any stale ones */
255 for (i=0; i<our_count; i++) {
256 if (!reps_in_list(&our_reps[i], reps, count) ||
257 (res && !check_MasterNC(p, &our_reps[i], res))) {
258 DEBUG(4,(__location__ ": Removed repsFrom for %s\n",
259 our_reps[i].ctr.ctr1.other_info->dns_name));
260 memmove(&our_reps[i], &our_reps[i+1], (our_count-(i+1))*sizeof(our_reps[0]));
261 our_count--;
262 i--;
263 modified = true;
267 if (modified) {
268 werr = dsdb_savereps(s->samdb, mem_ctx, p->dn, "repsFrom", our_reps, our_count);
269 if (!W_ERROR_IS_OK(werr)) {
270 DEBUG(0,(__location__ ": Failed to save repsFrom to %s - %s\n",
271 ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
272 return NT_STATUS_INTERNAL_DB_CORRUPTION;
274 /* dreplsrv should refresh its state */
275 notify_dreplsrv = true;
278 /* remove stale repsTo entries */
279 modified = false;
280 werr = dsdb_loadreps(s->samdb, mem_ctx, p->dn, "repsTo", &our_reps, &our_count);
281 if (!W_ERROR_IS_OK(werr)) {
282 DEBUG(0,(__location__ ": Failed to load repsTo from %s - %s\n",
283 ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
284 return NT_STATUS_INTERNAL_DB_CORRUPTION;
287 /* remove any stale ones */
288 for (i=0; i<our_count; i++) {
289 if (!reps_in_list(&our_reps[i], reps, count)) {
290 DEBUG(4,(__location__ ": Removed repsTo for %s\n",
291 our_reps[i].ctr.ctr1.other_info->dns_name));
292 memmove(&our_reps[i], &our_reps[i+1], (our_count-(i+1))*sizeof(our_reps[0]));
293 our_count--;
294 i--;
295 modified = true;
299 if (modified) {
300 werr = dsdb_savereps(s->samdb, mem_ctx, p->dn, "repsTo", our_reps, our_count);
301 if (!W_ERROR_IS_OK(werr)) {
302 DEBUG(0,(__location__ ": Failed to save repsTo to %s - %s\n",
303 ldb_dn_get_linearized(p->dn), ldb_errstring(s->samdb)));
304 return NT_STATUS_INTERNAL_DB_CORRUPTION;
306 /* dreplsrv should refresh its state */
307 notify_dreplsrv = true;
311 /* notify dreplsrv toplogy has changed */
312 if (notify_dreplsrv) {
313 kccsrv_notify_drepl_server(s, mem_ctx);
316 return NT_STATUS_OK;
322 form a unique list of DNs from a search result and a given set of attributes
324 static int kccsrv_dn_list(struct ldb_context *ldb, struct ldb_result *res,
325 TALLOC_CTX *mem_ctx,
326 const char **attrs,
327 struct ldb_dn ***dn_list, int *dn_count)
329 int i;
330 struct ldb_dn **nc_list = NULL;
331 int nc_count = 0;
333 nc_list = talloc_array(mem_ctx, struct ldb_dn *, 0);
334 if (nc_list == NULL) {
335 return LDB_ERR_OPERATIONS_ERROR;
338 /* gather up a list of all NCs in this forest */
339 for (i=0; i<res->count; i++) {
340 struct ldb_message *msg = res->msgs[i];
341 int j;
342 for (j=0; attrs[j]; j++) {
343 struct ldb_message_element *el;
344 int k;
346 el = ldb_msg_find_element(msg, attrs[j]);
347 if (el == NULL) continue;
348 for (k=0; k<el->num_values; k++) {
349 struct ldb_dn *dn;
350 dn = ldb_dn_from_ldb_val(nc_list, ldb, &el->values[k]);
351 if (dn != NULL) {
352 int l;
353 for (l=0; l<nc_count; l++) {
354 if (ldb_dn_compare(nc_list[l], dn) == 0) break;
356 if (l < nc_count) continue;
357 nc_list = talloc_realloc(mem_ctx, nc_list, struct ldb_dn *, nc_count+1);
358 if (nc_list == NULL) {
359 return LDB_ERR_OPERATIONS_ERROR;
361 nc_list[nc_count] = dn;
362 nc_count++;
368 (*dn_list) = nc_list;
369 (*dn_count) = nc_count;
370 return LDB_SUCCESS;
375 look for any additional global catalog partitions that we should be
376 replicating (by looking for msDS-HasDomainNCs), and add them to our
377 hasPartialReplicaNCs NTDS attribute
379 static int kccsrv_gc_update(struct kccsrv_service *s, struct ldb_result *res)
381 int i;
382 struct ldb_dn **nc_list = NULL;
383 int nc_count = 0;
384 struct ldb_dn **our_nc_list = NULL;
385 int our_nc_count = 0;
386 const char *attrs1[] = { "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", NULL };
387 const char *attrs2[] = { "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", "hasPartialReplicaNCs", NULL };
388 int ret;
389 TALLOC_CTX *tmp_ctx = talloc_new(res);
390 struct ldb_result *res2;
391 struct ldb_message *msg;
393 /* get a complete list of NCs for the forest */
394 ret = kccsrv_dn_list(s->samdb, res, tmp_ctx, attrs1, &nc_list, &nc_count);
395 if (ret != LDB_SUCCESS) {
396 DEBUG(1,("Failed to get NC list for GC update - %s\n", ldb_errstring(s->samdb)));
397 talloc_free(tmp_ctx);
398 return ret;
401 /* get a list of what NCs we are already replicating */
402 ret = dsdb_search_dn(s->samdb, tmp_ctx, &res2, samdb_ntds_settings_dn(s->samdb, tmp_ctx), attrs2, 0);
403 if (ret != LDB_SUCCESS) {
404 DEBUG(1,("Failed to get our NC list attributes for GC update - %s\n", ldb_errstring(s->samdb)));
405 talloc_free(tmp_ctx);
406 return ret;
409 ret = kccsrv_dn_list(s->samdb, res2, tmp_ctx, attrs2, &our_nc_list, &our_nc_count);
410 if (ret != LDB_SUCCESS) {
411 DEBUG(1,("Failed to get our NC list for GC update - %s\n", ldb_errstring(s->samdb)));
412 talloc_free(tmp_ctx);
413 return ret;
416 msg = ldb_msg_new(tmp_ctx);
417 if (msg == NULL) {
418 talloc_free(tmp_ctx);
419 return LDB_ERR_OPERATIONS_ERROR;
421 msg->dn = res2->msgs[0]->dn;
423 /* see if we are missing any */
424 for (i=0; i<nc_count; i++) {
425 int j;
426 for (j=0; j<our_nc_count; j++) {
427 if (ldb_dn_compare(nc_list[i], our_nc_list[j]) == 0) break;
429 if (j == our_nc_count) {
430 /* its a new one */
431 ret = ldb_msg_add_string(msg, "hasPartialReplicaNCs",
432 ldb_dn_get_extended_linearized(msg, nc_list[i], 1));
433 if (ret != LDB_SUCCESS) {
434 talloc_free(tmp_ctx);
435 return ret;
441 if (msg->num_elements == 0) {
442 /* none to add */
443 talloc_free(tmp_ctx);
444 return LDB_SUCCESS;
447 if (s->am_rodc) {
448 DEBUG(5, ("%d partial replica should be added but we are RODC so we skip\n", msg->num_elements));
449 talloc_free(tmp_ctx);
450 return LDB_SUCCESS;
453 msg->elements[0].flags = LDB_FLAG_MOD_ADD;
455 ret = dsdb_modify(s->samdb, msg, 0);
456 if (ret != LDB_SUCCESS) {
457 DEBUG(0,("Failed to add hasPartialReplicaNCs - %s\n",
458 ldb_errstring(s->samdb)));
461 talloc_free(tmp_ctx);
462 return ret;
467 this is the core of our initial simple KCC
468 We just add a repsFrom entry for all DCs we find that have nTDSDSA
469 objects, except for ourselves
471 NTSTATUS kccsrv_simple_update(struct kccsrv_service *s, TALLOC_CTX *mem_ctx)
473 struct ldb_result *res;
474 unsigned int i;
475 int ret;
476 const char *attrs[] = { "objectGUID", "invocationID", "msDS-hasMasterNCs", "hasMasterNCs", "msDS-HasDomainNCs", NULL };
477 struct repsFromToBlob *reps = NULL;
478 uint32_t count = 0;
479 struct kcc_connection_list *ntds_conn, *dsa_conn;
481 ret = dsdb_search(s->samdb, mem_ctx, &res, s->config_dn, LDB_SCOPE_SUBTREE,
482 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "objectClass=nTDSDSA");
483 if (ret != LDB_SUCCESS) {
484 DEBUG(0,(__location__ ": Failed nTDSDSA search - %s\n", ldb_errstring(s->samdb)));
485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
488 if (samdb_is_gc(s->samdb)) {
489 kccsrv_gc_update(s, res);
492 /* get the current list of connections */
493 ntds_conn = kccsrv_find_connections(s, mem_ctx);
495 dsa_conn = talloc_zero(mem_ctx, struct kcc_connection_list);
497 for (i=0; i<res->count; i++) {
498 struct repsFromTo1 *r1;
499 struct GUID ntds_guid, invocation_id;
501 ntds_guid = samdb_result_guid(res->msgs[i], "objectGUID");
502 if (GUID_compare(&ntds_guid, &s->ntds_guid) == 0) {
503 /* don't replicate with ourselves */
504 continue;
507 invocation_id = samdb_result_guid(res->msgs[i], "invocationID");
509 reps = talloc_realloc(mem_ctx, reps, struct repsFromToBlob, count+1);
510 NT_STATUS_HAVE_NO_MEMORY(reps);
512 ZERO_STRUCT(reps[count]);
513 reps[count].version = 1;
514 r1 = &reps[count].ctr.ctr1;
516 r1->other_info = talloc_zero(reps, struct repsFromTo1OtherInfo);
517 r1->other_info->dns_name = samdb_ntds_msdcs_dns_name(s->samdb, reps, &ntds_guid);
518 r1->source_dsa_obj_guid = ntds_guid;
519 r1->source_dsa_invocation_id = invocation_id;
520 r1->replica_flags = kccsrv_replica_flags(s);
521 memset(r1->schedule, 0x11, sizeof(r1->schedule));
523 dsa_conn->servers = talloc_realloc(dsa_conn, dsa_conn->servers,
524 struct kcc_connection,
525 dsa_conn->count + 1);
526 NT_STATUS_HAVE_NO_MEMORY(dsa_conn->servers);
527 dsa_conn->servers[dsa_conn->count].dsa_guid = r1->source_dsa_obj_guid;
528 dsa_conn->count++;
530 count++;
533 kccsrv_apply_connections(s, ntds_conn, dsa_conn);
535 return kccsrv_add_repsFrom(s, mem_ctx, reps, count, res);
539 static void kccsrv_periodic_run(struct kccsrv_service *service);
541 static void kccsrv_periodic_handler_te(struct tevent_context *ev, struct tevent_timer *te,
542 struct timeval t, void *ptr)
544 struct kccsrv_service *service = talloc_get_type(ptr, struct kccsrv_service);
545 WERROR status;
547 service->periodic.te = NULL;
549 kccsrv_periodic_run(service);
551 status = kccsrv_periodic_schedule(service, service->periodic.interval);
552 if (!W_ERROR_IS_OK(status)) {
553 task_server_terminate(service->task, win_errstr(status), true);
554 return;
558 WERROR kccsrv_periodic_schedule(struct kccsrv_service *service, uint32_t next_interval)
560 TALLOC_CTX *tmp_mem;
561 struct tevent_timer *new_te;
562 struct timeval next_time;
564 /* prevent looping */
565 if (next_interval == 0) next_interval = 1;
567 next_time = timeval_current_ofs(next_interval, 50);
569 if (service->periodic.te) {
571 * if the timestamp of the new event is higher,
572 * as current next we don't need to reschedule
574 if (timeval_compare(&next_time, &service->periodic.next_event) > 0) {
575 return WERR_OK;
579 /* reset the next scheduled timestamp */
580 service->periodic.next_event = next_time;
582 new_te = tevent_add_timer(service->task->event_ctx, service,
583 service->periodic.next_event,
584 kccsrv_periodic_handler_te, service);
585 W_ERROR_HAVE_NO_MEMORY(new_te);
587 tmp_mem = talloc_new(service);
588 DEBUG(4,("kccsrv_periodic_schedule(%u) %sscheduled for: %s\n",
589 next_interval,
590 (service->periodic.te?"re":""),
591 nt_time_string(tmp_mem, timeval_to_nttime(&next_time))));
592 talloc_free(tmp_mem);
594 talloc_free(service->periodic.te);
595 service->periodic.te = new_te;
597 return WERR_OK;
600 static void kccsrv_periodic_run(struct kccsrv_service *service)
602 TALLOC_CTX *mem_ctx;
603 NTSTATUS status;
605 DEBUG(4,("kccsrv_periodic_run(): update\n"));
607 mem_ctx = talloc_new(service);
609 if (service->samba_kcc_code)
610 status = kccsrv_samba_kcc(service, mem_ctx);
611 else {
612 status = kccsrv_simple_update(service, mem_ctx);
613 if (!NT_STATUS_IS_OK(status))
614 DEBUG(0,("kccsrv_simple_update failed - %s\n",
615 nt_errstr(status)));
618 status = kccsrv_check_deleted(service, mem_ctx);
619 if (!NT_STATUS_IS_OK(status)) {
620 DEBUG(0,("kccsrv_check_deleted failed - %s\n", nt_errstr(status)));
622 talloc_free(mem_ctx);
625 /* Called when samba_kcc script has finished
627 static void samba_kcc_done(struct tevent_req *subreq)
629 struct kccsrv_service *service =
630 tevent_req_callback_data(subreq, struct kccsrv_service);
631 int rc;
632 int sys_errno;
634 service->periodic.subreq = NULL;
636 rc = samba_runcmd_recv(subreq, &sys_errno);
637 TALLOC_FREE(subreq);
639 if (rc != 0)
640 service->periodic.status =
641 map_nt_error_from_unix_common(sys_errno);
642 else
643 service->periodic.status = NT_STATUS_OK;
645 if (!NT_STATUS_IS_OK(service->periodic.status))
646 DEBUG(0,(__location__ ": Failed samba_kcc - %s\n",
647 nt_errstr(service->periodic.status)));
648 else
649 DEBUG(3,("Completed samba_kcc OK\n"));
652 /* Invocation of the samba_kcc python script for replication
653 * topology generation.
655 NTSTATUS kccsrv_samba_kcc(struct kccsrv_service *service,
656 TALLOC_CTX *ctxp)
658 NTSTATUS status = NT_STATUS_OK;
659 const char * const *samba_kcc_command =
660 lpcfg_samba_kcc_command(service->task->lp_ctx);
662 /* kill any existing child */
663 TALLOC_FREE(service->periodic.subreq);
665 DEBUG(0,("Calling samba_kcc script\n"));
666 service->periodic.subreq = samba_runcmd_send(service,
667 service->task->event_ctx,
668 timeval_current_ofs(40, 0),
669 2, 0, samba_kcc_command, NULL);
671 if (service->periodic.subreq == NULL) {
672 status = NT_STATUS_NO_MEMORY;
673 goto xerror;
675 tevent_req_set_callback(service->periodic.subreq,
676 samba_kcc_done, service);
678 xerror:
679 if (!NT_STATUS_IS_OK(status))
680 DEBUG(0,(__location__ ": failed - %s\n", nt_errstr(status)));
681 return status;