📄 journal.c
字号:
"%s: close: %s", filename, isc_result_totext(result)); (void)isc_file_remove(filename); return (ISC_R_UNEXPECTED); } return (ISC_R_SUCCESS);}static isc_result_tjournal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, isc_boolean_t create, dns_journal_t **journalp) { FILE *fp = NULL; isc_result_t result; journal_rawheader_t rawheader; dns_journal_t *j; INSIST(journalp != NULL && *journalp == NULL); j = isc_mem_get(mctx, sizeof(*j)); if (j == NULL) return (ISC_R_NOMEMORY); j->mctx = mctx; j->state = JOURNAL_STATE_INVALID; j->fp = NULL; j->filename = filename; j->index = NULL; j->rawindex = NULL; result = isc_stdio_open(j->filename, write ? "rb+" : "rb", &fp); if (result == ISC_R_FILENOTFOUND) { if (create) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_INFO, "journal file %s does not exist, " "creating it", j->filename); CHECK(journal_file_create(mctx, filename)); /* * Retry. */ result = isc_stdio_open(j->filename, "rb+", &fp); } else { FAIL(ISC_R_NOTFOUND); } } if (result != ISC_R_SUCCESS) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: open: %s", j->filename, isc_result_totext(result)); FAIL(ISC_R_UNEXPECTED); } j->fp = fp; /* * Set magic early so that seek/read can succeed. */ j->magic = DNS_JOURNAL_MAGIC; CHECK(journal_seek(j, 0)); CHECK(journal_read(j, &rawheader, sizeof(rawheader))); if (memcmp(rawheader.h.format, initial_journal_header.format, sizeof(initial_journal_header.format)) != 0) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: journal format not recognized", j->filename); FAIL(ISC_R_UNEXPECTED); } journal_header_decode(&rawheader, &j->header); /* * If there is an index, read the raw index into a dynamically * allocated buffer and then convert it into a cooked index. */ if (j->header.index_size != 0) { unsigned int i; unsigned int rawbytes; unsigned char *p; rawbytes = j->header.index_size * sizeof(journal_rawpos_t); j->rawindex = isc_mem_get(mctx, rawbytes); if (j->rawindex == NULL) FAIL(ISC_R_NOMEMORY); CHECK(journal_read(j, j->rawindex, rawbytes)); j->index = isc_mem_get(mctx, j->header.index_size * sizeof(journal_pos_t)); if (j->index == NULL) FAIL(ISC_R_NOMEMORY); p = j->rawindex; for (i = 0; i < j->header.index_size; i++) { j->index[i].serial = decode_uint32(p); p += 4; j->index[i].offset = decode_uint32(p); p += 4; } INSIST(p == j->rawindex + rawbytes); } j->offset = -1; /* Invalid, must seek explicitly. */ /* * Initialize the iterator. */ dns_name_init(&j->it.name, NULL); dns_rdata_init(&j->it.rdata); /* * Set up empty initial buffers for uncheched and checked * wire format RR data. They will be reallocated * later. */ isc_buffer_init(&j->it.source, NULL, 0); isc_buffer_init(&j->it.target, NULL, 0); dns_decompress_init(&j->it.dctx, -1, DNS_DECOMPRESS_NONE); j->state = write ? JOURNAL_STATE_WRITE : JOURNAL_STATE_READ; *journalp = j; return (ISC_R_SUCCESS); failure: j->magic = 0; if (j->index != NULL) { isc_mem_put(j->mctx, j->index, j->header.index_size * sizeof(journal_rawpos_t)); j->index = NULL; } if (j->fp != NULL) (void)isc_stdio_close(j->fp); isc_mem_put(j->mctx, j, sizeof(*j)); return (result);}isc_result_tdns_journal_open(isc_mem_t *mctx, const char *filename, isc_boolean_t write, dns_journal_t **journalp) { return (journal_open(mctx, filename, write, write, journalp));}/* * A comparison function defining the sorting order for * entries in the IXFR-style journal file. * * The IXFR format requires that deletions are sorted before * additions, and within either one, SOA records are sorted * before others. * * Also sort the non-SOA records by type as a courtesy to the * server receiving the IXFR - it may help reduce the amount of * rdataset merging it has to do. */static intixfr_order(const void *av, const void *bv) { dns_difftuple_t const * const *ap = av; dns_difftuple_t const * const *bp = bv; dns_difftuple_t const *a = *ap; dns_difftuple_t const *b = *bp; int r; r = (b->op == DNS_DIFFOP_DEL) - (a->op == DNS_DIFFOP_DEL); if (r != 0) return (r); r = (b->rdata.type == dns_rdatatype_soa) - (a->rdata.type == dns_rdatatype_soa); if (r != 0) return (r); r = (a->rdata.type - b->rdata.type); return (r);}/* * Advance '*pos' to the next journal transaction. * * Requires: * *pos refers to a valid journal transaction. * * Ensures: * When ISC_R_SUCCESS is returned, * *pos refers to the next journal transaction. * * Returns one of: * * ISC_R_SUCCESS * ISC_R_NOMORE *pos pointed at the last transaction * Other results due to file errors are possible. */static isc_result_tjournal_next(dns_journal_t *j, journal_pos_t *pos) { isc_result_t result; journal_xhdr_t xhdr; REQUIRE(DNS_JOURNAL_VALID(j)); result = journal_seek(j, pos->offset); if (result != ISC_R_SUCCESS) return (result); if (pos->serial == j->header.end.serial) return (ISC_R_NOMORE); /* * Read the header of the current transaction. * This will return ISC_R_NOMORE if we are at EOF. */ result = journal_read_xhdr(j, &xhdr); if (result != ISC_R_SUCCESS) return (result); /* * Check serial number consistency. */ if (xhdr.serial0 != pos->serial) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: journal file corrupt: " "expected serial %u, got %u", j->filename, pos->serial, xhdr.serial0); return (ISC_R_UNEXPECTED); } /* * Check for offset wraparound. */ if ((isc_offset_t)(pos->offset + sizeof(journal_rawxhdr_t) + xhdr.size) < pos->offset) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "%s: offset too large", j->filename); return (ISC_R_UNEXPECTED); } pos->offset += sizeof(journal_rawxhdr_t) + xhdr.size; pos->serial = xhdr.serial1; return (ISC_R_SUCCESS);}/* * If the index of the journal 'j' contains an entry "better" * than '*best_guess', replace '*best_guess' with it. * * "Better" means having a serial number closer to 'serial' * but not greater than 'serial'. */static voidindex_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *best_guess) { unsigned int i; if (j->index == NULL) return; for (i = 0; i < j->header.index_size; i++) { if (POS_VALID(j->index[i]) && DNS_SERIAL_GE(serial, j->index[i].serial) && DNS_SERIAL_GT(j->index[i].serial, best_guess->serial)) *best_guess = j->index[i]; }}/* * Add a new index entry. If there is no room, make room by removing * the odd-numbered entries and compacting the others into the first * half of the index. This decimates old index entries exponentially * over time, so that the index always contains a much larger fraction * of recent serial numbers than of old ones. This is deliberate - * most index searches are for outgoing IXFR, and IXFR tends to request * recent versions more often than old ones. */static voidindex_add(dns_journal_t *j, journal_pos_t *pos) { unsigned int i; if (j->index == NULL) return; /* * Search for a vacant position. */ for (i = 0; i < j->header.index_size; i++) { if (! POS_VALID(j->index[i])) break; } if (i == j->header.index_size) { unsigned int k = 0; /* * Found no vacant position. Make some room. */ for (i = 0; i < j->header.index_size; i += 2) { j->index[k++] = j->index[i]; } i = k; /* 'i' identifies the first vacant position. */ while (k < j->header.index_size) { POS_INVALIDATE(j->index[k]); k++; } } INSIST(i < j->header.index_size); INSIST(! POS_VALID(j->index[i])); /* * Store the new index entry. */ j->index[i] = *pos;}/* * Invalidate any existing index entries that could become * ambiguous when a new transaction with number 'serial' is added. */static voidindex_invalidate(dns_journal_t *j, isc_uint32_t serial) { unsigned int i; if (j->index == NULL) return; for (i = 0; i < j->header.index_size; i++) { if (! DNS_SERIAL_GT(serial, j->index[i].serial)) POS_INVALIDATE(j->index[i]); }}/* * Try to find a transaction with initial serial number 'serial' * in the journal 'j'. * * If found, store its position at '*pos' and return ISC_R_SUCCESS. * * If 'serial' is current (= the ending serial number of the * last transaction in the journal), set '*pos' to * the position immediately following the last transaction and * return ISC_R_SUCCESS. * * If 'serial' is within the range of addressable serial numbers * covered by the journal but that particular serial number is missing * (from the journal, not just from the index), return ISC_R_NOTFOUND. * * If 'serial' is outside the range of addressable serial numbers * covered by the journal, return ISC_R_RANGE. * */static isc_result_tjournal_find(dns_journal_t *j, isc_uint32_t serial, journal_pos_t *pos) { isc_result_t result; journal_pos_t current_pos; REQUIRE(DNS_JOURNAL_VALID(j)); if (DNS_SERIAL_GT(j->header.begin.serial, serial)) return (ISC_R_RANGE); if (DNS_SERIAL_GT(serial, j->header.end.serial)) return (ISC_R_RANGE); if (serial == j->header.end.serial) { *pos = j->header.end; return (ISC_R_SUCCESS); } current_pos = j->header.begin; index_find(j, serial, ¤t_pos); while (current_pos.serial != serial) { if (DNS_SERIAL_GT(current_pos.serial, serial)) return (ISC_R_NOTFOUND); result = journal_next(j, ¤t_pos); if (result != ISC_R_SUCCESS) return (result); } *pos = current_pos; return (ISC_R_SUCCESS);}isc_result_tdns_journal_begin_transaction(dns_journal_t *j) { isc_uint32_t offset; isc_result_t result; journal_rawxhdr_t hdr; REQUIRE(DNS_JOURNAL_VALID(j)); REQUIRE(j->state == JOURNAL_STATE_WRITE); /* * Find the file offset where the new transaction should * be written, and seek there. */ if (JOURNAL_EMPTY(&j->header)) { offset = sizeof(journal_rawheader_t) + j->header.index_size * sizeof(journal_rawpos_t); } else { offset = j->header.end.offset; } j->x.pos[0].offset = offset; j->x.pos[1].offset = offset; /* Initial value, will be incremented. */ j->x.n_soa = 0; CHECK(journal_seek(j, offset)); /* * Write a dummy transaction header of all zeroes to reserve * space. It will be filled in when the transaction is * finished. */ memset(&hdr, 0, sizeof(hdr)); CHECK(journal_write(j, &hdr, sizeof(hdr))); j->x.pos[1].offset = j->offset; j->state = JOURNAL_STATE_TRANSACTION; result = ISC_R_SUCCESS; failure: return (result);}isc_result_tdns_journal_writediff(dns_journal_t *j, dns_diff_t *diff) { dns_difftuple_t *t; isc_buffer_t buffer; void *mem = NULL; unsigned int size; isc_result_t result; isc_region_t used; REQUIRE(DNS_DIFF_VALID(diff)); REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); isc_log_write(JOURNAL_DEBUG_LOGARGS(3), "writing to journal"); (void)dns_diff_print(diff, NULL); /* * Pass 1: determine the buffer size needed, and * keep track of SOA serial numbers. */ size = 0; for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; t = ISC_LIST_NEXT(t, link)) { if (t->rdata.type == dns_rdatatype_soa) { if (j->x.n_soa < 2) j->x.pos[j->x.n_soa].serial = dns_soa_getserial(&t->rdata); j->x.n_soa++; } size += sizeof(journal_rawrrhdr_t); size += t->name.length; /* XXX should have access macro? */ size += 10; size += t->rdata.length; } mem = isc_mem_get(j->mctx, size); if (mem == NULL) return (ISC_R_NOMEMORY); isc_buffer_init(&buffer, mem, size); /* * Pass 2. Write RRs to buffer. */ for (t = ISC_LIST_HEAD(diff->tuples); t != NULL; t = ISC_LIST_NEXT(t, link)) { /* * Write the RR header. */ isc_buffer_putuint32(&buffer, t->name.length + 10 + t->rdata.length); /* * Write the owner name, RR header, and RR data. */ isc_buffer_putmem(&buffer, t->name.ndata, t->name.length); isc_buffer_putuint16(&buffer, t->rdata.type); isc_buffer_putuint16(&buffer, t->rdata.rdclass); isc_buffer_putuint32(&buffer, t->ttl); INSIST(t->rdata.length < 65536); isc_buffer_putuint16(&buffer, (isc_uint16_t)t->rdata.length); INSIST(isc_buffer_availablelength(&buffer) >= t->rdata.length); isc_buffer_putmem(&buffer, t->rdata.data, t->rdata.length); } isc_buffer_usedregion(&buffer, &used); INSIST(used.length == size); j->x.pos[1].offset += used.length; /* * Write the buffer contents to the journal file. */ CHECK(journal_write(j, used.base, used.length)); result = ISC_R_SUCCESS; failure: if (mem != NULL) isc_mem_put(j->mctx, mem, size); return (result);}isc_result_tdns_journal_commit(dns_journal_t *j) { isc_result_t result; journal_rawheader_t rawheader; REQUIRE(DNS_JOURNAL_VALID(j)); REQUIRE(j->state == JOURNAL_STATE_TRANSACTION); /* * Perform some basic consistency checks. */ if (j->x.n_soa != 2) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "malformed transaction: %d SOAs", j->x.n_soa); return (ISC_R_UNEXPECTED); } if (! (DNS_SERIAL_GT(j->x.pos[1].serial, j->x.pos[0].serial) || (bind8_compat && j->x.pos[1].serial == j->x.pos[0].serial))) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "malformed transaction: serial number " "would decrease"); return (ISC_R_UNEXPECTED); } if (! JOURNAL_EMPTY(&j->header)) { if (j->x.pos[0].serial != j->header.end.serial) { isc_log_write(JOURNAL_COMMON_LOGARGS, ISC_LOG_ERROR, "malformed transaction: " "%s last serial %u != " "transaction first serial %u", j->filename, j->header.end.serial, j->x.pos[0].serial); return (ISC_R_UNEXPECTED); } } /* * Some old journal entries may become non-addressable * when we increment the current serial number. Purge them
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -