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