static ya_result journal_ix_reopen(journal *jh) { journal_ix *jix = (journal_ix*)jh; ya_result ret = SUCCESS; journal_ix_writelock(jix); if(jix->fd < 0) { if(ISOK(ret = open_ex(jix->journal_name, O_RDWR))) { jix->fd = ret; } else { ret = ERRNO_ERROR; if(ret == MAKE_ERRNO_ERROR(ENOENT)) { ret = ZDB_ERROR_ICMTL_NOTFOUND; } } } journal_ix_writeunlock(jix); return ret; }
static ya_result journal_ix_get_ixfr_stream_at_serial(journal *jh, u32 serial_from, input_stream *out_input_stream, dns_resource_record *last_soa_rr) { journal_ix *jix = (journal_ix*)jh; ya_result return_value = SUCCESS; journal_ix_readlock(jix); /* * check that serial_from in in the journal range * set the file descriptor to the position * create a stream that'll stop at the current end of the stream */ if(serial_lt(serial_from, jix->first_serial) || serial_ge(serial_from, jix->last_serial) || ((jix->first_serial == 0) && (jix->last_serial == 0))) { /* out of known range */ journal_ix_readunlock(jix); if(serial_from == jix->last_serial) { return SUCCESS; } else { return ZDB_JOURNAL_SERIAL_OUT_OF_KNOWN_RANGE; } } /* * On success, dup() returns a new file descriptor that has the following in common with the original: * * _ Same open file (or pipe) * _ Same file pointer (both file descriptors share one file pointer) <= THIS is a problem * _ Same access mode (read, write, or read/write) * * So this is wrong: * * cloned_fd = dup(jix->fd); */ int cloned_fd; while((cloned_fd = open_ex(jix->journal_name, O_RDONLY)) < 0) { int err = errno; if(err == EINTR) { continue; } return_value = MAKE_ERRNO_ERROR(err); #ifdef DEBUG log_debug("journal: ix: unable to clone the file descriptor: %r", return_value); #endif journal_ix_readunlock(jix); return return_value; } /* * given that I use a clone of the fd and * given that only appends are done in the file and * given that the limit of the file has already been processed (should be at this point) * * THEN * * there is no point keeping the lock for reading (on unix systems) */ struct stat journal_stat; s64 last_page_offset = jix->last_page_offset; if(fstat(cloned_fd, &journal_stat) < 0) { return_value = ERRNO_ERROR; log_err("journal: ix: unable to get journal file status", return_value); close_ex(cloned_fd); return return_value; } s64 file_size = journal_stat.st_size; #if DEBUG_JOURNAL log_debug("journal: ix: the last page starts at position %lld", last_page_offset); #endif journal_ix_readunlock(jix); jix = NULL; input_stream fis; fd_input_stream_attach(&fis, cloned_fd); if(last_soa_rr != NULL) { /* seek and store the last SOA print*/ last_soa_rr->tctr.qtype = 0; // clear type if(lseek(cloned_fd, last_page_offset, SEEK_SET) >= 0) { /* deleted SOA */ if((return_value = dns_resource_record_read(last_soa_rr, &fis)) > 0 ) // Not FAIL nor EOF { if(last_soa_rr->tctr.qtype == TYPE_SOA) { /* DEL records */ last_soa_rr->tctr.qtype = 0; // clear type /* scan until added SOA found */ while((return_value = dns_resource_record_read(last_soa_rr, &fis)) > 0 ) // Not FAIL nor EOF { if(last_soa_rr->tctr.qtype == TYPE_SOA) { break; } } } } // if the SOA has not been found, it's an error (EOF has been reached is covered by this) if(ISOK(return_value)) { if(last_soa_rr->tctr.qtype != TYPE_SOA) { return_value = ZDB_JOURNAL_SOA_RECORD_EXPECTED; } } } else { return_value = ERRNO_ERROR; } if(FAIL(return_value)) { input_stream_close(&fis); return return_value; } } /* * this format has no indexing so we scan for a page that STARTS with a DELETE of the SOA with serial = serial_from */ if(lseek(cloned_fd, 0, SEEK_SET) != 0) /* the resulting offset MUST be zero */ { return_value = ERRNO_ERROR; if(ISOK(return_value)) { return_value = ERROR; } input_stream_close(&fis); return return_value; } input_stream bis; dns_resource_record rr; dns_resource_record_init(&rr); buffer_input_stream_init(&fis, &bis, 512); s64 offset = 0; /* skip until the right serial is found */ u32 soa_count = 0; #ifdef DEBUG_JOURNAL u32 rr_count = 0; #endif for(;;) { if( (return_value = dns_resource_record_read(&rr, &bis)) <= 0 ) // FAIL or nothing to { return_value = ZDB_JOURNAL_ERROR_READING_JOURNAL; /* is the journal file broken ? */ break; } #ifdef DEBUG_JOURNAL rr_count++; #endif u32 record_size = return_value; if(rr.tctr.qtype == TYPE_SOA) { // ((0+1)&1) != 0 => Y N Y N if((++soa_count & 1) != 0) // 1 2 3 4 { u8 *p = rr.rdata; if(FAIL(return_value = dnsname_len(p))) { break; } p += return_value; if(FAIL(return_value = dnsname_len(p))) { break; } p += return_value; u32 serial = ntohl(GET_U32_AT(*p)); if(serial_ge(serial, serial_from)) { if(serial == serial_from) { /* setup the serial to be from 'offset' up to the current length of the stream */ return_value = SUCCESS; } else { /* the serial does not exist in the range */ return_value = ZDB_JOURNAL_SERIAL_OUT_OF_KNOWN_RANGE; } break; } } } offset += record_size; } #if DEBUG_JOURNAL log_debug("journal: ix: serial %08x (%d) is at offset %lld. %d records parsed", serial_from, serial_from, offset, rr_count); #endif dns_resource_record_clear(&rr); /* * detach the file descriptor from the file stream in the buffer stream * I do it like this because the streams are not needed anymore but the * file descriptor still is (if no error occurred) */ fd_input_stream_detach(buffer_input_stream_get_filtered(&bis)); input_stream_close(&bis); if(ISOK(return_value)) { // offset is the start of the page we are looking for if(lseek(cloned_fd, offset, SEEK_SET) >= 0) { fd_input_stream_attach(&fis, cloned_fd); limited_input_stream_init(&fis, out_input_stream, file_size - offset); } else { return_value = ERRNO_ERROR; close_ex(cloned_fd); } } else { close_ex(cloned_fd); } return return_value; }
static void* scheduler_queue_zone_write_thread(void* data_) { char fullname_tmp[MAX_PATH]; zone_write_param *zwp = (zone_write_param*)data_; zdb_zone *zone = zwp->zone; u32 serial; log_info("zone write text: writing %{dnsname} zone file", zone->origin); if(ZDB_ZONE_INVALID(zone)) { log_err("zone write text: zone %{dnsname} marked as invalid", zone->origin); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); return NULL; } if(FAIL(zdb_zone_getserial(zone, &serial))) { log_err("zone write text: no SOA in %{dnsname}", zone->origin); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); return NULL; } if(FAIL(snformat(fullname_tmp, sizeof (fullname_tmp), "%s.%d.tmp", zwp->file_path, serial))) { log_err("zone write text: path '%s.%d.tmp' is too big", zwp->file_path, serial); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /* WARNING: From this point forward, 'zone' cannot be used anymore */ return NULL; } /** * @todo check there is not already a zone file writer working here ... * */ /* * delete the temp file if it exists already */ if(unlink(fullname_tmp) < 0) { int err = errno; if(err != ENOENT) { log_err("zone write text: cannot cleanup '%s': %r", MAKE_ERRNO_ERROR(err)); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /* WARNING: From this point forward, 'zone' cannot be used anymore */ return NULL; } } log_info("zone write text: writing '%s'", fullname_tmp); zdb_zone_lock(zone, ZDB_ZONE_MUTEX_SIMPLEREADER); zdb_zone_write_text_file(zone, fullname_tmp, FALSE); zdb_zone_unlock(zone, ZDB_ZONE_MUTEX_SIMPLEREADER); log_info("zone write text: renaming '%s' to '%s'", fullname_tmp, zwp->file_path); if(rename(fullname_tmp, zwp->file_path) < 0) { log_err("zone write text: unable to rename tmp zone file into '%s': %r", zwp->file_path, ERRNO_ERROR); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /** @note Calling this so the scheduler gets a SCHEDULER_TASK_FINISHED is mandatory. */ return NULL; } log_info("zone write text: %{dnsname} zone file written", zone->origin); scheduler_schedule_task(scheduler_queue_zone_write_callback, zwp); /* WARNING: From this point forward, 'zwp' cannot be used anymore */ return NULL; }
static ya_result config_main_verify_and_update_directory(const char *base_path, char **dirp) { char fullpath[PATH_MAX]; char tempfile[PATH_MAX]; if(dirp == NULL) { return UNEXPECTED_NULL_ARGUMENT_ERROR; } char *dir = *dirp; if(dir == NULL) { return UNEXPECTED_NULL_ARGUMENT_ERROR; } ya_result fullpath_len = snprintf(fullpath, sizeof(fullpath),"/%s/%s", base_path, dir); if(FAIL(fullpath_len)) { return fullpath_len; } bool dirsep = TRUE; int j = 1; for(int i = 1; i <= fullpath_len; i++) { char c = fullpath[i]; if(c == '/') { if(!dirsep) { fullpath[j++] = c; } dirsep = TRUE; } else { fullpath[j++] = fullpath[i]; dirsep = FALSE; } } struct stat ds; if(stat(fullpath, &ds) < 0) { int err = errno; ttylog_err("error: '%s': %s", fullpath, strerror(err)); return MAKE_ERRNO_ERROR(err); } if((ds.st_mode & S_IFMT) != S_IFDIR) { ttylog_err("error: '%s' is not a directory", dir); return INVALID_PATH; } snformat(tempfile, sizeof(tempfile), "%s/ydf.XXXXXX", fullpath); int tempfd; if((tempfd = mkstemp(tempfile)) < 0) { int return_code = ERRNO_ERROR; ttylog_err("error: '%s' is not writable: %r", fullpath, return_code); return return_code; } unlink(tempfile); close_ex(tempfd); free(dir); *dirp = strdup(fullpath); chroot_manage_path(dirp, fullpath, FALSE); return SUCCESS; }
ya_result tcp_input_output_stream_connect_sockaddr(const struct sockaddr *sa, input_stream *istream_, output_stream *ostream_, struct sockaddr *bind_from, u8 to_sec) { int fd; while((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { int err = errno; if(err != EINTR) { return MAKE_ERRNO_ERROR(err); } } /* * Bind the socket if required. */ if(bind_from != NULL) { while((bind(fd, bind_from, sizeof(socketaddress))) < 0) { int err = errno; if(err != EINTR) { close_ex(fd); /* could clear errno */ return MAKE_ERRNO_ERROR(err); } } } int ssec, susec, rsec, rusec; tcp_get_sendtimeout(fd, &ssec, &susec); tcp_get_recvtimeout(fd, &rsec, &rusec); tcp_set_sendtimeout(fd, to_sec, 0); tcp_set_recvtimeout(fd, to_sec, 0); tcp_set_nodelay(fd, tcp_nodelay); tcp_set_cork(fd, tcp_cork); #if HAS_SOCKADDR_SA_LEN while(connect(fd, sa, sa->sa_len) < 0) #else while(connect(fd, sa, sizeof(socketaddress)) < 0) #endif { int err = errno; if(err != EINTR) { close_ex(fd); // Linux quirk if(err == EINPROGRESS) { err = ETIMEDOUT; } return MAKE_ERRNO_ERROR(err); } } /* can only fail if fd < 0, which is never the case here */ fd_input_stream_attach(istream_, fd); fd_output_stream_attach_noclose(ostream_, fd); tcp_set_sendtimeout(fd, ssec, susec); tcp_set_recvtimeout(fd, rsec, rusec); return fd; }