/** * Checks whether appending `len' bytes of data to the header would fit * within the maximum header size requirement in case a continuation * is emitted, and using the configured separator. */ bool header_fmt_value_fits(const header_fmt_t *hf, size_t len) { size_t final_len; size_t maxlen, n; header_fmt_check(hf); if (hf->empty) return FALSE; maxlen = size_saturate_sub(hf->max_size, sizeof("\r\n")); /* * If it fits on the line, no continuation will have to be emitted. * Otherwise, we'll need the stripped version of the separator, * followed by "\r\n\t" (3 chars). */ final_len = size_saturate_add(str_len(hf->header), len); n = size_saturate_add(hf->current_len, size_saturate_add(len, hf->seplen)); if (n <= hf->maxlen) { final_len = size_saturate_add(final_len, hf->seplen); } else { final_len = size_saturate_add(final_len, hf->stripped_seplen); final_len = size_saturate_add(final_len, 3); } return final_len < maxlen; /* Could say "<=" perhaps, but let's be safe */ }
size_t concat_strings_v(char *dst, size_t size, const char *s, va_list ap) { char *p = dst; size_t ret = 0; g_assert(0 == size || NULL != dst); if (size > 0) { if (!s) *p = '\0'; while (NULL != s) { size_t len; len = g_strlcpy(p, s, size); ret = size_saturate_add(ret, len); s = va_arg(ap, const char *); size = size_saturate_sub(size, len); if (0 == size) break; p += len; } } while (NULL != s) { ret = size_saturate_add(ret, strlen(s)); s = va_arg(ap, const char *); } g_assert(ret < SIZE_MAX); return ret; }
/** * Drop message (eslist iterator). * * @return TRUE to force message to be removed from list. */ static bool udp_tx_desc_drop(void *data, void *udata) { struct udp_tx_desc *txd = data; udp_sched_t *us = udata; udp_sched_check(us); udp_tx_desc_check(txd); g_assert(1 == pmsg_refcnt(txd->mb)); us->buffered = size_saturate_sub(us->buffered, pmsg_size(txd->mb)); udp_tx_desc_flag_release(txd, us); return TRUE; }
/** * Send message (eslist iterator callback). * * @return TRUE if message was sent and freed up. */ static bool udp_tx_desc_send(void *data, void *udata) { struct udp_tx_desc *txd = data; udp_sched_t *us = udata; unsigned prio; udp_sched_check(us); udp_tx_desc_check(txd); if (us->used_all) return FALSE; /* * Avoid flushing consecutive queued messages to the same destination, * for regular (non-prioritary) messages. * * This serves two purposes: * * 1- It makes sure one single host does not capture all the available * outgoing bandwidth. * * 2- It somehow delays consecutive packets to a given host thereby reducing * flooding and hopefully avoiding saturation of its RX flow. */ prio = pmsg_prio(txd->mb); if (PMSG_P_DATA == prio && hset_contains(us->seen, txd->to)) { udp_sched_log(2, "%p: skipping mb=%p (%d bytes) to %s", us, txd->mb, pmsg_size(txd->mb), gnet_host_to_string(txd->to)); return FALSE; } if (udp_sched_mb_sendto(us, txd->mb, txd->to, txd->tx, txd->cb)) { if (PMSG_P_DATA == prio && pmsg_was_sent(txd->mb)) hset_insert(us->seen, atom_host_get(txd->to)); } else { return FALSE; /* Unsent, leave it in the queue */ } us->buffered = size_saturate_sub(us->buffered, pmsg_size(txd->mb)); udp_tx_desc_flag_release(txd, us); return TRUE; }
/** * Store big value in the .dat file, writing to the supplied block numbers. * * @param db the sdbm database * @param bvec start of block vector, containing block numbers * @param data start of data to write * @param len length of data to write * * @return -1 on error with errno set, 0 if OK. */ static int big_store(DBM *db, const void *bvec, const void *data, size_t len) { DBMBIG *dbg = db->big; int bcnt = bigbcnt(len); int n; const void *p; const char *q; size_t remain; g_return_val_if_fail(NULL == dbg->bitcheck, -1); if (-1 == dbg->fd && -1 == big_open(dbg)) return -1; /* * Look at the amount of consecutive block numbers we have to be able * to write into them via a single system call. */ n = bcnt; p = bvec; q = data; remain = len; while (n > 0) { size_t towrite = MIN(remain, BIG_BLKSIZE); guint32 bno = peek_be32(p); guint32 prev_bno = bno; p = const_ptr_add_offset(p, sizeof(guint32)); n--; remain = size_saturate_sub(remain, towrite); while (n > 0) { guint32 next_bno = peek_be32(p); size_t amount; if (next_bno <= prev_bno) /* Block numbers are sorted */ goto corrupted_page; if (next_bno - prev_bno != 1) break; /* Not consecutive */ prev_bno = next_bno; p = const_ptr_add_offset(p, sizeof(guint32)); amount = MIN(remain, BIG_BLKSIZE); towrite += amount; n--; remain = size_saturate_sub(remain, amount); } dbg->bigwrite++; if (-1 == compat_pwrite(dbg->fd, q, towrite, OFF_DAT(bno))) { g_warning("sdbm: \"%s\": " "could not write %lu bytes starting at data block #%u: %s", sdbm_name(db), (unsigned long) towrite, bno, g_strerror(errno)); ioerr(db, TRUE); return -1; } q += towrite; dbg->bigwrite_blk += bigblocks(towrite); g_assert(ptr_diff(q, data) <= len); } g_assert(ptr_diff(q, data) == len); return 0; corrupted_page: g_warning("sdbm: \"%s\": corrupted page: %d big data block%s not sorted", sdbm_name(db), bcnt, 1 == bcnt ? "" : "s"); ioerr(db, FALSE); errno = EFAULT; /* Data corrupted somehow (.pag file) */ return -1; }
/** * Fetch data block from the .dat file, reading from the supplied block numbers. * * @param db the sdbm database * @param bvec start of block vector, containing block numbers * @param len length of the data to be read * * @return -1 on error with errno set, 0 if OK. Read data is left in the * scratch buffer. */ static int big_fetch(DBM *db, const void *bvec, size_t len) { int bcnt = bigbcnt(len); DBMBIG *dbg = db->big; int n; const void *p; char *q; size_t remain; guint32 prev_bno; if (-1 == dbg->fd && -1 == big_open(dbg)) return -1; if (dbg->scratch_len < len) big_scratch_grow(dbg, len); /* * Read consecutive blocks in one single system call. */ n = bcnt; p = bvec; q = dbg->scratch; remain = len; while (n > 0) { size_t toread = MIN(remain, BIG_BLKSIZE); guint32 bno = peek_be32(p); prev_bno = bno; if (!big_block_is_allocated(db, prev_bno)) goto corrupted_database; p = const_ptr_add_offset(p, sizeof(guint32)); n--; remain = size_saturate_sub(remain, toread); while (n > 0) { guint32 next_bno = peek_be32(p); size_t amount; if (next_bno <= prev_bno) /* Block numbers are sorted */ goto corrupted_page; if (next_bno - prev_bno != 1) break; /* Not consecutive */ prev_bno = next_bno; if (!big_block_is_allocated(db, prev_bno)) goto corrupted_database; p = const_ptr_add_offset(p, sizeof(guint32)); amount = MIN(remain, BIG_BLKSIZE); toread += amount; n--; remain = size_saturate_sub(remain, amount); } dbg->bigread++; if (-1 == compat_pread(dbg->fd, q, toread, OFF_DAT(bno))) { g_warning("sdbm: \"%s\": " "could not read %lu bytes starting at data block #%u: %s", sdbm_name(db), (unsigned long) toread, bno, g_strerror(errno)); ioerr(db, FALSE); return -1; } q += toread; dbg->bigread_blk += bigblocks(toread); g_assert(UNSIGNED(q - dbg->scratch) <= dbg->scratch_len); } g_assert(UNSIGNED(q - dbg->scratch) == len); return 0; corrupted_database: g_warning("sdbm: \"%s\": cannot read unallocated data block #%u", sdbm_name(db), prev_bno); goto fault; corrupted_page: g_warning("sdbm: \"%s\": corrupted page: %d big data block%s not sorted", sdbm_name(db), bcnt, 1 == bcnt ? "" : "s"); /* FALL THROUGH */ fault: ioerr(db, FALSE); errno = EFAULT; /* Data corrupted somehow (.pag or .dat file) */ return -1; }