1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2008 Poul-Henning Kamp 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <stdio.h> 32 #include <unistd.h> 33 #include <assert.h> 34 #include <err.h> 35 #include <time.h> 36 #include <string.h> 37 #include <stdlib.h> 38 #include <zlib.h> 39 #include <sys/endian.h> 40 41 #include "fifolog.h" 42 #include "libfifolog.h" 43 #include "libfifolog_int.h" 44 #include "miniobj.h" 45 46 /*--------------------------------------------------------------------*/ 47 48 struct fifolog_reader { 49 unsigned magic; 50 #define FIFOLOG_READER_MAGIC 0x1036d139 51 struct fifolog_file *ff; 52 unsigned olen; 53 unsigned char *obuf; 54 time_t now; 55 }; 56 57 struct fifolog_reader * 58 fifolog_reader_open(const char *fname) 59 { 60 const char *retval; 61 struct fifolog_reader *fr; 62 int i; 63 64 fr = calloc(1, sizeof(*fr)); 65 if (fr == NULL) 66 err(1, "Cannot malloc"); 67 68 retval = fifolog_int_open(&fr->ff, fname, 0); 69 if (retval != NULL) 70 err(1, "%s", retval); 71 72 fr->obuf = calloc(16, fr->ff->recsize); 73 if (fr->obuf == NULL) 74 err(1, "Cannot malloc"); 75 fr->olen = fr->ff->recsize * 16; 76 77 i = inflateInit(fr->ff->zs); 78 assert(i == Z_OK); 79 80 fr->magic = FIFOLOG_READER_MAGIC; 81 return (fr); 82 } 83 84 /* 85 * Find the next SYNC block 86 * 87 * Return: 88 * 0 - empty fifolog 89 * 1 - found sync block 90 * 2 - would have wrapped around 91 * 3 - End of written log. 92 */ 93 94 static int 95 fifolog_reader_findsync(const struct fifolog_file *ff, off_t *o) 96 { 97 int e; 98 unsigned seq, seqs; 99 100 assert(*o < ff->logsize); 101 e = fifolog_int_read(ff, *o); 102 if (e) 103 err(1, "Read error (%d) while looking for SYNC", e); 104 seq = be32dec(ff->recbuf); 105 if (*o == 0 && seq == 0) 106 return (0); 107 108 if (ff->recbuf[4] & FIFOLOG_FLG_SYNC) 109 return (1); /* That was easy... */ 110 while(1) { 111 assert(*o < ff->logsize); 112 (*o)++; 113 seq++; 114 if (*o == ff->logsize) 115 return (2); /* wraparound */ 116 e = fifolog_int_read(ff, *o); 117 if (e) 118 err(1, "Read error (%d) while looking for SYNC", e); 119 seqs = be32dec(ff->recbuf); 120 if (seqs != seq) 121 return (3); /* End of log */ 122 if (ff->recbuf[4] & FIFOLOG_FLG_SYNC) 123 return (1); /* Bingo! */ 124 } 125 } 126 127 /* 128 * Seek out a given timestamp 129 */ 130 131 off_t 132 fifolog_reader_seek(const struct fifolog_reader *fr, time_t t0) 133 { 134 off_t o, s, st; 135 time_t t, tt; 136 unsigned seq, seqs; 137 const char *retval; 138 int e; 139 140 CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); 141 142 /* 143 * First, find the first SYNC block 144 */ 145 o = 0; 146 e = fifolog_reader_findsync(fr->ff, &o); 147 if (e == 0) 148 return (0); /* empty fifolog */ 149 assert(e == 1); 150 151 assert(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC); 152 seq = be32dec(fr->ff->recbuf); 153 t = be32dec(fr->ff->recbuf + 5); 154 155 if (t > t0) { 156 /* Check if there is a second older part we can use */ 157 retval = fifolog_int_findend(fr->ff, &s); 158 if (retval != NULL) 159 err(1, "%s", retval); 160 s++; 161 e = fifolog_reader_findsync(fr->ff, &s); 162 if (e == 0) 163 return (0); /* empty fifolog */ 164 if (e == 1) { 165 o = s; 166 seq = be32dec(fr->ff->recbuf); 167 t = be32dec(fr->ff->recbuf + 5); 168 } 169 } 170 171 /* Now do a binary search to find the sync block right before t0 */ 172 s = st = (fr->ff->logsize - o) / 2; 173 while (s > 1) { 174 /* We know we shouldn't wrap */ 175 if (o + st > fr->ff->logsize + 1) { 176 s = st = s / 2; 177 continue; 178 } 179 e = fifolog_int_read(fr->ff, o + st); 180 if (e) { 181 s = st = s / 2; 182 continue; 183 } 184 /* If not in same part, sequence won't match */ 185 seqs = be32dec(fr->ff->recbuf); 186 if (seqs != seq + st) { 187 s = st = s / 2; 188 continue; 189 } 190 /* If not sync block, try next */ 191 if (!(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC)) { 192 st++; 193 continue; 194 } 195 /* Check timestamp */ 196 tt = be32dec(fr->ff->recbuf + 5); 197 if (tt >= t0) { 198 s = st = s / 2; 199 continue; 200 } 201 o += st; 202 seq = seqs; 203 } 204 fprintf(stderr, "Read from %jx\n", o * fr->ff->recsize); 205 return (o); 206 } 207 208 static unsigned char * 209 fifolog_reader_chop(struct fifolog_reader *fr, fifolog_reader_render_t *func, void *priv) 210 { 211 u_char *p, *q; 212 uint32_t v, w, u; 213 214 p = fr->obuf; 215 q = fr->obuf + (fr->olen - fr->ff->zs->avail_out); 216 217 while (1) { 218 /* Make sure we have a complete header */ 219 if (p + 5 >= q) 220 return (p); 221 w = 4; 222 u = be32dec(p); 223 if (u & FIFOLOG_TIMESTAMP) { 224 fr->now = be32dec(p + 4); 225 w += 4; 226 } 227 if (u & FIFOLOG_LENGTH) { 228 v = p[w]; 229 w++; 230 if (p + w + v >= q) 231 return (p); 232 } else { 233 for (v = 0; p + v + w < q && p[v + w] != '\0'; v++) 234 continue; 235 if (p + v + w >= q) 236 return (p); 237 v++; 238 } 239 func(priv, fr->now, u, p + w, v); 240 p += w + v; 241 } 242 } 243 244 /* 245 * Process fifolog until end of written log or provided timestamp 246 */ 247 248 void 249 fifolog_reader_process(struct fifolog_reader *fr, off_t from, fifolog_reader_render_t *func, void *priv, time_t end) 250 { 251 uint32_t seq, lseq; 252 off_t o = from; 253 int i, e; 254 time_t t; 255 u_char *p, *q; 256 z_stream *zs; 257 258 CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC); 259 zs = fr->ff->zs; 260 lseq = 0; 261 while (1) { 262 e = fifolog_int_read(fr->ff, o); 263 if (e) 264 err(1, "Read error (%d)", e); 265 if (++o >= fr->ff->logsize) 266 o = 0; 267 seq = be32dec(fr->ff->recbuf); 268 if (lseq != 0 && seq != lseq + 1) 269 break; 270 lseq = seq; 271 zs->avail_in = fr->ff->recsize - 5; 272 zs->next_in = fr->ff->recbuf + 5; 273 if (fr->ff->recbuf[4] & FIFOLOG_FLG_1BYTE) 274 zs->avail_in -= fr->ff->recbuf[fr->ff->recsize - 1]; 275 if (fr->ff->recbuf[4] & FIFOLOG_FLG_4BYTE) 276 zs->avail_in -= 277 be32dec(fr->ff->recbuf + fr->ff->recsize - 4); 278 if (fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC) { 279 i = inflateReset(zs); 280 assert(i == Z_OK); 281 zs->next_out = fr->obuf; 282 zs->avail_out = fr->olen; 283 t = be32dec(fr->ff->recbuf + 5); 284 if (t > end) 285 break; 286 zs->next_in += 4; 287 zs->avail_in -= 4; 288 } 289 290 while(zs->avail_in > 0) { 291 i = inflate(zs, 0); 292 if (i == Z_BUF_ERROR) { 293 #if 1 294 fprintf(stderr, 295 "Z_BUF_ERROR [%d,%d] [%d,%d,%d]\n", 296 (int)(zs->next_in - fr->ff->recbuf), 297 zs->avail_in, 298 (int)(zs->next_out - fr->obuf), 299 zs->avail_out, fr->olen); 300 exit (250); 301 #else 302 303 i = Z_OK; 304 #endif 305 } 306 if (i == Z_STREAM_END) { 307 i = inflateReset(zs); 308 } 309 if (i != Z_OK) { 310 fprintf(stderr, "inflate = %d\n", i); 311 exit (250); 312 } 313 assert(i == Z_OK); 314 if (zs->avail_out != fr->olen) { 315 q = fr->obuf + (fr->olen - zs->avail_out); 316 p = fifolog_reader_chop(fr, func, priv); 317 if (p < q) 318 (void)memmove(fr->obuf, p, q - p); 319 zs->avail_out = fr->olen - (q - p); 320 zs->next_out = fr->obuf + (q - p); 321 } 322 } 323 } 324 } 325