#include "jscan.h"
static struct jhash *JHashAry[JHASH_SIZE];
static void jnormalize(struct jstream *js);
static int jaddrecord_backtrack(struct jsession *ss, struct jdata *jd);
struct jstream *
jaddrecord(struct jsession *ss, struct jdata *jd)
{
struct journal_rawrecbeg *head;
struct jstream *js;
struct jhash *jh;
struct jhash **jhp;
js = malloc(sizeof(struct jstream));
bzero(js, sizeof(struct jstream));
js->js_jdata = jref(jd);
js->js_head = (void *)jd->jd_data;
js->js_session = ss;
head = js->js_head;
if ((head->streamid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
(JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
) {
jnormalize(js);
return (js);
}
retry:
jhp = &JHashAry[head->streamid & JHASH_MASK];
while ((jh = *jhp) != NULL) {
if (jh->jh_session == ss &&
((jh->jh_transid ^ head->streamid) & JREC_STREAMID_MASK) == 0
) {
break;
}
jhp = &jh->jh_hash;
}
if (jh == NULL) {
if (ss->ss_direction == JD_FORWARDS &&
(head->streamid & JREC_STREAMCTL_BEGIN) == 0
) {
if (verbose_opt > 1)
fprintf(stderr, "mid-transaction detected transid %016jx "
"streamid %04x\n",
(uintmax_t)jd->jd_transid,
head->streamid & JREC_STREAMID_MASK);
if (jaddrecord_backtrack(ss, jd) == 0) {
if (verbose_opt)
fprintf(stderr, "mid-transaction streamid %04x collection "
"succeeded\n",
head->streamid & JREC_STREAMID_MASK);
goto retry;
}
fprintf(stderr, "mid-transaction streamid %04x collection failed\n",
head->streamid & JREC_STREAMID_MASK);
jscan_dispose(js);
return(NULL);
} else if (ss->ss_direction == JD_BACKWARDS &&
(head->streamid & JREC_STREAMCTL_END) == 0
) {
if (verbose_opt > 1)
fprintf(stderr, "mid-transaction detected transid %016jx "
"streamid %04x\n",
(uintmax_t)jd->jd_transid,
head->streamid & JREC_STREAMID_MASK);
if (jaddrecord_backtrack(ss, jd) == 0) {
if (verbose_opt)
fprintf(stderr, "mid-transaction streamid %04x "
"collection succeeded\n",
head->streamid & JREC_STREAMID_MASK);
goto retry;
}
fprintf(stderr, "mid-transaction streamid %04x collection failed\n",
head->streamid & JREC_STREAMID_MASK);
jscan_dispose(js);
return(NULL);
}
}
if (jh == NULL) {
jh = malloc(sizeof(*jh));
bzero(jh, sizeof(*jh));
*jhp = jh;
jh->jh_first = js;
jh->jh_last = js;
jh->jh_transid = head->streamid;
jh->jh_session = ss;
return (NULL);
}
jh->jh_transid |= head->streamid & JREC_STREAMCTL_MASK;
if (ss->ss_direction == JD_FORWARDS) {
jh->jh_last->js_next = js;
jh->jh_last = js;
} else {
js->js_next = jh->jh_first;
jh->jh_first = js;
}
if ((jh->jh_transid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
(JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
) {
*jhp = jh->jh_hash;
js = jh->jh_first;
free(jh);
jnormalize(js);
} else {
js = NULL;
}
return (js);
}
static
void
jnormalize(struct jstream *js)
{
struct jstream *jscan;
off_t off;
js->js_normalized_off = 0;
js->js_normalized_base = (void *)js->js_head;
js->js_normalized_size = js->js_head->recsize - sizeof(struct journal_rawrecend);
js->js_normalized_total = js->js_normalized_size;
off = js->js_normalized_size;
for (jscan = js->js_next; jscan; jscan = jscan->js_next) {
jscan->js_normalized_off = off;
jscan->js_normalized_base = (char *)jscan->js_head +
sizeof(struct journal_rawrecbeg);
jscan->js_normalized_size = jscan->js_head->recsize -
sizeof(struct journal_rawrecbeg) -
sizeof(struct journal_rawrecend);
off += jscan->js_normalized_size;
js->js_normalized_total += jscan->js_normalized_size;
}
}
static
int
jaddrecord_backtrack(struct jsession *ss, struct jdata *jd)
{
struct jfile *jf = ss->ss_jfin;
struct jdata *scan;
struct jstream *js;
u_int16_t streamid;
u_int16_t scanid;
assert(ss->ss_direction == JD_FORWARDS || ss->ss_direction == JD_BACKWARDS);
if (jmodes & JMODEF_INPUT_PIPE)
return(-1);
streamid = ((struct journal_rawrecbeg *)jd->jd_data)->streamid & JREC_STREAMID_MASK;
if (ss->ss_direction == JD_FORWARDS) {
scan = jref(jd);
while ((scan = jread(jf, scan, JD_BACKWARDS)) != NULL) {
scanid = ((struct journal_rawrecbeg *)scan->jd_data)->streamid;
if ((scanid & JREC_STREAMID_MASK) != streamid)
continue;
if (scanid & JREC_STREAMCTL_END) {
jfree(jf, scan);
return(-1);
}
if (scanid & JREC_STREAMCTL_BEGIN)
break;
}
while (scan != NULL && scan->jd_transid < jd->jd_transid) {
scanid = ((struct journal_rawrecbeg *)scan->jd_data)->streamid;
if ((scanid & JREC_STREAMID_MASK) == streamid) {
js = jaddrecord(ss, scan);
assert(js == NULL);
}
scan = jread(jf, scan, JD_FORWARDS);
}
if (scan == NULL)
return(-1);
jfree(jf, scan);
} else {
scan = jref(jd);
while ((scan = jread(jf, scan, JD_FORWARDS)) != NULL) {
scanid = ((struct journal_rawrecbeg *)scan->jd_data)->streamid;
if ((scanid & JREC_STREAMID_MASK) != streamid)
continue;
if (scanid & JREC_STREAMCTL_BEGIN) {
jfree(jf, scan);
return(-1);
}
if (scanid & JREC_STREAMCTL_END)
break;
}
while (scan != NULL && scan->jd_transid > jd->jd_transid) {
scanid = ((struct journal_rawrecbeg *)scan->jd_data)->streamid;
if ((scanid & JREC_STREAMID_MASK) == streamid) {
js = jaddrecord(ss, scan);
assert(js == NULL);
}
scan = jread(jf, scan, JD_BACKWARDS);
}
if (scan == NULL)
return(-1);
jfree(jf, scan);
}
return(0);
}
void
jscan_dispose(struct jstream *js)
{
struct jstream *jnext;
if (js->js_alloc_buf) {
free(js->js_alloc_buf);
js->js_alloc_buf = NULL;
js->js_alloc_size = 0;
}
while (js) {
jnext = js->js_next;
jfree(js->js_session->ss_jfin, js->js_jdata);
js->js_jdata = NULL;
free(js);
js = jnext;
}
}
int
jsread(struct jstream *js, off_t off, void *buf, int bytes)
{
const void *ptr;
int n;
while (bytes) {
n = jsreadany(js, off, &ptr);
if (n == 0)
return (ENOENT);
if (n > bytes)
n = bytes;
bcopy(ptr, buf, n);
buf = (char *)buf + n;
off += n;
bytes -= n;
}
return(0);
}
int
jsreadp(struct jstream *js, off_t off, const void **bufp,
int bytes)
{
int error = 0;
int n;
n = jsreadany(js, off, bufp);
if (n < bytes) {
if (js->js_alloc_size < bytes) {
if (js->js_alloc_buf)
free(js->js_alloc_buf);
js->js_alloc_buf = malloc(bytes);
js->js_alloc_size = bytes;
if (js->js_alloc_buf == NULL)
fprintf(stderr, "attempt to allocate %d bytes failed\n", bytes);
assert(js->js_alloc_buf != NULL);
}
error = jsread(js, off, js->js_alloc_buf, bytes);
if (error) {
*bufp = NULL;
} else {
*bufp = js->js_alloc_buf;
}
}
return(error);
}
int
jsreadcallback(struct jstream *js, ssize_t (*func)(int, const void *, size_t),
int fd, off_t off, int bytes)
{
const void *bufp;
int res;
int n;
int r;
res = 0;
while (bytes && (n = jsreadany(js, off, &bufp)) > 0) {
if (n > bytes)
n = bytes;
r = func(fd, bufp, n);
if (r != n) {
if (res == 0)
res = -1;
}
res += n;
bytes -= n;
off += n;
}
return(res);
}
int
jsreadany(struct jstream *js, off_t off, const void **bufp)
{
struct jstream *scan;
int n;
if ((scan = js->js_cache) == NULL || scan->js_normalized_off > off)
scan = js;
while (scan && scan->js_normalized_off <= off) {
js->js_cache = scan;
if (scan->js_normalized_off + scan->js_normalized_size > off) {
n = (int)(off - scan->js_normalized_off);
*bufp = scan->js_normalized_base + n;
return(scan->js_normalized_size - n);
}
scan = scan->js_next;
}
return(0);
}