Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / bind / dist / contrib / dlz / drivers / dlz_filesystem_driver.c
blob47500c43f3f15f735701ecd6195df3ac3897b0db
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the
8 * above copyright notice and this permission notice appear in all
9 * copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
12 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
13 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14 * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
17 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
18 * USE OR PERFORMANCE OF THIS SOFTWARE.
20 * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21 * conceived and contributed by Rob Butler.
23 * Permission to use, copy, modify, and distribute this software for any
24 * purpose with or without fee is hereby granted, provided that the
25 * above copyright notice and this permission notice appear in all
26 * copies.
28 * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
29 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
31 * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
34 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
35 * USE OR PERFORMANCE OF THIS SOFTWARE.
39 * Copyright (C) 1999-2001 Internet Software Consortium.
41 * Permission to use, copy, modify, and distribute this software for any
42 * purpose with or without fee is hereby granted, provided that the above
43 * copyright notice and this permission notice appear in all copies.
45 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
46 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
48 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
49 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
50 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
51 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
52 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
55 #ifdef DLZ_FILESYSTEM
57 #include <config.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <stdlib.h>
62 #include <sys/stat.h>
64 #include <dns/log.h>
65 #include <dns/sdlz.h>
66 #include <dns/result.h>
68 #include <isc/dir.h>
69 #include <isc/mem.h>
70 #include <isc/platform.h>
71 #include <isc/print.h>
72 #include <isc/result.h>
73 #include <isc/util.h>
75 #include <named/globals.h>
77 #include <dlz/dlz_filesystem_driver.h>
79 static dns_sdlzimplementation_t *dlz_fs = NULL;
81 typedef struct config_data {
82 char *basedir;
83 int basedirsize;
84 char *datadir;
85 int datadirsize;
86 char *xfrdir;
87 int xfrdirsize;
88 int splitcnt;
89 char separator;
90 char pathsep;
91 isc_mem_t *mctx;
92 } config_data_t;
94 typedef struct dir_entry dir_entry_t;
96 struct dir_entry {
97 char dirpath[ISC_DIR_PATHMAX];
98 ISC_LINK(dir_entry_t) link;
101 typedef ISC_LIST(dir_entry_t) dlist_t;
103 /* forward reference */
105 static void
106 fs_destroy(void *driverarg, void *dbdata);
109 * Private methods
112 static isc_boolean_t
113 is_safe(const char *input)
115 unsigned int i;
116 unsigned int len = strlen(input);
118 /* check that only allowed characters are in the domain name */
119 for (i=0; i < len; i++) {
120 /* '.' is allowed, but has special requirements */
121 if (input[i] == '.') {
122 /* '.' is not allowed as first char */
123 if (i == 0)
124 return ISC_FALSE;
125 /* '..', two dots together is not allowed. */
126 else if (input[i-1] == '.')
127 return ISC_FALSE;
128 /* '.' is not allowed as last char */
129 if (i == len)
130 return ISC_FALSE;
131 /* only 1 dot in ok location, continue at next char */
132 continue;
134 /* '-' is allowed, continue at next char */
135 if (input[i] == '-')
136 continue;
137 /* 0-9 is allowed, continue at next char */
138 if (input[i] >= '0' && input[i] <= '9')
139 continue;
140 /* A-Z uppercase is allowed, continue at next char */
141 if (input[i] >= 'A' && input[i] <= 'Z')
142 continue;
143 /* a-z lowercase is allowed, continue at next char */
144 if (input[i] >= 'a' && input[i] <= 'z')
145 continue;
148 * colon needs to be allowed for IPV6 client
149 * addresses. Not dangerous in domain names, as not a
150 * special char.
152 if (input[i] == ':')
153 continue;
156 * '@' needs to be allowed for in zone data. Not
157 * dangerous in domain names, as not a special char.
159 if (input[i] == '@')
160 continue;
163 * if we reach this point we have encountered a
164 * disallowed char!
166 return ISC_FALSE;
168 /* everything ok. */
169 return ISC_TRUE;
172 static isc_result_t
173 create_path_helper(char *out, const char *in, config_data_t *cd)
176 char *tmpString;
177 char *tmpPtr;
178 int i;
180 tmpString = isc_mem_strdup(ns_g_mctx, in);
181 if (tmpString == NULL)
182 return (ISC_R_NOMEMORY);
185 * don't forget is_safe guarantees '.' will NOT be the
186 * first/last char
188 while ((tmpPtr = strrchr(tmpString, '.')) != NULL) {
189 i = 0;
190 while (tmpPtr[i+1] != '\0') {
191 if (cd->splitcnt < 1)
192 strcat(out, (char *) &tmpPtr[i+1]);
193 else
194 strncat(out, (char *) &tmpPtr[i+1],
195 cd->splitcnt);
196 strncat(out, (char *) &cd->pathsep, 1);
197 if (cd->splitcnt == 0)
198 break;
199 if (strlen((char *) &tmpPtr[i+1]) <=
200 (unsigned int) cd->splitcnt)
201 break;
202 i += cd->splitcnt;
204 tmpPtr[0] = '\0';
207 /* handle the "first" label properly */
208 i=0;
209 tmpPtr = tmpString;
210 while (tmpPtr[i] != '\0') {
211 if (cd->splitcnt < 1)
212 strcat(out, (char *) &tmpPtr[i]);
213 else
214 strncat(out, (char *) &tmpPtr[i], cd->splitcnt);
215 strncat(out, (char *) &cd->pathsep, 1);
216 if (cd->splitcnt == 0)
217 break;
218 if (strlen((char *) &tmpPtr[i]) <=
219 (unsigned int) cd->splitcnt)
220 break;
221 i += cd->splitcnt;
224 isc_mem_free(ns_g_mctx, tmpString);
225 return (ISC_R_SUCCESS);
229 * Checks to make sure zone and host are safe. If safe, then
230 * hashes zone and host strings to build a path. If zone / host
231 * are not safe an error is returned.
234 static isc_result_t
235 create_path(const char *zone, const char *host, const char *client,
236 config_data_t *cd, char **path)
239 char *tmpPath;
240 int pathsize;
241 int len;
242 isc_result_t result;
244 /* we require a zone & cd parameter */
245 REQUIRE(zone != NULL);
246 REQUIRE(cd != NULL);
247 /* require path to be a pointer to NULL */
248 REQUIRE(path != NULL && *path == NULL);
250 * client and host may both be NULL, but they can't both be
251 * NON-NULL
253 REQUIRE( (host == NULL && client == NULL) ||
254 (host != NULL && client == NULL) ||
255 (host == NULL && client != NULL) );
257 /* if the requested zone is "unsafe", return error */
258 if (is_safe(zone) != ISC_TRUE)
259 return (ISC_R_FAILURE);
261 /* if host was passed, verify that it is safe */
262 if ((host != NULL) && (is_safe(host) != ISC_TRUE) )
263 return (ISC_R_FAILURE);
265 /* if host was passed, verify that it is safe */
266 if ((client != NULL) && (is_safe(client) != ISC_TRUE) )
267 return (ISC_R_FAILURE);
269 /* Determine how much memory the split up string will require */
270 if (host != NULL)
271 len = strlen(zone) + strlen(host);
272 else if (client != NULL)
273 len = strlen(zone) + strlen(client);
274 else
275 len = strlen(zone);
278 * even though datadir and xfrdir will never be in the same
279 * string we only waste a few bytes by allocating for both,
280 * and then we are safe from buffer overruns.
282 pathsize = len + cd->basedirsize +
283 cd->datadirsize + cd->xfrdirsize + 4;
285 /* if we are splitting names, we will need extra space. */
286 if (cd->splitcnt > 0)
287 pathsize += len/cd->splitcnt;
289 tmpPath = isc_mem_allocate(ns_g_mctx , pathsize * sizeof(char));
290 if (tmpPath == NULL) {
291 /* write error message */
292 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
293 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
294 "Filesystem driver unable to "
295 "allocate memory in create_path().");
296 result = ISC_R_NOMEMORY;
297 goto cleanup_mem;
301 * build path string.
302 * start out with base directory.
304 strcpy(tmpPath, cd->basedir);
306 /* add zone name - parsed properly */
307 if ((result = create_path_helper(tmpPath, zone, cd)) != ISC_R_SUCCESS)
308 goto cleanup_mem;
311 * When neither client or host is passed we are building a
312 * path to see if a zone is supported. We require that a zone
313 * path have the "data dir" directory contained within it so
314 * that we know this zone is really supported. Otherwise,
315 * this zone may not really be supported because we are
316 * supporting a delagated sub zone.
318 * Example:
320 * We are supporting long.domain.com and using a splitcnt of
321 * 0. the base dir is "/base-dir/" and the data dir is
322 * "/.datadir" We want to see if we are authoritative for
323 * domain.com. Path /base-dir/com/domain/.datadir since
324 * /base-dir/com/domain/.datadir does not exist, we are not
325 * authoritative for the domain "domain.com". However we are
326 * authoritative for the domain "long.domain.com" because the
327 * path /base-dir/com/domain/long/.datadir does exist!
330 /* if client is passed append xfr dir, otherwise append data dir */
331 if (client != NULL) {
332 strcat(tmpPath, cd->xfrdir);
333 strncat(tmpPath, (char *) &cd->pathsep, 1);
334 strcat(tmpPath, client);
335 } else {
336 strcat(tmpPath, cd->datadir);
339 /* if host not null, add it. */
340 if (host != NULL) {
341 strncat(tmpPath, (char *) &cd->pathsep, 1);
342 if ((result = create_path_helper(tmpPath, host,
343 cd)) != ISC_R_SUCCESS)
344 goto cleanup_mem;
347 /* return the path we built. */
348 *path = tmpPath;
350 /* return success */
351 result = ISC_R_SUCCESS;
353 cleanup_mem:
354 /* cleanup memory */
356 /* free tmpPath memory */
357 if (tmpPath != NULL && result != ISC_R_SUCCESS)
358 isc_mem_free(ns_g_mctx, tmpPath);
360 /* free tmpPath memory */
361 return result;
364 static isc_result_t
365 process_dir(isc_dir_t dir, void *passback, config_data_t *cd,
366 dlist_t *dir_list, unsigned int basedirlen)
369 char tmp[ISC_DIR_PATHMAX + ISC_DIR_NAMEMAX];
370 int astPos;
371 struct stat sb;
372 isc_result_t result = ISC_R_FAILURE;
373 char *endp;
374 char *type;
375 char *ttlStr;
376 char *data;
377 char host[ISC_DIR_NAMEMAX];
378 char *tmpString;
379 char *tmpPtr;
380 int ttl;
381 int i;
382 int len;
383 dir_entry_t *direntry;
384 isc_boolean_t foundHost;
386 tmp[0] = '\0'; /* set 1st byte to '\0' so strcpy works right. */
387 host[0] = '\0';
388 foundHost = ISC_FALSE;
390 /* copy base directory name to tmp. */
391 strcpy(tmp, dir.dirname);
393 /* dir.dirname will always have '*' as the last char. */
394 astPos = strlen(dir.dirname) - 1;
396 /* if dir_list != NULL, were are performing a zone xfr */
397 if (dir_list != NULL) {
398 /* if splitcnt == 0, determine host from path. */
399 if (cd->splitcnt == 0) {
400 if (strlen(tmp) - 3 > basedirlen) {
401 tmp[astPos-1] = '\0';
402 tmpString = (char *) &tmp[basedirlen+1];
403 /* handle filesystem's special wildcard "-" */
404 if (strcmp(tmpString, "-") == 0) {
405 strcpy(host, "*");
406 } else {
408 * not special wildcard -- normal name
410 while ((tmpPtr = strrchr(tmpString,
411 cd->pathsep))
412 != NULL) {
413 strcat(host, tmpPtr + 1);
414 strcat(host, ".");
415 tmpPtr[0] = '\0';
417 strcat(host, tmpString);
420 foundHost = ISC_TRUE;
421 /* set tmp again for use later */
422 strcpy(tmp, dir.dirname);
424 } else {
426 * if splitcnt != 0 determine host from
427 * ".host" directory entry
429 while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
430 if (strncasecmp(".host",
431 dir.entry.name, 5) == 0) {
433 * handle filesystem's special
434 * wildcard "-"
436 if (strcmp((char *) &dir.entry.name[6],
437 "-") == 0)
438 strcpy(host, "*");
439 else
440 strcpy(host,
441 (char *)
442 &dir.entry.name[6]);
443 foundHost = ISC_TRUE;
444 break;
447 /* reset dir list for use later */
448 isc_dir_reset(&dir);
449 } /* end of else */
452 while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
454 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
455 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
456 "Filesystem driver Dir name:"
457 " '%s' Dir entry: '%s'\n",
458 dir.dirname, dir.entry.name);
460 /* skip any entries starting with "." */
461 if (dir.entry.name[0] == '.')
462 continue;
465 * get rid of '*', set to NULL. Effectively trims
466 * string from previous loop to base directory only
467 * while still leaving memory for concat to be
468 * performed next.
471 tmp[astPos] = '\0';
473 /* add name to base directory name. */
474 strcat(tmp, dir.entry.name);
476 /* make sure we can stat entry */
477 if (stat(tmp, &sb) == 0 ) {
478 /* if entry is a directory */
479 if ((sb.st_mode & S_IFDIR) != 0) {
481 * if dir list is NOT NULL, add dir to
482 * dir list
484 if (dir_list != NULL) {
485 direntry =
486 isc_mem_get(ns_g_mctx,
487 sizeof(dir_entry_t));
488 if (direntry == NULL)
489 return (ISC_R_NOMEMORY);
490 strcpy(direntry->dirpath, tmp);
491 ISC_LINK_INIT(direntry, link);
492 ISC_LIST_APPEND(*dir_list, direntry,
493 link);
494 result = ISC_R_SUCCESS;
496 continue;
499 * if entry is a file be sure we do
500 * not add entry to DNS results if we
501 * are performing a zone xfr and we
502 * could not find a host entry.
505 } else if (dir_list != NULL &&
506 foundHost == ISC_FALSE) {
507 continue;
509 } else /* if we cannot stat entry, skip it. */
510 continue;
512 type = dir.entry.name;
513 ttlStr = strchr(type, cd->separator);
514 if (ttlStr == NULL) {
515 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
516 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
517 "Filesystem driver: "
518 "%s could not be parsed properly",
519 tmp);
520 return ISC_R_FAILURE;
523 /* replace separator char with NULL to split string */
524 ttlStr[0] = '\0';
525 /* start string after NULL of previous string */
526 ttlStr = (char *) &ttlStr[1];
528 data = strchr(ttlStr, cd->separator);
529 if (data == NULL) {
530 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
531 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
532 "Filesystem driver: "
533 "%s could not be parsed properly",
534 tmp);
535 return ISC_R_FAILURE;
538 /* replace separator char with NULL to split string */
539 data[0] = '\0';
541 /* start string after NULL of previous string */
542 data = (char *) &data[1];
544 /* replace all cd->separator chars with a space. */
545 len = strlen(data);
547 for (i=0; i < len; i++) {
548 if (data[i] == cd->separator)
549 data[i] = ' ';
552 /* convert text to int, make sure it worked right */
553 ttl = strtol(ttlStr, &endp, 10);
554 if (*endp != '\0' || ttl < 0) {
555 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
556 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
557 "Filesystem driver "
558 "ttl must be a postive number");
561 /* pass data back to Bind */
562 if (dir_list == NULL)
563 result = dns_sdlz_putrr((dns_sdlzlookup_t *) passback,
564 type, ttl, data);
565 else
566 result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *)
567 passback,
568 (char *) host,
569 type, ttl, data);
571 /* if error, return error right away */
572 if (result != ISC_R_SUCCESS)
573 return result;
574 } /* end of while loop */
576 return result;
580 * SDLZ interface methods
583 static isc_result_t
584 fs_allowzonexfr(void *driverarg, void *dbdata, const char *name,
585 const char *client)
588 isc_result_t result;
589 char *path;
590 struct stat sb;
591 config_data_t *cd;
592 path = NULL;
594 UNUSED(driverarg);
596 cd = (config_data_t *) dbdata;
598 if (create_path(name, NULL, client, cd, &path) != ISC_R_SUCCESS) {
599 return (ISC_R_NOTFOUND);
602 if (stat(path, &sb) != 0) {
603 result = ISC_R_NOTFOUND;
604 goto complete_AXFR;
607 if ((sb.st_mode & S_IFREG) != 0) {
608 result = ISC_R_SUCCESS;
609 goto complete_AXFR;
612 result = ISC_R_NOTFOUND;
614 complete_AXFR:
615 isc_mem_free(ns_g_mctx, path);
616 return result;
619 static isc_result_t
620 fs_allnodes(const char *zone, void *driverarg, void *dbdata,
621 dns_sdlzallnodes_t *allnodes)
624 isc_result_t result;
625 dlist_t *dir_list;
626 config_data_t *cd;
627 char *basepath;
628 unsigned int basepathlen;
629 struct stat sb;
630 isc_dir_t dir;
631 dir_entry_t *dir_entry;
632 dir_entry_t *next_de;
634 basepath = NULL;
635 dir_list = NULL;
637 UNUSED(driverarg);
638 UNUSED(allnodes);
640 cd = (config_data_t *) dbdata;
642 /* allocate memory for list */
643 dir_list = isc_mem_get(ns_g_mctx, sizeof(dlist_t));
644 if (dir_list == NULL) {
645 result = ISC_R_NOTFOUND;
646 goto complete_allnds;
649 /* initialize list */
650 ISC_LIST_INIT(*dir_list);
652 if (create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS) {
653 return (ISC_R_NOTFOUND);
656 /* remove path separator at end of path so stat works properly */
657 basepathlen = strlen(basepath);
659 if (stat(basepath, &sb) != 0) {
660 result = ISC_R_NOTFOUND;
661 goto complete_allnds;
664 if ((sb.st_mode & S_IFDIR) == 0) {
665 result = ISC_R_NOTFOUND;
666 goto complete_allnds;
669 /* initialize and open directory */
670 isc_dir_init(&dir);
671 result = isc_dir_open(&dir, basepath);
673 /* if directory open failed, return error. */
674 if (result != ISC_R_SUCCESS) {
675 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
676 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
677 "Unable to open %s directory to read entries.",
678 basepath);
679 result = ISC_R_FAILURE;
680 goto complete_allnds;
683 /* process the directory */
684 result = process_dir(dir, allnodes, cd, dir_list, basepathlen);
686 /* close the directory */
687 isc_dir_close(&dir);
689 if (result != ISC_R_SUCCESS)
690 goto complete_allnds;
692 /* get first dir entry from list. */
693 dir_entry = ISC_LIST_HEAD(*dir_list);
694 while (dir_entry != NULL) {
696 result = isc_dir_open(&dir, dir_entry->dirpath);
697 /* if directory open failed, return error. */
698 if (result != ISC_R_SUCCESS) {
699 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
700 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
701 "Unable to open %s "
702 "directory to read entries.",
703 basepath);
704 result = ISC_R_FAILURE;
705 goto complete_allnds;
708 /* process the directory */
709 result = process_dir(dir, allnodes, cd, dir_list, basepathlen);
711 /* close the directory */
712 isc_dir_close(&dir);
714 if (result != ISC_R_SUCCESS)
715 goto complete_allnds;
717 dir_entry = ISC_LIST_NEXT(dir_entry, link);
718 } /* end while */
720 complete_allnds:
721 if (dir_list != NULL) {
722 /* clean up entries from list. */
723 dir_entry = ISC_LIST_HEAD(*dir_list);
724 while (dir_entry != NULL) {
725 next_de = ISC_LIST_NEXT(dir_entry, link);
726 isc_mem_put(ns_g_mctx, dir_entry, sizeof(dir_entry_t));
727 dir_entry = next_de;
728 } /* end while */
729 isc_mem_put(ns_g_mctx, dir_list, sizeof(dlist_t));
732 if (basepath != NULL)
733 isc_mem_free(ns_g_mctx, basepath);
735 return result;
738 static isc_result_t
739 fs_findzone(void *driverarg, void *dbdata, const char *name)
742 isc_result_t result;
743 char *path;
744 struct stat sb;
745 path = NULL;
747 UNUSED(driverarg);
749 if (create_path(name, NULL, NULL, (config_data_t *) dbdata,
750 &path) != ISC_R_SUCCESS) {
751 return (ISC_R_NOTFOUND);
754 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
755 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
756 "Filesystem driver Findzone() Checking for path: '%s'\n",
757 path);
759 if (stat(path, &sb) != 0) {
760 result = ISC_R_NOTFOUND;
761 goto complete_FZ;
764 if ((sb.st_mode & S_IFDIR) != 0) {
765 result = ISC_R_SUCCESS;
766 goto complete_FZ;
769 result = ISC_R_NOTFOUND;
771 complete_FZ:
773 isc_mem_free(ns_g_mctx, path);
774 return result;
777 static isc_result_t
778 fs_lookup(const char *zone, const char *name, void *driverarg,
779 void *dbdata, dns_sdlzlookup_t *lookup)
781 isc_result_t result;
782 char *path;
783 struct stat sb;
784 isc_dir_t dir;
785 path = NULL;
787 UNUSED(driverarg);
788 UNUSED(lookup);
790 if (strcmp(name, "*") == 0)
792 * handle filesystem's special wildcard "-"
794 result = create_path(zone, "-", NULL,
795 (config_data_t *) dbdata, &path);
796 else
797 result = create_path(zone, name, NULL,
798 (config_data_t *) dbdata, &path);
800 if ( result != ISC_R_SUCCESS) {
801 return (ISC_R_NOTFOUND);
804 /* remove path separator at end of path so stat works properly */
805 path[strlen(path)-1] = '\0';
807 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
808 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
809 "Filesystem driver lookup() Checking for path: '%s'\n",
810 path);
813 if (stat(path, &sb) != 0) {
814 result = ISC_R_NOTFOUND;
815 goto complete_lkup;
818 if ((sb.st_mode & S_IFDIR) == 0) {
819 result = ISC_R_NOTFOUND;
820 goto complete_lkup;
823 /* initialize and open directory */
824 isc_dir_init(&dir);
825 result = isc_dir_open(&dir, path);
827 /* if directory open failed, return error. */
828 if (result != ISC_R_SUCCESS) {
829 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
830 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
831 "Unable to open %s directory to read entries.",
832 path);
833 result = ISC_R_FAILURE;
834 goto complete_lkup;
837 /* process any records in the directory */
838 result = process_dir(dir, lookup, (config_data_t *) dbdata, NULL, 0);
840 /* close the directory */
841 isc_dir_close(&dir);
843 complete_lkup:
845 isc_mem_free(ns_g_mctx, path);
846 return result;
849 static isc_result_t
850 fs_create(const char *dlzname, unsigned int argc, char *argv[],
851 void *driverarg, void **dbdata)
853 config_data_t *cd;
854 char *endp;
855 int len;
856 char pathsep;
858 UNUSED(driverarg);
859 UNUSED(dlzname);
861 /* we require 5 command line args. */
862 if (argc != 6) {
863 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
864 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
865 "Filesystem driver requires "
866 "6 command line args.");
867 return (ISC_R_FAILURE);
870 if (strlen(argv[5]) > 1) {
871 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
872 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
873 "Filesystem driver can only "
874 "accept a single character for separator.");
875 return (ISC_R_FAILURE);
878 /* verify base dir ends with '/' or '\' */
879 len = strlen(argv[1]);
880 if (argv[1][len-1] != '\\' && argv[1][len-1] != '/') {
881 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
882 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
883 "Base dir parameter for filesystem driver "
884 "should end with %s",
885 "either '/' or '\\' ");
886 return (ISC_R_FAILURE);
889 /* determine and save path separator for later */
890 if (argv[1][len-1] == '\\')
891 pathsep = '\\';
892 else
893 pathsep = '/';
895 /* allocate memory for our config data */
896 cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t));
897 if (cd == NULL)
898 goto no_mem;
900 /* zero the memory */
901 memset(cd, 0, sizeof(config_data_t));
903 cd->pathsep = pathsep;
905 /* get and store our base directory */
906 cd->basedir = isc_mem_strdup(ns_g_mctx, argv[1]);
907 if (cd->basedir == NULL)
908 goto no_mem;
909 cd->basedirsize = strlen(cd->basedir);
911 /* get and store our data sub-dir */
912 cd->datadir = isc_mem_strdup(ns_g_mctx, argv[2]);
913 if (cd->datadir == NULL)
914 goto no_mem;
915 cd->datadirsize = strlen(cd->datadir);
917 /* get and store our zone xfr sub-dir */
918 cd->xfrdir = isc_mem_strdup(ns_g_mctx, argv[3]);
919 if (cd->xfrdir == NULL)
920 goto no_mem;
921 cd->xfrdirsize = strlen(cd->xfrdir);
923 /* get and store our directory split count */
924 cd->splitcnt = strtol(argv[4], &endp, 10);
925 if (*endp != '\0' || cd->splitcnt < 0) {
926 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
927 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
928 "Directory split count must be zero (0) "
929 "or a postive number");
932 /* get and store our separator character */
933 cd->separator = *argv[5];
935 /* attach config data to memory context */
936 isc_mem_attach(ns_g_mctx, &cd->mctx);
938 /* pass back config data */
939 *dbdata = cd;
941 /* return success */
942 return(ISC_R_SUCCESS);
944 /* handle no memory error */
945 no_mem:
947 /* if we allocated a config data object clean it up */
948 if (cd != NULL)
949 fs_destroy(NULL, cd);
951 /* write error message */
952 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
953 DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
954 "Filesystem driver unable to "
955 "allocate memory for config data.");
957 /* return error */
958 return (ISC_R_NOMEMORY);
961 static void
962 fs_destroy(void *driverarg, void *dbdata)
964 isc_mem_t *mctx;
965 config_data_t *cd;
967 UNUSED(driverarg);
969 cd = (config_data_t *) dbdata;
972 * free memory for each section of config data that was
973 * allocated
975 if (cd->basedir != NULL)
976 isc_mem_free(ns_g_mctx, cd->basedir);
978 if (cd->datadir != NULL)
979 isc_mem_free(ns_g_mctx, cd->datadir);
981 if (cd->xfrdir != NULL)
982 isc_mem_free(ns_g_mctx, cd->xfrdir);
984 /* hold memory context to use later */
985 mctx = cd->mctx;
987 /* free config data memory */
988 isc_mem_put(mctx, cd, sizeof(config_data_t));
990 /* detach memory from context */
991 isc_mem_detach(&mctx);
994 static dns_sdlzmethods_t dlz_fs_methods = {
995 fs_create,
996 fs_destroy,
997 fs_findzone,
998 fs_lookup,
999 NULL,
1000 fs_allnodes,
1001 fs_allowzonexfr
1005 * Wrapper around dns_sdlzregister().
1007 isc_result_t
1008 dlz_fs_init(void)
1010 isc_result_t result;
1013 * Write debugging message to log
1015 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1016 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1017 "Registering DLZ filesystem driver.");
1019 result = dns_sdlzregister("filesystem", &dlz_fs_methods, NULL,
1020 DNS_SDLZFLAG_RELATIVEOWNER |
1021 DNS_SDLZFLAG_RELATIVERDATA,
1022 ns_g_mctx, &dlz_fs);
1023 if (result != ISC_R_SUCCESS) {
1024 UNEXPECTED_ERROR(__FILE__, __LINE__,
1025 "dns_sdlzregister() failed: %s",
1026 isc_result_totext(result));
1027 result = ISC_R_UNEXPECTED;
1030 return result;
1034 * Wrapper around dns_sdlzunregister().
1036 void
1037 dlz_fs_clear(void) {
1040 * Write debugging message to log
1042 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1043 DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1044 "Unregistering DLZ filesystem driver.");
1046 if (dlz_fs != NULL)
1047 dns_sdlzunregister(&dlz_fs);
1050 #endif