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