libsmbconf: add private_data section to smbconf_ctx.
[gd/samba-autobuild/.git] / source3 / lib / smbconf / smbconf_reg.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  libsmbconf - Samba configuration library, registry backend
4  *  Copyright (C) Michael Adam 2008
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 "includes.h"
21 #include "smbconf_private.h"
22
23 struct reg_private_data {
24         NT_USER_TOKEN *token;
25 };
26
27 /**********************************************************************
28  *
29  * helper functions
30  *
31  **********************************************************************/
32
33 static struct reg_private_data *rpd(struct smbconf_ctx *ctx)
34 {
35         return (struct reg_private_data *)(ctx->data);
36 }
37
38 /**
39  * Open a registry key specified by "path"
40  */
41 static WERROR smbconf_reg_open_path(TALLOC_CTX *mem_ctx,
42                                     struct smbconf_ctx *ctx,
43                                     const char *path,
44                                     uint32 desired_access,
45                                     struct registry_key **key)
46 {
47         WERROR werr = WERR_OK;
48
49         if (ctx == NULL) {
50                 DEBUG(1, ("Error: configuration is not open!\n"));
51                 werr = WERR_INVALID_PARAM;
52                 goto done;
53         }
54
55         if (rpd(ctx)->token == NULL) {
56                 DEBUG(1, ("Error: token missing from smbconf_ctx. "
57                           "was smbconf_init() called?\n"));
58                 werr = WERR_INVALID_PARAM;
59                 goto done;
60         }
61
62         if (path == NULL) {
63                 DEBUG(1, ("Error: NULL path string given\n"));
64                 werr = WERR_INVALID_PARAM;
65                 goto done;
66         }
67
68         werr = reg_open_path(mem_ctx, path, desired_access, rpd(ctx)->token,
69                              key);
70
71         if (!W_ERROR_IS_OK(werr)) {
72                 DEBUG(1, ("Error opening registry path '%s': %s\n",
73                           path, dos_errstr(werr)));
74         }
75
76 done:
77         return werr;
78 }
79
80 /**
81  * Open a subkey of the base key (i.e a service)
82  */
83 static WERROR smbconf_reg_open_service_key(TALLOC_CTX *mem_ctx,
84                                            struct smbconf_ctx *ctx,
85                                            const char *servicename,
86                                            uint32 desired_access,
87                                            struct registry_key **key)
88 {
89         WERROR werr = WERR_OK;
90         char *path = NULL;
91
92         if (servicename == NULL) {
93                 DEBUG(3, ("Error: NULL servicename given.\n"));
94                 werr = WERR_INVALID_PARAM;
95                 goto done;
96         }
97
98         path = talloc_asprintf(mem_ctx, "%s\\%s", ctx->path, servicename);
99         if (path == NULL) {
100                 werr = WERR_NOMEM;
101                 goto done;
102         }
103
104         werr = smbconf_reg_open_path(mem_ctx, ctx, path, desired_access, key);
105
106 done:
107         TALLOC_FREE(path);
108         return werr;
109 }
110
111 /**
112  * open the base key
113  */
114 static WERROR smbconf_reg_open_base_key(TALLOC_CTX *mem_ctx,
115                                         struct smbconf_ctx *ctx,
116                                         uint32 desired_access,
117                                         struct registry_key **key)
118 {
119         return smbconf_reg_open_path(mem_ctx, ctx, ctx->path, desired_access,
120                                      key);
121 }
122
123 /**
124  * check if a value exists in a given registry key
125  */
126 static bool smbconf_value_exists(struct registry_key *key, const char *param)
127 {
128         bool ret = false;
129         WERROR werr = WERR_OK;
130         TALLOC_CTX *ctx = talloc_stackframe();
131         struct registry_value *value = NULL;
132
133         werr = reg_queryvalue(ctx, key, param, &value);
134         if (W_ERROR_IS_OK(werr)) {
135                 ret = true;
136         }
137
138         TALLOC_FREE(ctx);
139         return ret;
140 }
141
142 /**
143  * create a subkey of the base key (i.e. a service...)
144  */
145 static WERROR smbconf_reg_create_service_key(TALLOC_CTX *mem_ctx,
146                                              struct smbconf_ctx *ctx,
147                                              const char * subkeyname,
148                                              struct registry_key **newkey)
149 {
150         WERROR werr = WERR_OK;
151         struct registry_key *create_parent = NULL;
152         TALLOC_CTX *create_ctx;
153         enum winreg_CreateAction action = REG_ACTION_NONE;
154
155         /* create a new talloc ctx for creation. it will hold
156          * the intermediate parent key (SMBCONF) for creation
157          * and will be destroyed when leaving this function... */
158         if (!(create_ctx = talloc_stackframe())) {
159                 werr = WERR_NOMEM;
160                 goto done;
161         }
162
163         werr = smbconf_reg_open_base_key(create_ctx, ctx, REG_KEY_WRITE,
164                                          &create_parent);
165         if (!W_ERROR_IS_OK(werr)) {
166                 goto done;
167         }
168
169         werr = reg_createkey(mem_ctx, create_parent, subkeyname,
170                              REG_KEY_WRITE, newkey, &action);
171         if (W_ERROR_IS_OK(werr) && (action != REG_CREATED_NEW_KEY)) {
172                 DEBUG(10, ("Key '%s' already exists.\n", subkeyname));
173                 werr = WERR_ALREADY_EXISTS;
174         }
175         if (!W_ERROR_IS_OK(werr)) {
176                 DEBUG(5, ("Error creating key %s: %s\n",
177                          subkeyname, dos_errstr(werr)));
178         }
179
180 done:
181         TALLOC_FREE(create_ctx);
182         return werr;
183 }
184
185 /**
186  * add a value to a key.
187  */
188 static WERROR smbconf_reg_set_value(struct registry_key *key,
189                                     const char *valname,
190                                     const char *valstr)
191 {
192         struct registry_value val;
193         WERROR werr = WERR_OK;
194         char *subkeyname;
195         const char *canon_valname;
196         const char *canon_valstr;
197
198         if (!lp_canonicalize_parameter_with_value(valname, valstr,
199                                                   &canon_valname,
200                                                   &canon_valstr))
201         {
202                 if (canon_valname == NULL) {
203                         DEBUG(5, ("invalid parameter '%s' given\n",
204                                   valname));
205                 } else {
206                         DEBUG(5, ("invalid value '%s' given for "
207                                   "parameter '%s'\n", valstr, valname));
208                 }
209                 werr = WERR_INVALID_PARAM;
210                 goto done;
211         }
212
213         ZERO_STRUCT(val);
214
215         val.type = REG_SZ;
216         val.v.sz.str = CONST_DISCARD(char *, canon_valstr);
217         val.v.sz.len = strlen(canon_valstr) + 1;
218
219         if (registry_smbconf_valname_forbidden(canon_valname)) {
220                 DEBUG(5, ("Parameter '%s' not allowed in registry.\n",
221                           canon_valname));
222                 werr = WERR_INVALID_PARAM;
223                 goto done;
224         }
225
226         subkeyname = strrchr_m(key->key->name, '\\');
227         if ((subkeyname == NULL) || (*(subkeyname +1) == '\0')) {
228                 DEBUG(5, ("Invalid registry key '%s' given as "
229                           "smbconf section.\n", key->key->name));
230                 werr = WERR_INVALID_PARAM;
231                 goto done;
232         }
233         subkeyname++;
234         if (!strequal(subkeyname, GLOBAL_NAME) &&
235             lp_parameter_is_global(valname))
236         {
237                 DEBUG(5, ("Global paramter '%s' not allowed in "
238                           "service definition ('%s').\n", canon_valname,
239                           subkeyname));
240                 werr = WERR_INVALID_PARAM;
241                 goto done;
242         }
243
244         werr = reg_setvalue(key, canon_valname, &val);
245         if (!W_ERROR_IS_OK(werr)) {
246                 DEBUG(5, ("Error adding value '%s' to "
247                           "key '%s': %s\n",
248                           canon_valname, key->key->name, dos_errstr(werr)));
249         }
250
251 done:
252         return werr;
253 }
254
255 /**
256  * format a registry_value into a string.
257  *
258  * This is intended to be used for smbconf registry values,
259  * which are ar stored as REG_SZ values, so the incomplete
260  * handling should be ok.
261  */
262 static char *smbconf_format_registry_value(TALLOC_CTX *mem_ctx,
263                                            struct registry_value *value)
264 {
265         char *result = NULL;
266
267         /* alternatively, create a new talloc context? */
268         if (mem_ctx == NULL) {
269                 return result;
270         }
271
272         switch (value->type) {
273         case REG_DWORD:
274                 result = talloc_asprintf(mem_ctx, "%d", value->v.dword);
275                 break;
276         case REG_SZ:
277         case REG_EXPAND_SZ:
278                 result = talloc_asprintf(mem_ctx, "%s", value->v.sz.str);
279                 break;
280         case REG_MULTI_SZ: {
281                 uint32 j;
282                 for (j = 0; j < value->v.multi_sz.num_strings; j++) {
283                         result = talloc_asprintf(mem_ctx, "%s \"%s\" ",
284                                                  result,
285                                                  value->v.multi_sz.strings[j]);
286                         if (result == NULL) {
287                                 break;
288                         }
289                 }
290                 break;
291         }
292         case REG_BINARY:
293                 result = talloc_asprintf(mem_ctx, "binary (%d bytes)",
294                                          (int)value->v.binary.length);
295                 break;
296         default:
297                 result = talloc_asprintf(mem_ctx, "<unprintable>");
298                 break;
299         }
300         return result;
301 }
302
303 /**
304  * Get the values of a key as a list of value names
305  * and a list of value strings (ordered)
306  */
307 static WERROR smbconf_reg_get_values(TALLOC_CTX *mem_ctx,
308                                      struct registry_key *key,
309                                      uint32_t *num_values,
310                                      char ***value_names,
311                                      char ***value_strings)
312 {
313         TALLOC_CTX *tmp_ctx = NULL;
314         WERROR werr = WERR_OK;
315         uint32_t count;
316         struct registry_value *valvalue = NULL;
317         char *valname = NULL;
318         char **tmp_valnames = NULL;
319         char **tmp_valstrings = NULL;
320
321         if ((num_values == NULL) || (value_names == NULL) ||
322             (value_strings == NULL))
323         {
324                 werr = WERR_INVALID_PARAM;
325                 goto done;
326         }
327
328         tmp_ctx = talloc_stackframe();
329         if (tmp_ctx == NULL) {
330                 werr = WERR_NOMEM;
331                 goto done;
332         }
333
334         for (count = 0;
335              W_ERROR_IS_OK(werr = reg_enumvalue(tmp_ctx, key, count, &valname,
336                                                 &valvalue));
337              count++)
338         {
339                 char *valstring;
340
341                 werr = smbconf_add_string_to_array(tmp_ctx,
342                                                    &tmp_valnames,
343                                                    count, valname);
344                 if (!W_ERROR_IS_OK(werr)) {
345                         goto done;
346                 }
347
348                 valstring = smbconf_format_registry_value(tmp_ctx, valvalue);
349                 werr = smbconf_add_string_to_array(tmp_ctx, &tmp_valstrings,
350                                                    count, valstring);
351                 if (!W_ERROR_IS_OK(werr)) {
352                         goto done;
353                 }
354         }
355         if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
356                 goto done;
357         }
358
359         werr = WERR_OK;
360
361         *num_values = count;
362         if (count > 0) {
363                 *value_names = talloc_move(mem_ctx, &tmp_valnames);
364                 *value_strings = talloc_move(mem_ctx, &tmp_valstrings);
365         } else {
366                 *value_names = NULL;
367                 *value_strings = NULL;
368         }
369
370 done:
371         TALLOC_FREE(tmp_ctx);
372         return werr;
373 }
374
375 /**********************************************************************
376  *
377  * smbconf operations: registry implementations
378  *
379  **********************************************************************/
380
381 /**
382  * initialize the registry smbconf backend
383  */
384 static WERROR smbconf_reg_init(struct smbconf_ctx *ctx, const char *path)
385 {
386         WERROR werr = WERR_OK;
387
388         if (path == NULL) {
389                 path = KEY_SMBCONF;
390         }
391         ctx->path = talloc_strdup(ctx, path);
392         if (ctx->path == NULL) {
393                 werr = WERR_NOMEM;
394                 goto done;
395         }
396
397         ctx->data = TALLOC_ZERO_P(ctx, struct reg_private_data);
398
399         werr = ntstatus_to_werror(registry_create_admin_token(ctx,
400                                                         &(rpd(ctx)->token)));
401         if (!W_ERROR_IS_OK(werr)) {
402                 DEBUG(1, ("Error creating admin token\n"));
403                 goto done;
404         }
405
406         if (!registry_init_smbconf()) {
407                 werr = WERR_REG_IO_FAILURE;
408                 goto done;
409         }
410
411
412 done:
413         return werr;
414 }
415
416 static int smbconf_reg_shutdown(struct smbconf_ctx *ctx)
417 {
418         return regdb_close();
419 }
420
421 static WERROR smbconf_reg_open(struct smbconf_ctx *ctx)
422 {
423         return regdb_open();
424 }
425
426 static int smbconf_reg_close(struct smbconf_ctx *ctx)
427 {
428         return regdb_close();
429 }
430
431 /**
432  * Get the change sequence number of the given service/parameter.
433  * service and parameter strings may be NULL.
434  */
435 static void smbconf_reg_get_csn(struct smbconf_ctx *ctx,
436                                 struct smbconf_csn *csn,
437                                 const char *service, const char *param)
438 {
439         if (csn == NULL) {
440                 return;
441         }
442         csn->csn = (uint64_t)regdb_get_seqnum();
443 }
444
445 /**
446  * Drop the whole configuration (restarting empty) - registry version
447  */
448 static WERROR smbconf_reg_drop(struct smbconf_ctx *ctx)
449 {
450         char *path, *p;
451         WERROR werr = WERR_OK;
452         struct registry_key *parent_key = NULL;
453         struct registry_key *new_key = NULL;
454         TALLOC_CTX* mem_ctx = talloc_stackframe();
455         enum winreg_CreateAction action;
456
457         path = talloc_strdup(mem_ctx, ctx->path);
458         if (path == NULL) {
459                 werr = WERR_NOMEM;
460                 goto done;
461         }
462         p = strrchr(path, '\\');
463         *p = '\0';
464         werr = smbconf_reg_open_path(mem_ctx, ctx, path, REG_KEY_WRITE,
465                                      &parent_key);
466
467         if (!W_ERROR_IS_OK(werr)) {
468                 goto done;
469         }
470
471         werr = reg_deletekey_recursive(mem_ctx, parent_key, p+1);
472
473         if (!W_ERROR_IS_OK(werr)) {
474                 goto done;
475         }
476
477         werr = reg_createkey(mem_ctx, parent_key, p+1, REG_KEY_WRITE,
478                              &new_key, &action);
479
480 done:
481         TALLOC_FREE(mem_ctx);
482         return werr;
483 }
484
485 /**
486  * get the list of share names defined in the configuration.
487  * registry version.
488  */
489 static WERROR smbconf_reg_get_share_names(struct smbconf_ctx *ctx,
490                                           TALLOC_CTX *mem_ctx,
491                                           uint32_t *num_shares,
492                                           char ***share_names)
493 {
494         uint32_t count;
495         uint32_t added_count = 0;
496         TALLOC_CTX *tmp_ctx = NULL;
497         WERROR werr = WERR_OK;
498         struct registry_key *key = NULL;
499         char *subkey_name = NULL;
500         char **tmp_share_names = NULL;
501
502         if ((num_shares == NULL) || (share_names == NULL)) {
503                 werr = WERR_INVALID_PARAM;
504                 goto done;
505         }
506
507         tmp_ctx = talloc_stackframe();
508         if (tmp_ctx == NULL) {
509                 werr = WERR_NOMEM;
510                 goto done;
511         }
512
513         /* make sure "global" is always listed first */
514         if (smbconf_share_exists(ctx, GLOBAL_NAME)) {
515                 werr = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names,
516                                                    0, GLOBAL_NAME);
517                 if (!W_ERROR_IS_OK(werr)) {
518                         goto done;
519                 }
520                 added_count++;
521         }
522
523         werr = smbconf_reg_open_base_key(tmp_ctx, ctx,
524                                          SEC_RIGHTS_ENUM_SUBKEYS, &key);
525         if (!W_ERROR_IS_OK(werr)) {
526                 goto done;
527         }
528
529         for (count = 0;
530              W_ERROR_IS_OK(werr = reg_enumkey(tmp_ctx, key, count,
531                                               &subkey_name, NULL));
532              count++)
533         {
534                 if (strequal(subkey_name, GLOBAL_NAME)) {
535                         continue;
536                 }
537
538                 werr = smbconf_add_string_to_array(tmp_ctx,
539                                                    &tmp_share_names,
540                                                    added_count,
541                                                    subkey_name);
542                 if (!W_ERROR_IS_OK(werr)) {
543                         goto done;
544                 }
545                 added_count++;
546         }
547         if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
548                 goto done;
549         }
550         werr = WERR_OK;
551
552         *num_shares = added_count;
553         if (added_count > 0) {
554                 *share_names = talloc_move(mem_ctx, &tmp_share_names);
555         } else {
556                 *share_names = NULL;
557         }
558
559 done:
560         TALLOC_FREE(tmp_ctx);
561         return werr;
562 }
563
564 /**
565  * check if a share/service of a given name exists - registry version
566  */
567 static bool smbconf_reg_share_exists(struct smbconf_ctx *ctx,
568                                      const char *servicename)
569 {
570         bool ret = false;
571         WERROR werr = WERR_OK;
572         TALLOC_CTX *mem_ctx = talloc_stackframe();
573         struct registry_key *key = NULL;
574
575         werr = smbconf_reg_open_service_key(mem_ctx, ctx, servicename,
576                                             REG_KEY_READ, &key);
577         if (W_ERROR_IS_OK(werr)) {
578                 ret = true;
579         }
580
581         TALLOC_FREE(mem_ctx);
582         return ret;
583 }
584
585 /**
586  * Add a service if it does not already exist - registry version
587  */
588 static WERROR smbconf_reg_create_share(struct smbconf_ctx *ctx,
589                                        const char *servicename)
590 {
591         WERROR werr;
592         TALLOC_CTX *mem_ctx = talloc_stackframe();
593         struct registry_key *key = NULL;
594
595         werr = smbconf_reg_create_service_key(mem_ctx, ctx, servicename, &key);
596
597         TALLOC_FREE(mem_ctx);
598         return werr;
599 }
600
601 /**
602  * get a definition of a share (service) from configuration.
603  */
604 static WERROR smbconf_reg_get_share(struct smbconf_ctx *ctx,
605                                     TALLOC_CTX *mem_ctx,
606                                     const char *servicename,
607                                     uint32_t *num_params,
608                                     char ***param_names, char ***param_values)
609 {
610         WERROR werr = WERR_OK;
611         struct registry_key *key = NULL;
612
613         werr = smbconf_reg_open_service_key(mem_ctx, ctx, servicename,
614                                             REG_KEY_READ, &key);
615         if (!W_ERROR_IS_OK(werr)) {
616                 goto done;
617         }
618
619         werr = smbconf_reg_get_values(mem_ctx, key, num_params,
620                                       param_names, param_values);
621
622 done:
623         TALLOC_FREE(key);
624         return werr;
625 }
626
627 /**
628  * delete a service from configuration
629  */
630 static WERROR smbconf_reg_delete_share(struct smbconf_ctx *ctx,
631                                        const char *servicename)
632 {
633         WERROR werr = WERR_OK;
634         struct registry_key *key = NULL;
635         TALLOC_CTX *mem_ctx = talloc_stackframe();
636
637         werr = smbconf_reg_open_base_key(mem_ctx, ctx, REG_KEY_WRITE, &key);
638         if (!W_ERROR_IS_OK(werr)) {
639                 goto done;
640         }
641
642         werr = reg_deletekey_recursive(key, key, servicename);
643
644 done:
645         TALLOC_FREE(mem_ctx);
646         return werr;
647 }
648
649 /**
650  * set a configuration parameter to the value provided.
651  */
652 static WERROR smbconf_reg_set_parameter(struct smbconf_ctx *ctx,
653                                         const char *service,
654                                         const char *param,
655                                         const char *valstr)
656 {
657         WERROR werr;
658         struct registry_key *key = NULL;
659         TALLOC_CTX *mem_ctx = talloc_stackframe();
660
661         werr = smbconf_reg_open_service_key(mem_ctx, ctx, service,
662                                             REG_KEY_WRITE, &key);
663         if (!W_ERROR_IS_OK(werr)) {
664                 goto done;
665         }
666
667         werr = smbconf_reg_set_value(key, param, valstr);
668
669 done:
670         TALLOC_FREE(mem_ctx);
671         return werr;
672 }
673
674 /**
675  * get the value of a configuration parameter as a string
676  */
677 static WERROR smbconf_reg_get_parameter(struct smbconf_ctx *ctx,
678                                         TALLOC_CTX *mem_ctx,
679                                         const char *service,
680                                         const char *param,
681                                         char **valstr)
682 {
683         WERROR werr = WERR_OK;
684         struct registry_key *key = NULL;
685         struct registry_value *value = NULL;
686
687         werr = smbconf_reg_open_service_key(mem_ctx, ctx, service,
688                                             REG_KEY_READ, &key);
689         if (!W_ERROR_IS_OK(werr)) {
690                 goto done;
691         }
692
693         if (!smbconf_value_exists(key, param)) {
694                 werr = WERR_INVALID_PARAM;
695                 goto done;
696         }
697
698         werr = reg_queryvalue(mem_ctx, key, param, &value);
699         if (!W_ERROR_IS_OK(werr)) {
700                 goto done;
701         }
702
703         *valstr = smbconf_format_registry_value(mem_ctx, value);
704
705         if (*valstr == NULL) {
706                 werr = WERR_NOMEM;
707         }
708
709 done:
710         TALLOC_FREE(key);
711         TALLOC_FREE(value);
712         return werr;
713 }
714
715 /**
716  * delete a parameter from configuration
717  */
718 static WERROR smbconf_reg_delete_parameter(struct smbconf_ctx *ctx,
719                                            const char *service,
720                                            const char *param)
721 {
722         struct registry_key *key = NULL;
723         WERROR werr = WERR_OK;
724         TALLOC_CTX *mem_ctx = talloc_stackframe();
725
726         werr = smbconf_reg_open_service_key(mem_ctx, ctx, service,
727                                             REG_KEY_ALL, &key);
728         if (!W_ERROR_IS_OK(werr)) {
729                 goto done;
730         }
731
732         if (!smbconf_value_exists(key, param)) {
733                 werr = WERR_INVALID_PARAM;
734                 goto done;
735         }
736
737         werr = reg_deletevalue(key, param);
738
739 done:
740         TALLOC_FREE(mem_ctx);
741         return werr;
742 }
743
744 struct smbconf_ops smbconf_ops_reg = {
745         .init                   = smbconf_reg_init,
746         .shutdown               = smbconf_reg_shutdown,
747         .open_conf              = smbconf_reg_open,
748         .close_conf             = smbconf_reg_close,
749         .get_csn                = smbconf_reg_get_csn,
750         .drop                   = smbconf_reg_drop,
751         .get_share_names        = smbconf_reg_get_share_names,
752         .share_exists           = smbconf_reg_share_exists,
753         .create_share           = smbconf_reg_create_share,
754         .get_share              = smbconf_reg_get_share,
755         .delete_share           = smbconf_reg_delete_share,
756         .set_parameter          = smbconf_reg_set_parameter,
757         .get_parameter          = smbconf_reg_get_parameter,
758         .delete_parameter       = smbconf_reg_delete_parameter
759 };
760
761
762 /**
763  * initialize the smbconf registry backend
764  * the only function that is exported from this module
765  */
766 WERROR smbconf_init_reg(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx,
767                         const char *path)
768 {
769         return smbconf_init(mem_ctx, conf_ctx, path, &smbconf_ops_reg);
770 }