Home | History | Annotate | Line # | Download | only in dns
masterdump.c revision 1.17
      1 /*	$NetBSD: masterdump.c,v 1.17 2025/05/21 14:48:02 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <inttypes.h>
     19 #include <stdbool.h>
     20 #include <stdlib.h>
     21 
     22 #include <isc/async.h>
     23 #include <isc/atomic.h>
     24 #include <isc/buffer.h>
     25 #include <isc/file.h>
     26 #include <isc/loop.h>
     27 #include <isc/magic.h>
     28 #include <isc/mem.h>
     29 #include <isc/refcount.h>
     30 #include <isc/result.h>
     31 #include <isc/stdio.h>
     32 #include <isc/string.h>
     33 #include <isc/time.h>
     34 #include <isc/types.h>
     35 #include <isc/util.h>
     36 #include <isc/work.h>
     37 
     38 #include <dns/db.h>
     39 #include <dns/dbiterator.h>
     40 #include <dns/fixedname.h>
     41 #include <dns/log.h>
     42 #include <dns/master.h>
     43 #include <dns/masterdump.h>
     44 #include <dns/ncache.h>
     45 #include <dns/rdata.h>
     46 #include <dns/rdataclass.h>
     47 #include <dns/rdataset.h>
     48 #include <dns/rdatasetiter.h>
     49 #include <dns/rdatatype.h>
     50 #include <dns/time.h>
     51 #include <dns/ttl.h>
     52 
     53 #define DNS_DCTX_MAGIC	  ISC_MAGIC('D', 'c', 't', 'x')
     54 #define DNS_DCTX_VALID(d) ISC_MAGIC_VALID(d, DNS_DCTX_MAGIC)
     55 
     56 #define RETERR(x)                        \
     57 	do {                             \
     58 		isc_result_t _r = (x);   \
     59 		if (_r != ISC_R_SUCCESS) \
     60 			return ((_r));   \
     61 	} while (0)
     62 
     63 #define CHECK(x)                          \
     64 	do {                              \
     65 		if ((x) != ISC_R_SUCCESS) \
     66 			goto cleanup;     \
     67 	} while (0)
     68 
     69 struct dns_master_style {
     70 	dns_masterstyle_flags_t flags; /* DNS_STYLEFLAG_* */
     71 	unsigned int ttl_column;
     72 	unsigned int class_column;
     73 	unsigned int type_column;
     74 	unsigned int rdata_column;
     75 	unsigned int line_length;
     76 	unsigned int tab_width;
     77 	unsigned int split_width;
     78 };
     79 
     80 /*%
     81  * The maximum length of the newline+indentation that is output
     82  * when inserting a line break in an RR.  This effectively puts an
     83  * upper limits on the value of "rdata_column", because if it is
     84  * very large, the tabs and spaces needed to reach it will not fit.
     85  */
     86 #define DNS_TOTEXT_LINEBREAK_MAXLEN 100
     87 
     88 /*% Does the rdataset 'r' contain a stale answer? */
     89 #define STALE(r) (((r)->attributes & DNS_RDATASETATTR_STALE) != 0)
     90 /*% Does the rdataset 'r' contain an expired answer? */
     91 #define ANCIENT(r) (((r)->attributes & DNS_RDATASETATTR_ANCIENT) != 0)
     92 
     93 /*%
     94  * Context structure for a masterfile dump in progress.
     95  */
     96 typedef struct dns_totext_ctx {
     97 	dns_master_style_t style;
     98 	bool class_printed;
     99 	char *linebreak;
    100 	char linebreak_buf[DNS_TOTEXT_LINEBREAK_MAXLEN];
    101 	dns_name_t *origin;
    102 	dns_name_t *neworigin;
    103 	dns_fixedname_t origin_fixname;
    104 	uint32_t current_ttl;
    105 	bool current_ttl_valid;
    106 	dns_ttl_t serve_stale_ttl;
    107 	dns_indent_t indent;
    108 } dns_totext_ctx_t;
    109 
    110 const dns_master_style_t dns_master_style_keyzone = {
    111 	DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
    112 		DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA |
    113 		DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL |
    114 		DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT |
    115 		DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_KEYDATA,
    116 	24,
    117 	24,
    118 	24,
    119 	32,
    120 	80,
    121 	8,
    122 	UINT_MAX
    123 };
    124 
    125 const dns_master_style_t dns_master_style_default = {
    126 	DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
    127 		DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_REL_DATA |
    128 		DNS_STYLEFLAG_OMIT_TTL | DNS_STYLEFLAG_TTL |
    129 		DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RRCOMMENT |
    130 		DNS_STYLEFLAG_MULTILINE,
    131 	24,
    132 	24,
    133 	24,
    134 	32,
    135 	80,
    136 	8,
    137 	UINT_MAX
    138 };
    139 
    140 const dns_master_style_t dns_master_style_full = {
    141 	DNS_STYLEFLAG_COMMENT | DNS_STYLEFLAG_RESIGN,
    142 	46,
    143 	46,
    144 	46,
    145 	64,
    146 	120,
    147 	8,
    148 	UINT_MAX
    149 };
    150 
    151 const dns_master_style_t dns_master_style_explicitttl = {
    152 	DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
    153 		DNS_STYLEFLAG_CLASS_PERNAME | DNS_STYLEFLAG_COMMENT |
    154 		DNS_STYLEFLAG_RRCOMMENT | DNS_STYLEFLAG_MULTILINE,
    155 	24,
    156 	32,
    157 	32,
    158 	40,
    159 	80,
    160 	8,
    161 	UINT_MAX
    162 };
    163 
    164 const dns_master_style_t dns_master_style_cache = {
    165 	DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
    166 		DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_RRCOMMENT |
    167 		DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE,
    168 	24,
    169 	32,
    170 	32,
    171 	40,
    172 	80,
    173 	8,
    174 	UINT_MAX
    175 };
    176 
    177 const dns_master_style_t dns_master_style_cache_with_expired = {
    178 	DNS_STYLEFLAG_OMIT_OWNER | DNS_STYLEFLAG_OMIT_CLASS |
    179 		DNS_STYLEFLAG_MULTILINE | DNS_STYLEFLAG_RRCOMMENT |
    180 		DNS_STYLEFLAG_TRUST | DNS_STYLEFLAG_NCACHE |
    181 		DNS_STYLEFLAG_EXPIRED,
    182 	24,
    183 	32,
    184 	32,
    185 	40,
    186 	80,
    187 	8,
    188 	UINT_MAX
    189 };
    190 
    191 const dns_master_style_t dns_master_style_simple = { 0,	 24, 32, 32,
    192 						     40, 80, 8,	 UINT_MAX };
    193 
    194 /*%
    195  * A style suitable for dns_rdataset_totext().
    196  */
    197 const dns_master_style_t dns_master_style_debug = {
    198 	DNS_STYLEFLAG_REL_OWNER, 24, 32, 40, 48, 80, 8, UINT_MAX
    199 };
    200 
    201 /*%
    202  * Similar, but indented (i.e., prepended with indentctx.string).
    203  */
    204 const dns_master_style_t dns_master_style_indent = {
    205 	DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_INDENT,
    206 	24,
    207 	32,
    208 	40,
    209 	48,
    210 	80,
    211 	8,
    212 	UINT_MAX
    213 };
    214 
    215 /*%
    216  * Similar, but with each line commented out.
    217  */
    218 const dns_master_style_t dns_master_style_comment = {
    219 	DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_MULTILINE |
    220 		DNS_STYLEFLAG_RRCOMMENT | DNS_STYLEFLAG_COMMENTDATA,
    221 	24,
    222 	32,
    223 	40,
    224 	48,
    225 	80,
    226 	8,
    227 	UINT_MAX
    228 };
    229 
    230 /*%
    231  * YAML style
    232  */
    233 const dns_master_style_t dns_master_style_yaml = {
    234 	DNS_STYLEFLAG_YAML | DNS_STYLEFLAG_REL_OWNER | DNS_STYLEFLAG_INDENT,
    235 	24,
    236 	32,
    237 	40,
    238 	48,
    239 	80,
    240 	8,
    241 	UINT_MAX
    242 };
    243 
    244 #define N_SPACES 10
    245 static char spaces[N_SPACES + 1] = "          ";
    246 
    247 #define N_TABS 10
    248 static char tabs[N_TABS + 1] = "\t\t\t\t\t\t\t\t\t\t";
    249 
    250 struct dns_dumpctx {
    251 	unsigned int magic;
    252 	isc_mem_t *mctx;
    253 	isc_mutex_t lock;
    254 	isc_refcount_t references;
    255 	atomic_bool canceled;
    256 	bool do_date;
    257 	isc_stdtime_t now;
    258 	FILE *f;
    259 	dns_db_t *db;
    260 	dns_dbversion_t *version;
    261 	dns_dbiterator_t *dbiter;
    262 	dns_totext_ctx_t tctx;
    263 	dns_dumpdonefunc_t done;
    264 	void *done_arg;
    265 	/* dns_master_dumpasync() */
    266 	isc_result_t result;
    267 	char *file;
    268 	char *tmpfile;
    269 	dns_masterformat_t format;
    270 	dns_masterrawheader_t header;
    271 	isc_result_t (*dumpsets)(isc_mem_t *mctx, const dns_name_t *name,
    272 				 dns_rdatasetiter_t *rdsiter,
    273 				 dns_totext_ctx_t *ctx, isc_buffer_t *buffer,
    274 				 FILE *f);
    275 };
    276 
    277 #define NXDOMAIN(x) (((x)->attributes & DNS_RDATASETATTR_NXDOMAIN) != 0)
    278 
    279 static const dns_indent_t default_indent = { "\t", 1 };
    280 static const dns_indent_t default_yamlindent = { "  ", 1 };
    281 
    282 /*%
    283  * Output tabs and spaces to go from column '*current' to
    284  * column 'to', and update '*current' to reflect the new
    285  * current column.
    286  */
    287 static isc_result_t
    288 indent(unsigned int *current, unsigned int to, int tabwidth,
    289        isc_buffer_t *target) {
    290 	isc_region_t r;
    291 	unsigned char *p;
    292 	unsigned int from;
    293 	int ntabs, nspaces, t;
    294 
    295 	from = *current;
    296 
    297 	if (to < from + 1) {
    298 		to = from + 1;
    299 	}
    300 
    301 	ntabs = to / tabwidth - from / tabwidth;
    302 	if (ntabs < 0) {
    303 		ntabs = 0;
    304 	}
    305 
    306 	if (ntabs > 0) {
    307 		isc_buffer_availableregion(target, &r);
    308 		if (r.length < (unsigned int)ntabs) {
    309 			return ISC_R_NOSPACE;
    310 		}
    311 		p = r.base;
    312 
    313 		t = ntabs;
    314 		while (t) {
    315 			int n = t;
    316 			if (n > N_TABS) {
    317 				n = N_TABS;
    318 			}
    319 			memmove(p, tabs, n);
    320 			p += n;
    321 			t -= n;
    322 		}
    323 		isc_buffer_add(target, ntabs);
    324 		from = (to / tabwidth) * tabwidth;
    325 	}
    326 
    327 	nspaces = to - from;
    328 	INSIST(nspaces >= 0);
    329 
    330 	isc_buffer_availableregion(target, &r);
    331 	if (r.length < (unsigned int)nspaces) {
    332 		return ISC_R_NOSPACE;
    333 	}
    334 	p = r.base;
    335 
    336 	t = nspaces;
    337 	while (t) {
    338 		int n = t;
    339 		if (n > N_SPACES) {
    340 			n = N_SPACES;
    341 		}
    342 		memmove(p, spaces, n);
    343 		p += n;
    344 		t -= n;
    345 	}
    346 	isc_buffer_add(target, nspaces);
    347 
    348 	*current = to;
    349 	return ISC_R_SUCCESS;
    350 }
    351 
    352 static isc_result_t
    353 totext_ctx_init(const dns_master_style_t *style, const dns_indent_t *indentctx,
    354 		dns_totext_ctx_t *ctx) {
    355 	isc_result_t result;
    356 
    357 	REQUIRE(style->tab_width != 0);
    358 
    359 	if (indentctx == NULL) {
    360 		if ((style->flags & DNS_STYLEFLAG_YAML) != 0) {
    361 			indentctx = &default_yamlindent;
    362 		} else {
    363 			indentctx = &default_indent;
    364 		}
    365 	}
    366 
    367 	ctx->style = *style;
    368 	ctx->class_printed = false;
    369 
    370 	dns_fixedname_init(&ctx->origin_fixname);
    371 
    372 	/*
    373 	 * Set up the line break string if needed.
    374 	 */
    375 	if ((ctx->style.flags & DNS_STYLEFLAG_MULTILINE) != 0) {
    376 		isc_buffer_t buf;
    377 		isc_region_t r;
    378 		unsigned int col = 0;
    379 
    380 		isc_buffer_init(&buf, ctx->linebreak_buf,
    381 				sizeof(ctx->linebreak_buf));
    382 
    383 		isc_buffer_availableregion(&buf, &r);
    384 		if (r.length < 1) {
    385 			return DNS_R_TEXTTOOLONG;
    386 		}
    387 		r.base[0] = '\n';
    388 		isc_buffer_add(&buf, 1);
    389 
    390 		if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
    391 		    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
    392 		{
    393 			unsigned int i, len = strlen(indentctx->string);
    394 			for (i = 0; i < indentctx->count; i++) {
    395 				if (isc_buffer_availablelength(&buf) < len) {
    396 					return DNS_R_TEXTTOOLONG;
    397 				}
    398 				isc_buffer_putstr(&buf, indentctx->string);
    399 			}
    400 		}
    401 
    402 		if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0) {
    403 			isc_buffer_availableregion(&buf, &r);
    404 			if (r.length < 1) {
    405 				return DNS_R_TEXTTOOLONG;
    406 			}
    407 			r.base[0] = ';';
    408 			isc_buffer_add(&buf, 1);
    409 		}
    410 
    411 		result = indent(&col, ctx->style.rdata_column,
    412 				ctx->style.tab_width, &buf);
    413 		/*
    414 		 * Do not return ISC_R_NOSPACE if the line break string
    415 		 * buffer is too small, because that would just make
    416 		 * dump_rdataset() retry indefinitely with ever
    417 		 * bigger target buffers.  That's a different buffer,
    418 		 * so it won't help.  Use DNS_R_TEXTTOOLONG as a substitute.
    419 		 */
    420 		if (result == ISC_R_NOSPACE) {
    421 			return DNS_R_TEXTTOOLONG;
    422 		}
    423 		if (result != ISC_R_SUCCESS) {
    424 			return result;
    425 		}
    426 
    427 		isc_buffer_availableregion(&buf, &r);
    428 		if (r.length < 1) {
    429 			return DNS_R_TEXTTOOLONG;
    430 		}
    431 		r.base[0] = '\0';
    432 		isc_buffer_add(&buf, 1);
    433 		ctx->linebreak = ctx->linebreak_buf;
    434 	} else {
    435 		ctx->linebreak = NULL;
    436 	}
    437 
    438 	ctx->origin = NULL;
    439 	ctx->neworigin = NULL;
    440 	ctx->current_ttl = 0;
    441 	ctx->current_ttl_valid = false;
    442 	ctx->serve_stale_ttl = 0;
    443 	ctx->indent = *indentctx;
    444 
    445 	return ISC_R_SUCCESS;
    446 }
    447 
    448 #define INDENT_TO(col)                                                        \
    449 	do {                                                                  \
    450 		if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {           \
    451 			if ((result = str_totext(" ", target)) !=             \
    452 			    ISC_R_SUCCESS)                                    \
    453 				return ((result));                            \
    454 		} else if ((result = indent(&column, ctx->style.col,          \
    455 					    ctx->style.tab_width, target)) != \
    456 			   ISC_R_SUCCESS)                                     \
    457 			return ((result));                                    \
    458 	} while (0)
    459 
    460 static isc_result_t
    461 str_totext(const char *source, isc_buffer_t *target) {
    462 	unsigned int l;
    463 	isc_region_t region;
    464 
    465 	isc_buffer_availableregion(target, &region);
    466 	l = strlen(source);
    467 
    468 	if (l > region.length) {
    469 		return ISC_R_NOSPACE;
    470 	}
    471 
    472 	memmove(region.base, source, l);
    473 	isc_buffer_add(target, l);
    474 	return ISC_R_SUCCESS;
    475 }
    476 
    477 static isc_result_t
    478 yaml_stringify(isc_buffer_t *target, char *start) {
    479 	isc_region_t r;
    480 	char *s = start;
    481 	char *tmp = NULL;
    482 
    483 	isc_buffer_availableregion(target, &r);
    484 	if (r.length < 1) {
    485 		return ISC_R_NOSPACE;
    486 	}
    487 
    488 	/* NUL terminate buffer for string operations below */
    489 	r.base[0] = '\0';
    490 
    491 	/* Escape quotes in string using quote quote */
    492 	while ((tmp = strchr(s, '\'')) != NULL) {
    493 		isc_buffer_availableregion(target, &r);
    494 		/* Space to shift by 1 with trailing NUL? */
    495 		if (r.length < 2) {
    496 			return ISC_R_NOSPACE;
    497 		}
    498 		memmove(tmp + 1, tmp,
    499 			(char *)isc_buffer_used(target) - tmp + 1);
    500 		isc_buffer_add(target, 1);
    501 		/* We now have "''..." - skip both quotes. */
    502 		s = tmp + 2;
    503 	}
    504 
    505 	return ISC_R_SUCCESS;
    506 }
    507 
    508 static isc_result_t
    509 ncache_summary(dns_rdataset_t *rdataset, bool omit_final_dot,
    510 	       dns_totext_ctx_t *ctx, isc_buffer_t *target) {
    511 	isc_result_t result = ISC_R_SUCCESS;
    512 	dns_rdataset_t rds;
    513 	dns_name_t name;
    514 	char *start = NULL;
    515 
    516 	dns_rdataset_init(&rds);
    517 	dns_name_init(&name, NULL);
    518 
    519 	do {
    520 		dns_ncache_current(rdataset, &name, &rds);
    521 		for (result = dns_rdataset_first(&rds); result == ISC_R_SUCCESS;
    522 		     result = dns_rdataset_next(&rds))
    523 		{
    524 			if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
    525 			    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
    526 			{
    527 				unsigned int i;
    528 				for (i = 0; i < ctx->indent.count; i++) {
    529 					CHECK(str_totext(ctx->indent.string,
    530 							 target));
    531 				}
    532 			}
    533 
    534 			if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
    535 				CHECK(str_totext("- '", target));
    536 				start = isc_buffer_used(target);
    537 			} else {
    538 				CHECK(str_totext("; ", target));
    539 			}
    540 
    541 			CHECK(dns_name_totext(
    542 				&name,
    543 				omit_final_dot ? DNS_NAME_OMITFINALDOT : 0,
    544 				target));
    545 			CHECK(str_totext(" ", target));
    546 			CHECK(dns_rdatatype_totext(rds.type, target));
    547 			if (rds.type == dns_rdatatype_rrsig) {
    548 				CHECK(str_totext(" ", target));
    549 				CHECK(dns_rdatatype_totext(rds.covers, target));
    550 				CHECK(str_totext(" ...", target));
    551 			} else {
    552 				dns_rdata_t rdata = DNS_RDATA_INIT;
    553 				dns_rdataset_current(&rds, &rdata);
    554 				CHECK(str_totext(" ", target));
    555 				CHECK(dns_rdata_tofmttext(&rdata, dns_rootname,
    556 							  0, 0, 0, " ",
    557 							  target));
    558 			}
    559 			if (start != NULL) {
    560 				RETERR(yaml_stringify(target, start));
    561 				CHECK(str_totext("\'", target));
    562 			}
    563 			CHECK(str_totext("\n", target));
    564 		}
    565 		dns_rdataset_disassociate(&rds);
    566 		result = dns_rdataset_next(rdataset);
    567 	} while (result == ISC_R_SUCCESS);
    568 
    569 	if (result == ISC_R_NOMORE) {
    570 		result = ISC_R_SUCCESS;
    571 	}
    572 cleanup:
    573 	if (dns_rdataset_isassociated(&rds)) {
    574 		dns_rdataset_disassociate(&rds);
    575 	}
    576 
    577 	return result;
    578 }
    579 
    580 /*
    581  * Convert 'rdataset' to master file text format according to 'ctx',
    582  * storing the result in 'target'.  If 'owner_name' is NULL, it
    583  * is omitted; otherwise 'owner_name' must be valid and have at least
    584  * one label.
    585  */
    586 
    587 static isc_result_t
    588 rdataset_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
    589 		dns_totext_ctx_t *ctx, bool omit_final_dot,
    590 		isc_buffer_t *target) {
    591 	isc_result_t result;
    592 	unsigned int column;
    593 	bool first = true;
    594 	uint32_t current_ttl;
    595 	bool current_ttl_valid;
    596 	dns_rdatatype_t type;
    597 	unsigned int type_start;
    598 	dns_fixedname_t fixed;
    599 	dns_name_t *name = NULL;
    600 	unsigned int i;
    601 	char *start = NULL;
    602 
    603 	REQUIRE(DNS_RDATASET_VALID(rdataset));
    604 
    605 	rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
    606 	result = dns_rdataset_first(rdataset);
    607 
    608 	current_ttl = ctx->current_ttl;
    609 	current_ttl_valid = ctx->current_ttl_valid;
    610 
    611 	if (owner_name != NULL) {
    612 		name = dns_fixedname_initname(&fixed);
    613 		dns_name_copy(owner_name, name);
    614 		dns_rdataset_getownercase(rdataset, name);
    615 	}
    616 
    617 	while (result == ISC_R_SUCCESS) {
    618 		column = 0;
    619 
    620 		/*
    621 		 * Indent?
    622 		 */
    623 		if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
    624 		    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
    625 		{
    626 			for (i = 0; i < ctx->indent.count; i++) {
    627 				RETERR(str_totext(ctx->indent.string, target));
    628 			}
    629 		}
    630 
    631 		/*
    632 		 * YAML or comment prefix?
    633 		 */
    634 		if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
    635 			RETERR(str_totext("- '", target));
    636 			start = isc_buffer_used(target);
    637 		} else if ((ctx->style.flags & DNS_STYLEFLAG_COMMENTDATA) != 0)
    638 		{
    639 			RETERR(str_totext(";", target));
    640 		}
    641 
    642 		/*
    643 		 * Owner name.
    644 		 */
    645 		if (name != NULL &&
    646 		    !((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0 &&
    647 		      !first))
    648 		{
    649 			unsigned int name_start = target->used;
    650 			RETERR(dns_name_totext(
    651 				name,
    652 				omit_final_dot ? DNS_NAME_OMITFINALDOT : 0,
    653 				target));
    654 			column += target->used - name_start;
    655 		}
    656 
    657 		/*
    658 		 * TTL.
    659 		 */
    660 		if ((ctx->style.flags & DNS_STYLEFLAG_NO_TTL) == 0 &&
    661 		    !((ctx->style.flags & DNS_STYLEFLAG_OMIT_TTL) != 0 &&
    662 		      current_ttl_valid && rdataset->ttl == current_ttl))
    663 		{
    664 			char ttlbuf[64];
    665 			isc_region_t r;
    666 			unsigned int length;
    667 
    668 			INDENT_TO(ttl_column);
    669 			if ((ctx->style.flags & DNS_STYLEFLAG_TTL_UNITS) != 0) {
    670 				length = target->used;
    671 				result = dns_ttl_totext(rdataset->ttl, false,
    672 							false, target);
    673 				if (result != ISC_R_SUCCESS) {
    674 					return result;
    675 				}
    676 				column += target->used - length;
    677 			} else {
    678 				length = snprintf(ttlbuf, sizeof(ttlbuf), "%u",
    679 						  rdataset->ttl);
    680 				INSIST(length <= sizeof(ttlbuf));
    681 				isc_buffer_availableregion(target, &r);
    682 				if (r.length < length) {
    683 					return ISC_R_NOSPACE;
    684 				}
    685 				memmove(r.base, ttlbuf, length);
    686 				isc_buffer_add(target, length);
    687 				column += length;
    688 			}
    689 
    690 			/*
    691 			 * If the $TTL directive is not in use, the TTL we
    692 			 * just printed becomes the default for subsequent RRs.
    693 			 */
    694 			if ((ctx->style.flags & DNS_STYLEFLAG_TTL) == 0) {
    695 				current_ttl = rdataset->ttl;
    696 				current_ttl_valid = true;
    697 			}
    698 		}
    699 
    700 		/*
    701 		 * Class.
    702 		 */
    703 		if ((ctx->style.flags & DNS_STYLEFLAG_NO_CLASS) == 0 &&
    704 		    ((ctx->style.flags & DNS_STYLEFLAG_OMIT_CLASS) == 0 ||
    705 		     !ctx->class_printed))
    706 		{
    707 			unsigned int class_start;
    708 			INDENT_TO(class_column);
    709 			class_start = target->used;
    710 			if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) !=
    711 			    0)
    712 			{
    713 				result = dns_rdataclass_tounknowntext(
    714 					rdataset->rdclass, target);
    715 			} else {
    716 				result = dns_rdataclass_totext(
    717 					rdataset->rdclass, target);
    718 			}
    719 			if (result != ISC_R_SUCCESS) {
    720 				return result;
    721 			}
    722 			column += (target->used - class_start);
    723 		}
    724 
    725 		/*
    726 		 * Type.
    727 		 */
    728 
    729 		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
    730 			type = rdataset->covers;
    731 		} else {
    732 			type = rdataset->type;
    733 		}
    734 
    735 		INDENT_TO(type_column);
    736 		type_start = target->used;
    737 		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
    738 			RETERR(str_totext("\\-", target));
    739 		}
    740 		switch (type) {
    741 		case dns_rdatatype_keydata:
    742 #define KEYDATA "KEYDATA"
    743 			if ((ctx->style.flags & DNS_STYLEFLAG_KEYDATA) != 0) {
    744 				if (isc_buffer_availablelength(target) <
    745 				    (sizeof(KEYDATA) - 1))
    746 				{
    747 					return ISC_R_NOSPACE;
    748 				}
    749 				isc_buffer_putstr(target, KEYDATA);
    750 				break;
    751 			}
    752 			FALLTHROUGH;
    753 		default:
    754 			if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) !=
    755 			    0)
    756 			{
    757 				result = dns_rdatatype_tounknowntext(type,
    758 								     target);
    759 			} else {
    760 				result = dns_rdatatype_totext(type, target);
    761 			}
    762 			if (result != ISC_R_SUCCESS) {
    763 				return result;
    764 			}
    765 		}
    766 		column += (target->used - type_start);
    767 
    768 		/*
    769 		 * Rdata.
    770 		 */
    771 		INDENT_TO(rdata_column);
    772 		if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
    773 			if (NXDOMAIN(rdataset)) {
    774 				RETERR(str_totext(";-$NXDOMAIN", target));
    775 			} else {
    776 				RETERR(str_totext(";-$NXRRSET", target));
    777 			}
    778 			if (start != NULL) {
    779 				RETERR(yaml_stringify(target, start));
    780 				RETERR(str_totext("'\n", target));
    781 			} else {
    782 				RETERR(str_totext("\n", target));
    783 			}
    784 
    785 			/*
    786 			 * Print a summary of the cached records which make
    787 			 * up the negative response.
    788 			 */
    789 			RETERR(ncache_summary(rdataset, omit_final_dot, ctx,
    790 					      target));
    791 			break;
    792 		} else {
    793 			dns_rdata_t rdata = DNS_RDATA_INIT;
    794 
    795 			dns_rdataset_current(rdataset, &rdata);
    796 
    797 			RETERR(dns_rdata_tofmttext(
    798 				&rdata, ctx->origin, ctx->style.flags,
    799 				ctx->style.line_length -
    800 					ctx->style.rdata_column,
    801 				ctx->style.split_width, ctx->linebreak,
    802 				target));
    803 			if (start != NULL) {
    804 				RETERR(yaml_stringify(target, start));
    805 				RETERR(str_totext("'\n", target));
    806 			} else {
    807 				RETERR(str_totext("\n", target));
    808 			}
    809 		}
    810 
    811 		first = false;
    812 		result = dns_rdataset_next(rdataset);
    813 	}
    814 
    815 	if (result != ISC_R_NOMORE) {
    816 		return result;
    817 	}
    818 
    819 	/*
    820 	 * Update the ctx state to reflect what we just printed.
    821 	 * This is done last, only when we are sure we will return
    822 	 * success, because this function may be called multiple
    823 	 * times with increasing buffer sizes until it succeeds,
    824 	 * and failed attempts must not update the state prematurely.
    825 	 */
    826 	ctx->class_printed = true;
    827 	ctx->current_ttl = current_ttl;
    828 	ctx->current_ttl_valid = current_ttl_valid;
    829 
    830 	return ISC_R_SUCCESS;
    831 }
    832 
    833 /*
    834  * Print the name, type, and class of an empty rdataset,
    835  * such as those used to represent the question section
    836  * of a DNS message.
    837  */
    838 static isc_result_t
    839 question_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
    840 		dns_totext_ctx_t *ctx, bool omit_final_dot,
    841 		isc_buffer_t *target) {
    842 	unsigned int column;
    843 	isc_result_t result;
    844 	char *start = NULL;
    845 
    846 	REQUIRE(DNS_RDATASET_VALID(rdataset));
    847 	result = dns_rdataset_first(rdataset);
    848 	REQUIRE(result == ISC_R_NOMORE);
    849 
    850 	column = 0;
    851 
    852 	if ((ctx->style.flags & DNS_STYLEFLAG_YAML) != 0) {
    853 		RETERR(str_totext("- '", target));
    854 		start = isc_buffer_used(target);
    855 	}
    856 
    857 	/* Owner name */
    858 	{
    859 		unsigned int name_start = target->used;
    860 		unsigned int opts = omit_final_dot ? DNS_NAME_OMITFINALDOT : 0;
    861 		RETERR(dns_name_totext(owner_name, opts, target));
    862 		column += target->used - name_start;
    863 	}
    864 
    865 	/* Class */
    866 	{
    867 		unsigned int class_start;
    868 		INDENT_TO(class_column);
    869 		class_start = target->used;
    870 		if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) {
    871 			result = dns_rdataclass_tounknowntext(rdataset->rdclass,
    872 							      target);
    873 		} else {
    874 			result = dns_rdataclass_totext(rdataset->rdclass,
    875 						       target);
    876 		}
    877 		if (result != ISC_R_SUCCESS) {
    878 			return result;
    879 		}
    880 		column += (target->used - class_start);
    881 	}
    882 
    883 	/* Type */
    884 	{
    885 		unsigned int type_start;
    886 		INDENT_TO(type_column);
    887 		type_start = target->used;
    888 		if ((ctx->style.flags & DNS_STYLEFLAG_UNKNOWNFORMAT) != 0) {
    889 			result = dns_rdatatype_tounknowntext(rdataset->type,
    890 							     target);
    891 		} else {
    892 			result = dns_rdatatype_totext(rdataset->type, target);
    893 		}
    894 		if (result != ISC_R_SUCCESS) {
    895 			return result;
    896 		}
    897 		column += (target->used - type_start);
    898 	}
    899 
    900 	if (start != NULL) {
    901 		RETERR(yaml_stringify(target, start));
    902 		RETERR(str_totext("\'", target));
    903 	}
    904 	RETERR(str_totext("\n", target));
    905 
    906 	return ISC_R_SUCCESS;
    907 }
    908 
    909 isc_result_t
    910 dns_rdataset_totext(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
    911 		    bool omit_final_dot, bool question, isc_buffer_t *target) {
    912 	dns_totext_ctx_t ctx;
    913 	isc_result_t result;
    914 	result = totext_ctx_init(&dns_master_style_debug, NULL, &ctx);
    915 	if (result != ISC_R_SUCCESS) {
    916 		UNEXPECTED_ERROR("could not set master file style");
    917 		return ISC_R_UNEXPECTED;
    918 	}
    919 
    920 	/*
    921 	 * The caller might want to give us an empty owner
    922 	 * name (e.g. if they are outputting into a master
    923 	 * file and this rdataset has the same name as the
    924 	 * previous one.)
    925 	 */
    926 	if (dns_name_countlabels(owner_name) == 0) {
    927 		owner_name = NULL;
    928 	}
    929 
    930 	if (question) {
    931 		return question_totext(rdataset, owner_name, &ctx,
    932 				       omit_final_dot, target);
    933 	} else {
    934 		return rdataset_totext(rdataset, owner_name, &ctx,
    935 				       omit_final_dot, target);
    936 	}
    937 }
    938 
    939 isc_result_t
    940 dns_master_rdatasettotext(const dns_name_t *owner_name,
    941 			  dns_rdataset_t *rdataset,
    942 			  const dns_master_style_t *style, dns_indent_t *indent,
    943 			  isc_buffer_t *target) {
    944 	dns_totext_ctx_t ctx;
    945 	isc_result_t result;
    946 	result = totext_ctx_init(style, indent, &ctx);
    947 	if (result != ISC_R_SUCCESS) {
    948 		UNEXPECTED_ERROR("could not set master file style");
    949 		return ISC_R_UNEXPECTED;
    950 	}
    951 
    952 	return rdataset_totext(rdataset, owner_name, &ctx, false, target);
    953 }
    954 
    955 isc_result_t
    956 dns_master_questiontotext(const dns_name_t *owner_name,
    957 			  dns_rdataset_t *rdataset,
    958 			  const dns_master_style_t *style,
    959 			  isc_buffer_t *target) {
    960 	dns_totext_ctx_t ctx;
    961 	isc_result_t result;
    962 	result = totext_ctx_init(style, NULL, &ctx);
    963 	if (result != ISC_R_SUCCESS) {
    964 		UNEXPECTED_ERROR("could not set master file style");
    965 		return ISC_R_UNEXPECTED;
    966 	}
    967 
    968 	return question_totext(rdataset, owner_name, &ctx, false, target);
    969 }
    970 
    971 /*
    972  * Print an rdataset.  'buffer' is a scratch buffer, which must have been
    973  * dynamically allocated by the caller.  It must be large enough to
    974  * hold the result from dns_ttl_totext().  If more than that is needed,
    975  * the buffer will be grown automatically.
    976  */
    977 
    978 static isc_result_t
    979 dump_rdataset(isc_mem_t *mctx, const dns_name_t *name, dns_rdataset_t *rdataset,
    980 	      dns_totext_ctx_t *ctx, isc_buffer_t *buffer, FILE *f) {
    981 	isc_region_t r;
    982 	isc_result_t result;
    983 
    984 	REQUIRE(buffer->length > 0);
    985 
    986 	/*
    987 	 * Output a $TTL directive if needed.
    988 	 */
    989 
    990 	if ((ctx->style.flags & DNS_STYLEFLAG_TTL) != 0) {
    991 		if (!ctx->current_ttl_valid ||
    992 		    ctx->current_ttl != rdataset->ttl)
    993 		{
    994 			if ((ctx->style.flags & DNS_STYLEFLAG_COMMENT) != 0) {
    995 				isc_buffer_clear(buffer);
    996 				result = dns_ttl_totext(rdataset->ttl, true,
    997 							true, buffer);
    998 				INSIST(result == ISC_R_SUCCESS);
    999 				isc_buffer_usedregion(buffer, &r);
   1000 				fprintf(f, "$TTL %u\t; %.*s\n", rdataset->ttl,
   1001 					(int)r.length, (char *)r.base);
   1002 			} else {
   1003 				fprintf(f, "$TTL %u\n", rdataset->ttl);
   1004 			}
   1005 			ctx->current_ttl = rdataset->ttl;
   1006 			ctx->current_ttl_valid = true;
   1007 		}
   1008 	}
   1009 
   1010 	isc_buffer_clear(buffer);
   1011 
   1012 	/*
   1013 	 * Generate the text representation of the rdataset into
   1014 	 * the buffer.  If the buffer is too small, grow it.
   1015 	 */
   1016 	for (;;) {
   1017 		int newlength;
   1018 		void *newmem;
   1019 		result = rdataset_totext(rdataset, name, ctx, false, buffer);
   1020 		if (result != ISC_R_NOSPACE) {
   1021 			break;
   1022 		}
   1023 
   1024 		newlength = buffer->length * 2;
   1025 		newmem = isc_mem_get(mctx, newlength);
   1026 		isc_mem_put(mctx, buffer->base, buffer->length);
   1027 		isc_buffer_init(buffer, newmem, newlength);
   1028 	}
   1029 	if (result != ISC_R_SUCCESS) {
   1030 		return result;
   1031 	}
   1032 
   1033 	/*
   1034 	 * Write the buffer contents to the master file.
   1035 	 */
   1036 	isc_buffer_usedregion(buffer, &r);
   1037 	result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
   1038 
   1039 	if (result != ISC_R_SUCCESS) {
   1040 		UNEXPECTED_ERROR("master file write failed: %s",
   1041 				 isc_result_totext(result));
   1042 		return result;
   1043 	}
   1044 
   1045 	return ISC_R_SUCCESS;
   1046 }
   1047 
   1048 /*
   1049  * Define the order in which rdatasets should be printed in zone
   1050  * files.  We will print SOA and NS records before others, SIGs
   1051  * immediately following the things they sign, and order everything
   1052  * else by RR number.  This is all just for aesthetics and
   1053  * compatibility with buggy software that expects the SOA to be first;
   1054  * the DNS specifications allow any order.
   1055  */
   1056 
   1057 static int
   1058 dump_order(const dns_rdataset_t *rds) {
   1059 	int t;
   1060 	int sig;
   1061 	if (rds->type == dns_rdatatype_rrsig) {
   1062 		t = rds->covers;
   1063 		sig = 1;
   1064 	} else {
   1065 		t = rds->type;
   1066 		sig = 0;
   1067 	}
   1068 	switch (t) {
   1069 	case dns_rdatatype_soa:
   1070 		t = 0;
   1071 		break;
   1072 	case dns_rdatatype_ns:
   1073 		t = 1;
   1074 		break;
   1075 	default:
   1076 		t += 2;
   1077 		break;
   1078 	}
   1079 	return (t << 1) + sig;
   1080 }
   1081 
   1082 static int
   1083 dump_order_compare(const void *a, const void *b) {
   1084 	return dump_order(*((const dns_rdataset_t *const *)a)) -
   1085 	       dump_order(*((const dns_rdataset_t *const *)b));
   1086 }
   1087 
   1088 /*
   1089  * Dump all the rdatasets of a domain name to a master file.  We make
   1090  * a "best effort" attempt to sort the RRsets in a nice order, but if
   1091  * there are more than MAXSORT RRsets, we punt and only sort them in
   1092  * groups of MAXSORT.  This is not expected to ever happen in practice
   1093  * since much less than 64 RR types have been registered with the
   1094  * IANA, so far, and the output will be correct (though not
   1095  * aesthetically pleasing) even if it does happen.
   1096  */
   1097 
   1098 #define MAXSORT 64
   1099 
   1100 static isc_result_t
   1101 dump_rdatasets_text(isc_mem_t *mctx, const dns_name_t *name,
   1102 		    dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
   1103 		    isc_buffer_t *buffer, FILE *f) {
   1104 	isc_result_t itresult, dumpresult;
   1105 	isc_region_t r;
   1106 	dns_rdataset_t rdatasets[MAXSORT];
   1107 	dns_rdataset_t *sorted[MAXSORT];
   1108 	int i, n;
   1109 
   1110 	itresult = dns_rdatasetiter_first(rdsiter);
   1111 	dumpresult = ISC_R_SUCCESS;
   1112 
   1113 	if (itresult == ISC_R_SUCCESS && ctx->neworigin != NULL) {
   1114 		isc_buffer_clear(buffer);
   1115 		itresult = dns_name_totext(ctx->neworigin, 0, buffer);
   1116 		RUNTIME_CHECK(itresult == ISC_R_SUCCESS);
   1117 		isc_buffer_usedregion(buffer, &r);
   1118 		fprintf(f, "$ORIGIN %.*s\n", (int)r.length, (char *)r.base);
   1119 		ctx->neworigin = NULL;
   1120 	}
   1121 
   1122 	if ((ctx->style.flags & DNS_STYLEFLAG_CLASS_PERNAME) != 0) {
   1123 		ctx->class_printed = false;
   1124 	}
   1125 
   1126 again:
   1127 	for (i = 0; itresult == ISC_R_SUCCESS && i < MAXSORT;
   1128 	     itresult = dns_rdatasetiter_next(rdsiter), i++)
   1129 	{
   1130 		dns_rdataset_init(&rdatasets[i]);
   1131 		dns_rdatasetiter_current(rdsiter, &rdatasets[i]);
   1132 		sorted[i] = &rdatasets[i];
   1133 	}
   1134 	n = i;
   1135 
   1136 	qsort(sorted, n, sizeof(sorted[0]), dump_order_compare);
   1137 
   1138 	for (i = 0; i < n; i++) {
   1139 		dns_rdataset_t *rds = sorted[i];
   1140 
   1141 		if (ANCIENT(rds) &&
   1142 		    (ctx->style.flags & DNS_STYLEFLAG_EXPIRED) == 0)
   1143 		{
   1144 			/* Omit expired entries */
   1145 			dns_rdataset_disassociate(rds);
   1146 			continue;
   1147 		}
   1148 
   1149 		if ((ctx->style.flags & DNS_STYLEFLAG_TRUST) != 0) {
   1150 			if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
   1151 			    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
   1152 			{
   1153 				unsigned int j;
   1154 				for (j = 0; j < ctx->indent.count; j++) {
   1155 					fprintf(f, "%s", ctx->indent.string);
   1156 				}
   1157 			}
   1158 			fprintf(f, "; %s\n", dns_trust_totext(rds->trust));
   1159 		}
   1160 		if (((rds->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
   1161 		    (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0)
   1162 		{
   1163 			/* Omit negative cache entries */
   1164 		} else {
   1165 			isc_result_t result;
   1166 			if (STALE(rds)) {
   1167 				isc_buffer_t b;
   1168 				char buf[sizeof("YYYYMMDDHHMMSS")];
   1169 				memset(buf, 0, sizeof(buf));
   1170 				isc_buffer_init(&b, buf, sizeof(buf) - 1);
   1171 				dns_time64_totext((uint64_t)rds->expire, &b);
   1172 				fprintf(f, "; stale since %s\n", buf);
   1173 			} else if (ANCIENT(rds)) {
   1174 				fprintf(f, "; expired (awaiting cleanup)\n");
   1175 			}
   1176 			result = dump_rdataset(mctx, name, rds, ctx, buffer, f);
   1177 			if (result != ISC_R_SUCCESS) {
   1178 				dumpresult = result;
   1179 			}
   1180 			if ((ctx->style.flags & DNS_STYLEFLAG_OMIT_OWNER) != 0)
   1181 			{
   1182 				name = NULL;
   1183 			}
   1184 		}
   1185 		if (((ctx->style.flags & DNS_STYLEFLAG_RESIGN) != 0) &&
   1186 		    ((rds->attributes & DNS_RDATASETATTR_RESIGN) != 0))
   1187 		{
   1188 			isc_buffer_t b;
   1189 			char buf[sizeof("YYYYMMDDHHMMSS")];
   1190 			memset(buf, 0, sizeof(buf));
   1191 			isc_buffer_init(&b, buf, sizeof(buf) - 1);
   1192 			dns_time64_totext((uint64_t)rds->resign, &b);
   1193 			if ((ctx->style.flags & DNS_STYLEFLAG_INDENT) != 0 ||
   1194 			    (ctx->style.flags & DNS_STYLEFLAG_YAML) != 0)
   1195 			{
   1196 				unsigned int j;
   1197 				for (j = 0; j < ctx->indent.count; j++) {
   1198 					fprintf(f, "%s", ctx->indent.string);
   1199 				}
   1200 			}
   1201 			fprintf(f, "; resign=%s\n", buf);
   1202 		}
   1203 		dns_rdataset_disassociate(rds);
   1204 	}
   1205 
   1206 	if (dumpresult != ISC_R_SUCCESS) {
   1207 		return dumpresult;
   1208 	}
   1209 
   1210 	/*
   1211 	 * If we got more data than could be sorted at once,
   1212 	 * go handle the rest.
   1213 	 */
   1214 	if (itresult == ISC_R_SUCCESS) {
   1215 		goto again;
   1216 	}
   1217 
   1218 	if (itresult == ISC_R_NOMORE) {
   1219 		itresult = ISC_R_SUCCESS;
   1220 	}
   1221 
   1222 	return itresult;
   1223 }
   1224 
   1225 /*
   1226  * Dump given RRsets in the "raw" format.
   1227  */
   1228 static isc_result_t
   1229 dump_rdataset_raw(isc_mem_t *mctx, const dns_name_t *name,
   1230 		  dns_rdataset_t *rdataset, isc_buffer_t *buffer, FILE *f) {
   1231 	isc_result_t result;
   1232 	uint32_t totallen;
   1233 	uint16_t dlen;
   1234 	isc_region_t r, r_hdr;
   1235 
   1236 	REQUIRE(buffer->length > 0);
   1237 	REQUIRE(DNS_RDATASET_VALID(rdataset));
   1238 
   1239 	rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
   1240 restart:
   1241 	totallen = 0;
   1242 	result = dns_rdataset_first(rdataset);
   1243 	REQUIRE(result == ISC_R_SUCCESS);
   1244 
   1245 	isc_buffer_clear(buffer);
   1246 
   1247 	/*
   1248 	 * Common header and owner name (length followed by name)
   1249 	 * These fields should be in a moderate length, so we assume we
   1250 	 * can store all of them in the initial buffer.
   1251 	 */
   1252 	isc_buffer_availableregion(buffer, &r_hdr);
   1253 	INSIST(r_hdr.length >= sizeof(dns_masterrawrdataset_t));
   1254 	isc_buffer_putuint32(buffer, totallen);		 /* XXX: leave space */
   1255 	isc_buffer_putuint16(buffer, rdataset->rdclass); /* 16-bit class */
   1256 	isc_buffer_putuint16(buffer, rdataset->type);	 /* 16-bit type */
   1257 	isc_buffer_putuint16(buffer, rdataset->covers);	 /* same as type */
   1258 	isc_buffer_putuint32(buffer, rdataset->ttl);	 /* 32-bit TTL */
   1259 	isc_buffer_putuint32(buffer, dns_rdataset_count(rdataset));
   1260 	totallen = isc_buffer_usedlength(buffer);
   1261 	INSIST(totallen <= sizeof(dns_masterrawrdataset_t));
   1262 
   1263 	dns_name_toregion(name, &r);
   1264 	INSIST(isc_buffer_availablelength(buffer) >= (sizeof(dlen) + r.length));
   1265 	dlen = (uint16_t)r.length;
   1266 	isc_buffer_putuint16(buffer, dlen);
   1267 	isc_buffer_copyregion(buffer, &r);
   1268 	totallen += sizeof(dlen) + r.length;
   1269 
   1270 	do {
   1271 		dns_rdata_t rdata = DNS_RDATA_INIT;
   1272 
   1273 		dns_rdataset_current(rdataset, &rdata);
   1274 		dns_rdata_toregion(&rdata, &r);
   1275 		INSIST(r.length <= 0xffffU);
   1276 		dlen = (uint16_t)r.length;
   1277 
   1278 		/*
   1279 		 * Copy the rdata into the buffer.  If the buffer is too small,
   1280 		 * grow it.  This should be rare, so we'll simply restart the
   1281 		 * entire procedure (or should we copy the old data and
   1282 		 * continue?).
   1283 		 */
   1284 		if (isc_buffer_availablelength(buffer) <
   1285 		    sizeof(dlen) + r.length)
   1286 		{
   1287 			int newlength;
   1288 			void *newmem;
   1289 
   1290 			newlength = buffer->length * 2;
   1291 			newmem = isc_mem_get(mctx, newlength);
   1292 			isc_mem_put(mctx, buffer->base, buffer->length);
   1293 			isc_buffer_init(buffer, newmem, newlength);
   1294 			goto restart;
   1295 		}
   1296 		isc_buffer_putuint16(buffer, dlen);
   1297 		isc_buffer_copyregion(buffer, &r);
   1298 		totallen += sizeof(dlen) + r.length;
   1299 
   1300 		result = dns_rdataset_next(rdataset);
   1301 	} while (result == ISC_R_SUCCESS);
   1302 
   1303 	if (result != ISC_R_NOMORE) {
   1304 		return result;
   1305 	}
   1306 
   1307 	/*
   1308 	 * Fill in the total length field.
   1309 	 * XXX: this is a bit tricky.  Since we have already "used" the space
   1310 	 * for the total length in the buffer, we first remember the entire
   1311 	 * buffer length in the region, "rewind", and then write the value.
   1312 	 */
   1313 	isc_buffer_usedregion(buffer, &r);
   1314 	isc_buffer_clear(buffer);
   1315 	isc_buffer_putuint32(buffer, totallen);
   1316 	INSIST(isc_buffer_usedlength(buffer) < totallen);
   1317 
   1318 	/*
   1319 	 * Write the buffer contents to the raw master file.
   1320 	 */
   1321 	result = isc_stdio_write(r.base, 1, (size_t)r.length, f, NULL);
   1322 
   1323 	if (result != ISC_R_SUCCESS) {
   1324 		UNEXPECTED_ERROR("raw master file write failed: %s",
   1325 				 isc_result_totext(result));
   1326 		return result;
   1327 	}
   1328 
   1329 	return result;
   1330 }
   1331 
   1332 static isc_result_t
   1333 dump_rdatasets_raw(isc_mem_t *mctx, const dns_name_t *owner_name,
   1334 		   dns_rdatasetiter_t *rdsiter, dns_totext_ctx_t *ctx,
   1335 		   isc_buffer_t *buffer, FILE *f) {
   1336 	isc_result_t result;
   1337 	dns_rdataset_t rdataset;
   1338 	dns_fixedname_t fixed;
   1339 	dns_name_t *name;
   1340 
   1341 	name = dns_fixedname_initname(&fixed);
   1342 	dns_name_copy(owner_name, name);
   1343 	for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS;
   1344 	     result = dns_rdatasetiter_next(rdsiter))
   1345 	{
   1346 		dns_rdataset_init(&rdataset);
   1347 		dns_rdatasetiter_current(rdsiter, &rdataset);
   1348 
   1349 		dns_rdataset_getownercase(&rdataset, name);
   1350 
   1351 		if (((rdataset.attributes & DNS_RDATASETATTR_NEGATIVE) != 0) &&
   1352 		    (ctx->style.flags & DNS_STYLEFLAG_NCACHE) == 0)
   1353 		{
   1354 			/* Omit negative cache entries */
   1355 		} else {
   1356 			result = dump_rdataset_raw(mctx, name, &rdataset,
   1357 						   buffer, f);
   1358 		}
   1359 		dns_rdataset_disassociate(&rdataset);
   1360 		if (result != ISC_R_SUCCESS) {
   1361 			return result;
   1362 		}
   1363 	}
   1364 
   1365 	if (result == ISC_R_NOMORE) {
   1366 		result = ISC_R_SUCCESS;
   1367 	}
   1368 
   1369 	return result;
   1370 }
   1371 
   1372 /*
   1373  * Initial size of text conversion buffer.  The buffer is used
   1374  * for several purposes: converting origin names, rdatasets,
   1375  * $DATE timestamps, and comment strings for $TTL directives.
   1376  *
   1377  * When converting rdatasets, it is dynamically resized, but
   1378  * when converting origins, timestamps, etc it is not.  Therefore,
   1379  * the initial size must large enough to hold the longest possible
   1380  * text representation of any domain name (for $ORIGIN).
   1381  */
   1382 static const int initial_buffer_length = 1200;
   1383 
   1384 static isc_result_t
   1385 dumptostream(dns_dumpctx_t *dctx);
   1386 
   1387 static void
   1388 dumpctx_destroy(dns_dumpctx_t *dctx) {
   1389 	dctx->magic = 0;
   1390 	isc_mutex_destroy(&dctx->lock);
   1391 	dns_dbiterator_destroy(&dctx->dbiter);
   1392 	if (dctx->version != NULL) {
   1393 		dns_db_closeversion(dctx->db, &dctx->version, false);
   1394 	}
   1395 	dns_db_detach(&dctx->db);
   1396 	if (dctx->file != NULL) {
   1397 		isc_mem_free(dctx->mctx, dctx->file);
   1398 	}
   1399 	if (dctx->tmpfile != NULL) {
   1400 		isc_mem_free(dctx->mctx, dctx->tmpfile);
   1401 	}
   1402 	isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
   1403 }
   1404 
   1405 void
   1406 dns_dumpctx_attach(dns_dumpctx_t *source, dns_dumpctx_t **target) {
   1407 	REQUIRE(DNS_DCTX_VALID(source));
   1408 	REQUIRE(target != NULL && *target == NULL);
   1409 
   1410 	isc_refcount_increment(&source->references);
   1411 
   1412 	*target = source;
   1413 }
   1414 
   1415 void
   1416 dns_dumpctx_detach(dns_dumpctx_t **dctxp) {
   1417 	dns_dumpctx_t *dctx;
   1418 
   1419 	REQUIRE(dctxp != NULL);
   1420 	dctx = *dctxp;
   1421 	*dctxp = NULL;
   1422 	REQUIRE(DNS_DCTX_VALID(dctx));
   1423 
   1424 	if (isc_refcount_decrement(&dctx->references) == 1) {
   1425 		dumpctx_destroy(dctx);
   1426 	}
   1427 }
   1428 
   1429 dns_dbversion_t *
   1430 dns_dumpctx_version(dns_dumpctx_t *dctx) {
   1431 	REQUIRE(DNS_DCTX_VALID(dctx));
   1432 	return dctx->version;
   1433 }
   1434 
   1435 dns_db_t *
   1436 dns_dumpctx_db(dns_dumpctx_t *dctx) {
   1437 	REQUIRE(DNS_DCTX_VALID(dctx));
   1438 	return dctx->db;
   1439 }
   1440 
   1441 void
   1442 dns_dumpctx_cancel(dns_dumpctx_t *dctx) {
   1443 	REQUIRE(DNS_DCTX_VALID(dctx));
   1444 
   1445 	atomic_store_release(&dctx->canceled, true);
   1446 }
   1447 
   1448 static isc_result_t
   1449 flushandsync(FILE *f, isc_result_t result, const char *temp) {
   1450 	bool logit = (result == ISC_R_SUCCESS);
   1451 
   1452 	if (result == ISC_R_SUCCESS) {
   1453 		result = isc_stdio_flush(f);
   1454 	}
   1455 	if (result != ISC_R_SUCCESS && logit) {
   1456 		if (temp != NULL) {
   1457 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1458 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1459 				      "dumping to master file: %s: flush: %s",
   1460 				      temp, isc_result_totext(result));
   1461 		} else {
   1462 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1463 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1464 				      "dumping to stream: flush: %s",
   1465 				      isc_result_totext(result));
   1466 		}
   1467 		logit = false;
   1468 	}
   1469 
   1470 	if (result == ISC_R_SUCCESS) {
   1471 		result = isc_stdio_sync(f);
   1472 	}
   1473 	if (result != ISC_R_SUCCESS && logit) {
   1474 		if (temp != NULL) {
   1475 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1476 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1477 				      "dumping to master file: %s: fsync: %s",
   1478 				      temp, isc_result_totext(result));
   1479 		} else {
   1480 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1481 				      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1482 				      "dumping to stream: fsync: %s",
   1483 				      isc_result_totext(result));
   1484 		}
   1485 	}
   1486 	return result;
   1487 }
   1488 
   1489 static isc_result_t
   1490 closeandrename(FILE *f, isc_result_t result, const char *temp,
   1491 	       const char *file) {
   1492 	isc_result_t tresult;
   1493 	bool logit = (result == ISC_R_SUCCESS);
   1494 
   1495 	result = flushandsync(f, result, temp);
   1496 	if (result != ISC_R_SUCCESS) {
   1497 		logit = false;
   1498 	}
   1499 
   1500 	tresult = isc_stdio_close(f);
   1501 	if (result == ISC_R_SUCCESS) {
   1502 		result = tresult;
   1503 	}
   1504 	if (result != ISC_R_SUCCESS && logit) {
   1505 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1506 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1507 			      "dumping master file: %s: fclose: %s", temp,
   1508 			      isc_result_totext(result));
   1509 		logit = false;
   1510 	}
   1511 	if (result == ISC_R_SUCCESS) {
   1512 		result = isc_file_rename(temp, file);
   1513 	} else {
   1514 		(void)isc_file_remove(temp);
   1515 	}
   1516 	if (result != ISC_R_SUCCESS && logit) {
   1517 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1518 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1519 			      "dumping master file: rename: %s: %s", file,
   1520 			      isc_result_totext(result));
   1521 	}
   1522 	return result;
   1523 }
   1524 
   1525 /*
   1526  * This will run in a libuv threadpool thread.
   1527  */
   1528 static void
   1529 master_dump_cb(void *data) {
   1530 	isc_result_t result = ISC_R_UNSET;
   1531 	dns_dumpctx_t *dctx = data;
   1532 	REQUIRE(DNS_DCTX_VALID(dctx));
   1533 
   1534 	if (atomic_load_acquire(&dctx->canceled)) {
   1535 		result = ISC_R_CANCELED;
   1536 	} else {
   1537 		result = dumptostream(dctx);
   1538 	}
   1539 
   1540 	if (dctx->file != NULL) {
   1541 		isc_result_t tresult = ISC_R_UNSET;
   1542 		tresult = closeandrename(dctx->f, result, dctx->tmpfile,
   1543 					 dctx->file);
   1544 		if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS) {
   1545 			result = tresult;
   1546 		}
   1547 	} else {
   1548 		result = flushandsync(dctx->f, result, NULL);
   1549 	}
   1550 
   1551 	dctx->result = result;
   1552 }
   1553 
   1554 /*
   1555  * This will run in a loop manager thread when the dump is complete.
   1556  */
   1557 static void
   1558 master_dump_done_cb(void *data) {
   1559 	dns_dumpctx_t *dctx = data;
   1560 
   1561 	(dctx->done)(dctx->done_arg, dctx->result);
   1562 	dns_dumpctx_detach(&dctx);
   1563 }
   1564 
   1565 static isc_result_t
   1566 dumpctx_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1567 	       const dns_master_style_t *style, FILE *f, dns_dumpctx_t **dctxp,
   1568 	       dns_masterformat_t format, dns_masterrawheader_t *header) {
   1569 	dns_dumpctx_t *dctx;
   1570 	isc_result_t result;
   1571 	unsigned int options;
   1572 
   1573 	dctx = isc_mem_get(mctx, sizeof(*dctx));
   1574 	*dctx = (dns_dumpctx_t){
   1575 		.f = f,
   1576 		.format = format,
   1577 	};
   1578 
   1579 	if (header == NULL) {
   1580 		dns_master_initrawheader(&dctx->header);
   1581 	} else {
   1582 		dctx->header = *header;
   1583 	}
   1584 
   1585 	switch (format) {
   1586 	case dns_masterformat_text:
   1587 		dctx->dumpsets = dump_rdatasets_text;
   1588 		break;
   1589 	case dns_masterformat_raw:
   1590 		dctx->dumpsets = dump_rdatasets_raw;
   1591 		break;
   1592 	default:
   1593 		UNREACHABLE();
   1594 	}
   1595 
   1596 	result = totext_ctx_init(style, NULL, &dctx->tctx);
   1597 	if (result != ISC_R_SUCCESS) {
   1598 		UNEXPECTED_ERROR("could not set master file style");
   1599 		goto cleanup;
   1600 	}
   1601 
   1602 	dctx->now = isc_stdtime_now();
   1603 	dns_db_attach(db, &dctx->db);
   1604 
   1605 	dctx->do_date = dns_db_iscache(dctx->db);
   1606 	if (dctx->do_date) {
   1607 		(void)dns_db_getservestalettl(dctx->db,
   1608 					      &dctx->tctx.serve_stale_ttl);
   1609 	}
   1610 
   1611 	if (dctx->format == dns_masterformat_text &&
   1612 	    (dctx->tctx.style.flags & DNS_STYLEFLAG_REL_OWNER) != 0)
   1613 	{
   1614 		options = DNS_DB_RELATIVENAMES;
   1615 	} else {
   1616 		options = 0;
   1617 	}
   1618 	result = dns_db_createiterator(dctx->db, options, &dctx->dbiter);
   1619 	if (result != ISC_R_SUCCESS) {
   1620 		goto cleanup;
   1621 	}
   1622 
   1623 	isc_mutex_init(&dctx->lock);
   1624 
   1625 	if (version != NULL) {
   1626 		dns_db_attachversion(dctx->db, version, &dctx->version);
   1627 	} else if (!dns_db_iscache(db)) {
   1628 		dns_db_currentversion(dctx->db, &dctx->version);
   1629 	}
   1630 	isc_mem_attach(mctx, &dctx->mctx);
   1631 
   1632 	isc_refcount_init(&dctx->references, 1);
   1633 	dctx->magic = DNS_DCTX_MAGIC;
   1634 	*dctxp = dctx;
   1635 	return ISC_R_SUCCESS;
   1636 
   1637 cleanup:
   1638 	if (dctx->dbiter != NULL) {
   1639 		dns_dbiterator_destroy(&dctx->dbiter);
   1640 	}
   1641 	if (dctx->db != NULL) {
   1642 		dns_db_detach(&dctx->db);
   1643 	}
   1644 	isc_mem_put(mctx, dctx, sizeof(*dctx));
   1645 	return result;
   1646 }
   1647 
   1648 static isc_result_t
   1649 writeheader(dns_dumpctx_t *dctx) {
   1650 	isc_result_t result = ISC_R_SUCCESS;
   1651 	isc_buffer_t buffer;
   1652 	char *bufmem;
   1653 	isc_region_t r;
   1654 	dns_masterrawheader_t rawheader;
   1655 	uint32_t rawversion, now32;
   1656 
   1657 	bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
   1658 
   1659 	isc_buffer_init(&buffer, bufmem, initial_buffer_length);
   1660 
   1661 	switch (dctx->format) {
   1662 	case dns_masterformat_text:
   1663 		/*
   1664 		 * If the database has cache semantics, output an
   1665 		 * RFC2540 $DATE directive so that the TTLs can be
   1666 		 * adjusted when it is reloaded.  For zones it is not
   1667 		 * really needed, and it would make the file
   1668 		 * incompatible with pre-RFC2540 software, so we omit
   1669 		 * it in the zone case.
   1670 		 */
   1671 		if (dctx->do_date) {
   1672 			fprintf(dctx->f, "; using a %u second stale ttl\n",
   1673 				dctx->tctx.serve_stale_ttl);
   1674 			result = dns_time32_totext(dctx->now, &buffer);
   1675 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1676 			isc_buffer_usedregion(&buffer, &r);
   1677 			fprintf(dctx->f, "$DATE %.*s\n", (int)r.length,
   1678 				(char *)r.base);
   1679 		}
   1680 		break;
   1681 	case dns_masterformat_raw:
   1682 		r.base = (unsigned char *)&rawheader;
   1683 		r.length = sizeof(rawheader);
   1684 		isc_buffer_region(&buffer, &r);
   1685 		now32 = dctx->now;
   1686 		rawversion = 1;
   1687 		if ((dctx->header.flags & DNS_MASTERRAW_COMPAT) != 0) {
   1688 			rawversion = 0;
   1689 		}
   1690 
   1691 		isc_buffer_putuint32(&buffer, dctx->format);
   1692 		isc_buffer_putuint32(&buffer, rawversion);
   1693 		isc_buffer_putuint32(&buffer, now32);
   1694 
   1695 		if (rawversion == 1) {
   1696 			isc_buffer_putuint32(&buffer, dctx->header.flags);
   1697 			isc_buffer_putuint32(&buffer,
   1698 					     dctx->header.sourceserial);
   1699 			isc_buffer_putuint32(&buffer, dctx->header.lastxfrin);
   1700 		}
   1701 
   1702 		INSIST(isc_buffer_usedlength(&buffer) <= sizeof(rawheader));
   1703 		result = isc_stdio_write(buffer.base, 1,
   1704 					 isc_buffer_usedlength(&buffer),
   1705 					 dctx->f, NULL);
   1706 		if (result != ISC_R_SUCCESS) {
   1707 			break;
   1708 		}
   1709 
   1710 		break;
   1711 	default:
   1712 		UNREACHABLE();
   1713 	}
   1714 
   1715 	isc_mem_put(dctx->mctx, buffer.base, buffer.length);
   1716 	return result;
   1717 }
   1718 
   1719 static isc_result_t
   1720 dumptostream(dns_dumpctx_t *dctx) {
   1721 	isc_result_t result = ISC_R_SUCCESS;
   1722 	isc_buffer_t buffer;
   1723 	char *bufmem;
   1724 	dns_name_t *name;
   1725 	dns_fixedname_t fixname;
   1726 	unsigned int options = DNS_DB_STALEOK;
   1727 
   1728 	if ((dctx->tctx.style.flags & DNS_STYLEFLAG_EXPIRED) != 0) {
   1729 		options |= DNS_DB_EXPIREDOK;
   1730 	}
   1731 
   1732 	bufmem = isc_mem_get(dctx->mctx, initial_buffer_length);
   1733 
   1734 	isc_buffer_init(&buffer, bufmem, initial_buffer_length);
   1735 
   1736 	name = dns_fixedname_initname(&fixname);
   1737 
   1738 	CHECK(writeheader(dctx));
   1739 
   1740 	result = dns_dbiterator_first(dctx->dbiter);
   1741 	if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) {
   1742 		goto cleanup;
   1743 	}
   1744 
   1745 	while (result == ISC_R_SUCCESS) {
   1746 		dns_rdatasetiter_t *rdsiter = NULL;
   1747 		dns_dbnode_t *node = NULL;
   1748 
   1749 		result = dns_dbiterator_current(dctx->dbiter, &node, name);
   1750 		if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
   1751 			break;
   1752 		}
   1753 		if (result == DNS_R_NEWORIGIN) {
   1754 			dns_name_t *origin =
   1755 				dns_fixedname_name(&dctx->tctx.origin_fixname);
   1756 			result = dns_dbiterator_origin(dctx->dbiter, origin);
   1757 			RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1758 			if ((dctx->tctx.style.flags & DNS_STYLEFLAG_REL_DATA) !=
   1759 			    0)
   1760 			{
   1761 				dctx->tctx.origin = origin;
   1762 			}
   1763 			dctx->tctx.neworigin = origin;
   1764 		}
   1765 
   1766 		result = dns_dbiterator_pause(dctx->dbiter);
   1767 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   1768 
   1769 		result = dns_db_allrdatasets(dctx->db, node, dctx->version,
   1770 					     options, dctx->now, &rdsiter);
   1771 		if (result != ISC_R_SUCCESS) {
   1772 			dns_db_detachnode(dctx->db, &node);
   1773 			goto cleanup;
   1774 		}
   1775 		result = (dctx->dumpsets)(dctx->mctx, name, rdsiter,
   1776 					  &dctx->tctx, &buffer, dctx->f);
   1777 		dns_rdatasetiter_destroy(&rdsiter);
   1778 		if (result != ISC_R_SUCCESS) {
   1779 			dns_db_detachnode(dctx->db, &node);
   1780 			goto cleanup;
   1781 		}
   1782 		dns_db_detachnode(dctx->db, &node);
   1783 		result = dns_dbiterator_next(dctx->dbiter);
   1784 	}
   1785 
   1786 	if (result == ISC_R_NOMORE) {
   1787 		result = ISC_R_SUCCESS;
   1788 	}
   1789 cleanup:
   1790 	RUNTIME_CHECK(dns_dbiterator_pause(dctx->dbiter) == ISC_R_SUCCESS);
   1791 	isc_mem_put(dctx->mctx, buffer.base, buffer.length);
   1792 	return result;
   1793 }
   1794 
   1795 isc_result_t
   1796 dns_master_dumptostreamasync(isc_mem_t *mctx, dns_db_t *db,
   1797 			     dns_dbversion_t *version,
   1798 			     const dns_master_style_t *style, FILE *f,
   1799 			     isc_loop_t *loop, dns_dumpdonefunc_t done,
   1800 			     void *done_arg, dns_dumpctx_t **dctxp) {
   1801 	dns_dumpctx_t *dctx = NULL;
   1802 	isc_result_t result;
   1803 
   1804 	REQUIRE(loop != NULL);
   1805 	REQUIRE(f != NULL);
   1806 	REQUIRE(done != NULL);
   1807 
   1808 	result = dumpctx_create(mctx, db, version, style, f, &dctx,
   1809 				dns_masterformat_text, NULL);
   1810 	if (result != ISC_R_SUCCESS) {
   1811 		return result;
   1812 	}
   1813 	dctx->done = done;
   1814 	dctx->done_arg = done_arg;
   1815 
   1816 	dns_dumpctx_attach(dctx, dctxp);
   1817 	isc_work_enqueue(loop, master_dump_cb, master_dump_done_cb, dctx);
   1818 
   1819 	return ISC_R_SUCCESS;
   1820 }
   1821 
   1822 isc_result_t
   1823 dns_master_dumptostream(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1824 			const dns_master_style_t *style,
   1825 			dns_masterformat_t format,
   1826 			dns_masterrawheader_t *header, FILE *f) {
   1827 	dns_dumpctx_t *dctx = NULL;
   1828 	isc_result_t result;
   1829 
   1830 	result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
   1831 				header);
   1832 	if (result != ISC_R_SUCCESS) {
   1833 		return result;
   1834 	}
   1835 
   1836 	result = dumptostream(dctx);
   1837 	INSIST(result != DNS_R_CONTINUE);
   1838 	dns_dumpctx_detach(&dctx);
   1839 
   1840 	result = flushandsync(f, result, NULL);
   1841 	return result;
   1842 }
   1843 
   1844 static isc_result_t
   1845 opentmp(isc_mem_t *mctx, const char *file, char **tempp, FILE **fp) {
   1846 	FILE *f = NULL;
   1847 	isc_result_t result;
   1848 	char *tempname = NULL;
   1849 	int tempnamelen;
   1850 
   1851 	tempnamelen = strlen(file) + 20;
   1852 	tempname = isc_mem_allocate(mctx, tempnamelen);
   1853 
   1854 	result = isc_file_mktemplate(file, tempname, tempnamelen);
   1855 	if (result != ISC_R_SUCCESS) {
   1856 		goto cleanup;
   1857 	}
   1858 
   1859 	result = isc_file_openunique(tempname, &f);
   1860 	if (result != ISC_R_SUCCESS) {
   1861 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1862 			      DNS_LOGMODULE_MASTERDUMP, ISC_LOG_ERROR,
   1863 			      "dumping master file: %s: open: %s", tempname,
   1864 			      isc_result_totext(result));
   1865 		goto cleanup;
   1866 	}
   1867 
   1868 #if defined(POSIX_FADV_DONTNEED)
   1869 	posix_fadvise(fileno(f), 0, 0, POSIX_FADV_DONTNEED);
   1870 #endif
   1871 
   1872 	*tempp = tempname;
   1873 	*fp = f;
   1874 	return ISC_R_SUCCESS;
   1875 
   1876 cleanup:
   1877 	isc_mem_free(mctx, tempname);
   1878 	return result;
   1879 }
   1880 
   1881 isc_result_t
   1882 dns_master_dumpasync(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1883 		     const dns_master_style_t *style, const char *filename,
   1884 		     isc_loop_t *loop, dns_dumpdonefunc_t done, void *done_arg,
   1885 		     dns_dumpctx_t **dctxp, dns_masterformat_t format,
   1886 		     dns_masterrawheader_t *header) {
   1887 	FILE *f = NULL;
   1888 	isc_result_t result;
   1889 	char *tempname = NULL;
   1890 	char *file = NULL;
   1891 	dns_dumpctx_t *dctx = NULL;
   1892 
   1893 	file = isc_mem_strdup(mctx, filename);
   1894 
   1895 	result = opentmp(mctx, filename, &tempname, &f);
   1896 	if (result != ISC_R_SUCCESS) {
   1897 		goto cleanup_file;
   1898 	}
   1899 
   1900 	result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
   1901 				header);
   1902 	if (result != ISC_R_SUCCESS) {
   1903 		goto cleanup_tempname;
   1904 	}
   1905 
   1906 	dctx->done = done;
   1907 	dctx->done_arg = done_arg;
   1908 	dctx->file = file;
   1909 	dctx->tmpfile = tempname;
   1910 
   1911 	dns_dumpctx_attach(dctx, dctxp);
   1912 	isc_work_enqueue(loop, master_dump_cb, master_dump_done_cb, dctx);
   1913 
   1914 	return ISC_R_SUCCESS;
   1915 
   1916 cleanup_tempname:
   1917 	(void)isc_stdio_close(f);
   1918 	(void)isc_file_remove(tempname);
   1919 	isc_mem_free(mctx, tempname);
   1920 
   1921 cleanup_file:
   1922 	isc_mem_free(mctx, file);
   1923 
   1924 	return result;
   1925 }
   1926 
   1927 isc_result_t
   1928 dns_master_dump(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *version,
   1929 		const dns_master_style_t *style, const char *filename,
   1930 		dns_masterformat_t format, dns_masterrawheader_t *header) {
   1931 	FILE *f = NULL;
   1932 	isc_result_t result;
   1933 	char *tempname;
   1934 	dns_dumpctx_t *dctx = NULL;
   1935 
   1936 	result = opentmp(mctx, filename, &tempname, &f);
   1937 	if (result != ISC_R_SUCCESS) {
   1938 		return result;
   1939 	}
   1940 
   1941 	result = dumpctx_create(mctx, db, version, style, f, &dctx, format,
   1942 				header);
   1943 	if (result != ISC_R_SUCCESS) {
   1944 		goto cleanup;
   1945 	}
   1946 
   1947 	result = dumptostream(dctx);
   1948 	INSIST(result != DNS_R_CONTINUE);
   1949 	dns_dumpctx_detach(&dctx);
   1950 
   1951 	result = closeandrename(f, result, tempname, filename);
   1952 
   1953 cleanup:
   1954 	isc_mem_free(mctx, tempname);
   1955 	return result;
   1956 }
   1957 
   1958 dns_masterstyle_flags_t
   1959 dns_master_styleflags(const dns_master_style_t *style) {
   1960 	REQUIRE(style != NULL);
   1961 	return style->flags;
   1962 }
   1963 
   1964 isc_result_t
   1965 dns_master_stylecreate(dns_master_style_t **stylep,
   1966 		       dns_masterstyle_flags_t flags, unsigned int ttl_column,
   1967 		       unsigned int class_column, unsigned int type_column,
   1968 		       unsigned int rdata_column, unsigned int line_length,
   1969 		       unsigned int tab_width, unsigned int split_width,
   1970 		       isc_mem_t *mctx) {
   1971 	dns_master_style_t *style;
   1972 
   1973 	REQUIRE(stylep != NULL && *stylep == NULL);
   1974 	style = isc_mem_get(mctx, sizeof(*style));
   1975 
   1976 	style->flags = flags;
   1977 	style->ttl_column = ttl_column;
   1978 	style->class_column = class_column;
   1979 	style->type_column = type_column;
   1980 	style->rdata_column = rdata_column;
   1981 	style->line_length = line_length;
   1982 	style->tab_width = tab_width;
   1983 	style->split_width = split_width;
   1984 	*stylep = style;
   1985 	return ISC_R_SUCCESS;
   1986 }
   1987 
   1988 void
   1989 dns_master_styledestroy(dns_master_style_t **stylep, isc_mem_t *mctx) {
   1990 	dns_master_style_t *style;
   1991 
   1992 	REQUIRE(stylep != NULL && *stylep != NULL);
   1993 	style = *stylep;
   1994 	*stylep = NULL;
   1995 	isc_mem_put(mctx, style, sizeof(*style));
   1996 }
   1997