Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / sbin / jscan / jfile.c
1 /*
2  * Copyright (c) 2004,2005 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
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  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sbin/jscan/jfile.c,v 1.10 2005/09/07 07:20:23 dillon Exp $
35  */
36
37 #include "jscan.h"
38 #include <dirent.h>
39
40 static void jalign(struct jfile *jf, enum jdirection direction);
41 static int jreadbuf(struct jfile *jf, enum jdirection direction,
42                     void *buf, int bytes);
43
44 /*
45  * Open a file descriptor for journal record access. 
46  *
47  * NOTE: only seekable descriptors are supported for backwards scans.
48  */
49 struct jfile *
50 jopen_fd(int fd)
51 {
52     struct jfile *jf;
53
54     jf = malloc(sizeof(struct jfile));
55     bzero(jf, sizeof(struct jfile));
56     jf->jf_fd = fd;
57     jf->jf_write_fd = -1;
58     jf->jf_open_flags = O_RDONLY;
59     jf->jf_pos = 0;
60     return(jf);
61 }
62
63 /*
64  * Open a prefix set.  <prefix>.nnnnnnnnn files or a <prefix>.transid file
65  * must exist to succeed.  No file descriptor is actually opened but
66  * the sequence number is initialized to the beginning or end of the set.
67  */
68 struct jfile *
69 jopen_prefix(const char *prefix, int rw)
70 {
71     struct jfile *jf;
72     struct jdata *jd;
73     unsigned int seq_beg = -1;
74     unsigned int seq_end = -1;
75     unsigned int seq;
76     struct stat st;
77     const char *dirname;
78     struct dirent *den;
79     DIR *dir;
80     char *basename;
81     char *data;
82     char *ptr;
83     int hastransid;
84     int baselen;
85     int fd;
86
87     dirname = data = strdup(prefix);
88     if ((basename = strrchr(dirname, '/')) != NULL) {
89         *basename++ = 0;
90     } else {
91         basename = data;
92         dirname = "./";
93     }
94     baselen = strlen(basename);
95     if ((dir = opendir(dirname)) != NULL) {
96         while ((den = readdir(dir)) != NULL) {
97             if (strncmp(den->d_name, basename, baselen) == 0 && 
98                 den->d_name[baselen] == '.'
99             ) {
100                 seq = strtoul(den->d_name + baselen + 1, &ptr, 16);
101                 if (*ptr == 0 && seq != ULONG_MAX) {
102                     if (seq_beg == (unsigned int)-1 || seq_beg > seq)
103                         seq_beg = seq;
104                     if (seq_end == (unsigned int)-1 || seq_end < seq)
105                         seq_end = seq;
106                 }
107             }
108         }
109         closedir(dir);
110     }
111     free(data);
112
113     hastransid = 0;
114     asprintf(&data, "%s.transid", prefix);
115     if (stat(data, &st) == 0)
116         hastransid = 1;
117     free(data);
118
119     if (seq_beg != (unsigned int)-1 || hastransid) {
120         if (seq_beg == (unsigned int)-1) {
121             seq_beg = 0;
122             seq_end = 0;
123             if (rw) {
124                 asprintf(&data, "%s.%08x", prefix, 0);
125                 if ((fd = open(data, O_RDWR|O_CREAT, 0666)) >= 0)
126                     close(fd);
127                 free(data);
128             }
129         }
130         jf = malloc(sizeof(struct jfile));
131         bzero(jf, sizeof(struct jfile));
132         jf->jf_fd = -1;
133         jf->jf_write_fd = -1;
134         jf->jf_prefix = strdup(prefix);
135         jf->jf_seq = seq_beg;
136         jf->jf_seq_beg = seq_beg;
137         jf->jf_seq_end = seq_end;
138         jf->jf_open_flags = rw ? (O_RDWR|O_CREAT) : O_RDONLY;
139         if (verbose_opt)
140             fprintf(stderr, "Open prefix set %08x-%08x\n", seq_beg, seq_end);
141         if ((jd = jread(jf, NULL, JD_BACKWARDS)) != NULL) {
142             jf->jf_last_transid = jd->jd_transid;
143             jfree(jf, jd);
144         }
145     } else {
146         jf = NULL;
147     }
148     return(jf);
149 }
150
151 /*
152  * Get a prefix set ready for append.
153  */
154 int
155 jrecord_init(const char *prefix)
156 {
157     struct jfile *jf;
158     struct stat st;
159     char *data;
160     int hasseqspace;
161     int fd;
162
163     /*
164      * Determine whether we already have a prefix set or whether we need
165      * to create one.
166      */
167     jf = jopen_prefix(prefix, 0);
168     hasseqspace = 0;
169     if (jf) {
170         if (jf->jf_seq_beg != (unsigned int)-1)
171             hasseqspace = 1;
172         jclose(jf);
173     }
174     asprintf(&data, "%s.transid", prefix);
175
176     /*
177      * If the sequence exists the transid file must ALREADY exist for us
178      * to be able to safely 'append' to the space.  Locked-down sequence
179      * spaces do not have a transid file.
180      */
181     if (hasseqspace) {
182         fd = open(data, O_RDWR, 0666);
183     } else {
184         fd = open(data, O_RDWR|O_CREAT, 0666);
185     }
186     free(data);
187     if (fd < 0)
188         return(-1);
189     if (fstat(fd, &st) == 0 && st.st_size == 0)
190         write(fd, "0000000000000000\n", 17);    /* starting transid in hex */
191     close(fd);
192     return(0);
193 }
194
195 /*
196  * Close a previously opened journal, clean up any side allocations.
197  */
198 void
199 jclose(struct jfile *jf)
200 {
201     if (jf->jf_fd >= 0) {
202         close(jf->jf_fd);
203         jf->jf_fd = -1;
204     }
205     if (jf->jf_write_fd >= 0) {
206         close(jf->jf_write_fd);
207         jf->jf_write_fd = -1;
208     }
209     free(jf);
210 }
211
212 /*
213  * Locate the next (or previous) raw record given a jfile, current record,
214  * and direction.  If the current record is NULL then the first or last
215  * record for the current sequence number is returned.
216  *
217  * PAD RECORD SPECIAL CASE.  Pad records can be 16 bytes long, which means
218  * that that rawrecend overlaps the transid field of the rawrecbeg.  Because
219  * the transid is garbage, we must skip and cannot return pad records.
220  */
221 struct jdata *
222 jread(struct jfile *jf, struct jdata *jd, enum jdirection direction)
223 {
224     struct journal_rawrecbeg head;
225     struct journal_rawrecbeg *headp;
226     struct journal_rawrecend tail;
227     struct journal_rawrecend *tailp;
228     struct stat st;
229     char *filename;
230     int allocsize;
231     int recsize;
232     int search;
233     int error;
234     int n;
235
236     if (jd) {
237         /*
238          * Handle the next/previous record case.  If running in the forwards
239          * direction we position the file just after jd.  If running in the
240          * backwards direction we position the file at the base of jd so
241          * the backwards read gets the previous record.
242          *
243          * In prefix mode we have to get the right descriptor open and
244          * position the file, since the fall through code resets to the
245          * beginning or end if it has to open a descriptor.
246          */
247         assert(direction != JD_SEQFIRST && direction != JD_SEQLAST);
248         if (jf->jf_prefix) {
249             if (jf->jf_fd >= 0 && jf->jf_seq != jd->jd_seq) {
250                 close(jf->jf_fd);
251                 jf->jf_fd = -1;
252             }
253             jf->jf_seq = jd->jd_seq;
254             if (jf->jf_fd < 0) {
255                 asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq);
256                 jf->jf_fd = open(filename, O_RDONLY);
257                 if (verbose_opt > 1)
258                     fprintf(stderr, "Open %s fd %d\n", filename, jf->jf_fd);
259                 free(filename);
260             }
261         }
262         if ((jmodes & JMODEF_INPUT_PIPE) == 0) {
263             if (direction == JD_FORWARDS) {
264                 jf->jf_pos = jd->jd_pos + jd->jd_size;
265                 lseek(jf->jf_fd, jf->jf_pos, 0);
266             } else {
267                 jf->jf_pos = jd->jd_pos;
268                 /* lseek(jf->jf_fd, jf->jf_pos, 0); not needed */
269             }
270         } else {
271             assert(direction == JD_FORWARDS && jf->jf_prefix == NULL);
272             assert(jf->jf_pos == jd->jd_pos + jd->jd_size);
273         }
274         jfree(jf, jd);
275     } else {
276         /*
277          * Handle the first/last record case.  In the prefix case we only
278          * need to set jf_seq and close the file handle and fall through.
279          * The SEQ modes maintain the current jf_seq (kinda a hack).
280          */
281         if (jf->jf_prefix) {
282             if (jf->jf_fd >= 0) {
283                 close(jf->jf_fd);
284                 jf->jf_fd = -1;
285             }
286             switch(direction) {
287             case JD_FORWARDS:
288                 jf->jf_seq = jf->jf_seq_beg;
289                 break;
290             case JD_BACKWARDS:
291                 jf->jf_seq = jf->jf_seq_end;
292                 break;
293             case JD_SEQFIRST:
294                 direction = JD_FORWARDS;
295                 break;
296             case JD_SEQLAST:
297                 direction = JD_BACKWARDS;
298                 break;
299             }
300         } else if ((jmodes & JMODEF_INPUT_PIPE) == 0) {
301             switch(direction) {
302             case JD_SEQFIRST:
303                 direction = JD_FORWARDS;
304                 /* fall through */
305             case JD_FORWARDS:
306                 jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_SET);
307                 break;
308             case JD_SEQLAST:
309                 direction = JD_BACKWARDS;
310                 /* fall through */
311             case JD_BACKWARDS:
312                 jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
313                 break;
314             }
315         } else {
316             if (direction == JD_SEQFIRST)
317                 direction = JD_FORWARDS;
318             assert(jf->jf_pos == 0 && direction == JD_FORWARDS);
319         }
320     }
321
322 top:
323     /*
324      * If we are doing a prefix scan and the descriptor is not open,
325      * open the file based on jf_seq and position it to the beginning
326      * or end based on the direction.  This is how we iterate through
327      * the prefix set.
328      */
329     if (jf->jf_fd < 0) {
330         asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq);
331         jf->jf_fd = open(filename, O_RDONLY);
332         if (verbose_opt > 1)
333             fprintf(stderr, "Open %s fd %d\n", filename, jf->jf_fd);
334         free(filename);
335         if (direction == JD_FORWARDS)
336             jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_SET);
337         else
338             jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
339     }
340
341     /*
342      * Get the current offset and make sure it is 16-byte aligned.  If it
343      * isn't, align it and enter search mode.
344      */
345     if (jf->jf_pos & 15) {
346         jf_warn(jf, "realigning bad offset and entering search mode");
347         jalign(jf, direction);
348         search = 1;
349     } else {
350         search = 0;
351     }
352
353     error = 0;
354     if (direction == JD_FORWARDS) {
355         /*
356          * Scan the journal forwards.  Note that the file pointer might not
357          * be seekable.
358          */
359         while ((error = jreadbuf(jf, direction, &head, sizeof(head))) == sizeof(head)) {
360             if (head.begmagic != JREC_BEGMAGIC) {
361                 if (search == 0)
362                     jf_warn(jf, "bad beginmagic, searching for new record");
363                 search = 1;
364                 jalign(jf, direction);
365                 continue;
366             }
367
368             /*
369              * The actual record is 16-byte aligned.  head.recsize contains
370              * the unaligned record size.
371              */
372             recsize = (head.recsize + 15) & ~15;
373             if (recsize < JREC_MINRECSIZE || recsize > JREC_MAXRECSIZE) {
374                 if (search == 0)
375                     jf_warn(jf, "bad recordsize: %d\n", recsize);
376                 search = 1;
377                 jalign(jf, direction);
378                 continue;
379             }
380             allocsize = offsetof(struct jdata, jd_data[recsize]);
381             allocsize = (allocsize + 255) & ~255;
382             jd = malloc(allocsize);
383             bzero(jd, offsetof(struct jdata, jd_data[0]));
384             bcopy(&head, jd->jd_data, sizeof(head));
385             n = jreadbuf(jf, direction, jd->jd_data + sizeof(head), 
386                          recsize - sizeof(head));
387             if (n != (int)(recsize - sizeof(head))) {
388                 if (search == 0)
389                     jf_warn(jf, "Incomplete stream record\n");
390                 search = 1;
391                 jalign(jf, direction);
392                 free(jd);
393                 continue;
394             }
395
396             tailp = (void *)(jd->jd_data + recsize - sizeof(*tailp));
397             if (tailp->endmagic != JREC_ENDMAGIC) {
398                 if (search == 0)
399                     jf_warn(jf, "bad endmagic, searching for new record");
400                 search = 1;
401                 jalign(jf, direction);
402                 free(jd);
403                 continue;
404             }
405
406             /*
407              * Skip pad records.
408              */
409             if (head.streamid == JREC_STREAMID_PAD) {
410                 free(jd);
411                 continue;
412             }
413
414             /*
415              * note: recsize is aligned (the actual record size),
416              * head.recsize is unaligned (the actual payload size).
417              */
418             jd->jd_transid = head.transid;
419             jd->jd_alloc = allocsize;
420             jd->jd_size = recsize;
421             jd->jd_seq = jf->jf_seq;
422             jd->jd_pos = jf->jf_pos - recsize;
423             jd->jd_refs = 1;
424             return(jd);
425         }
426     } else {
427         /*
428          * Scan the journal backwards.  Note that jread()'s reverse-seek and
429          * read.  The data read will be forward ordered, however.
430          */
431         while ((error = jreadbuf(jf, direction, &tail, sizeof(tail))) == sizeof(tail)) {
432             if (tail.endmagic != JREC_ENDMAGIC) {
433                 if (search == 0)
434                     jf_warn(jf, "bad endmagic, searching for new record");
435                 search = 1;
436                 jalign(jf, direction);
437                 continue;
438             }
439
440             /*
441              * The actual record is 16-byte aligned.  head.recsize contains
442              * the unaligned record size.
443              */
444             recsize = (tail.recsize + 15) & ~15;
445             if (recsize < JREC_MINRECSIZE || recsize > JREC_MAXRECSIZE) {
446                 if (search == 0)
447                     jf_warn(jf, "bad recordsize: %d\n", recsize);
448                 search = 1;
449                 jalign(jf, direction);
450                 continue;
451             }
452             allocsize = offsetof(struct jdata, jd_data[recsize]);
453             allocsize = (allocsize + 255) & ~255;
454             jd = malloc(allocsize);
455             bzero(jd, offsetof(struct jdata, jd_data[0]));
456             bcopy(&tail, jd->jd_data + recsize - sizeof(tail), sizeof(tail));
457             n = jreadbuf(jf, direction, jd->jd_data, recsize - sizeof(tail));
458             if (n != (int)(recsize - sizeof(tail))) {
459                 if (search == 0)
460                     jf_warn(jf, "Incomplete stream record\n");
461                 search = 1;
462                 jalign(jf, direction);
463                 free(jd);
464                 continue;
465             }
466
467             headp = (void *)jd->jd_data;
468             if (headp->begmagic != JREC_BEGMAGIC) {
469                 if (search == 0)
470                     jf_warn(jf, "bad begmagic, searching for new record");
471                 search = 1;
472                 jalign(jf, direction);
473                 free(jd);
474                 continue;
475             }
476
477             /*
478              * Skip pad records.
479              */
480             if (head.streamid == JREC_STREAMID_PAD) {
481                 free(jd);
482                 continue;
483             }
484
485             /*
486              * note: recsize is aligned (the actual record size),
487              * head.recsize is unaligned (the actual payload size).
488              */
489             jd->jd_transid = headp->transid;
490             jd->jd_alloc = allocsize;
491             jd->jd_size = recsize;
492             jd->jd_seq = jf->jf_seq;
493             jd->jd_pos = jf->jf_pos;
494             jd->jd_refs = 1;
495             return(jd);
496         }
497     }
498
499     /*
500      * If reading in prefix mode and there is no more data, close the 
501      * current descriptor, adjust the sequence number, and loop.
502      *
503      * If we hit the end of the sequence space and were asked to loop,
504      * check for the next sequence number and adjust jf_seq_end.  Leave
505      * the current descriptor open so we do not loose track of its seek
506      * position, and also to catch a race where another jscan may have
507      * written more data to the current sequence number before rolling
508      * the next sequence number.
509      */
510     if (error == 0 && jf->jf_prefix) {
511         if (direction == JD_FORWARDS) {
512             if (jf->jf_seq < jf->jf_seq_end) {
513                 ++jf->jf_seq;
514                 if (verbose_opt)
515                     fprintf(stderr, "jread: roll to seq %08x\n", jf->jf_seq);
516                 if (jf->jf_fd >= 0) {
517                     close(jf->jf_fd);
518                     jf->jf_fd = -1;
519                 }
520                 goto top;
521             }
522             if (jmodes & JMODEF_LOOP_FOREVER) {
523                 asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq + 1);
524                 if (stat(filename, &st) == 0) {
525                     ++jf->jf_seq_end;
526                     if (verbose_opt)
527                         fprintf(stderr, "jread: roll seq_end to %08x\n",
528                                          jf->jf_seq_end);
529                 } else {
530                     sleep(5);
531                 }
532                 goto top;
533             }
534         } else {
535             if (jf->jf_seq > jf->jf_seq_beg) {
536                 --jf->jf_seq;
537                 if (verbose_opt)
538                     fprintf(stderr, "jread: roll to seq %08x\n", jf->jf_seq);
539                 if (jf->jf_fd >= 0) {
540                     close(jf->jf_fd);
541                     jf->jf_fd = -1;
542                 }
543                 goto top;
544             }
545         }
546     }
547
548     /*
549      * If we hit EOF and were asked to loop forever on the input, leave
550      * the current descriptor open, sleep, and loop.
551      *
552      * We have already handled the prefix case.  This feature only works
553      * when doing forward scans and the input is not a pipe.
554      */
555     if (error == 0 && jf->jf_prefix == NULL &&
556         (jmodes & JMODEF_LOOP_FOREVER) &&
557         !(jmodes & JMODEF_INPUT_PIPE) && 
558         direction == JD_FORWARDS
559     ) {
560         sleep(5);
561         goto top;
562     }
563
564     /*
565      * Otherwise there are no more records and we are done.
566      */
567     return(NULL);
568 }
569
570 /*
571  * Write a record out.  If this is a prefix set and the file would
572  * exceed record_size, we rotate into a new sequence number.
573  */
574 void
575 jwrite(struct jfile *jf, struct jdata *jd)
576 {
577     struct stat st;
578     char *path;
579     int n;
580
581     assert(jf->jf_prefix);
582
583 again:
584     /*
585      * Open/create a new file in the prefix set
586      */
587     if (jf->jf_write_fd < 0) {
588         asprintf(&path, "%s.%08x", jf->jf_prefix, jf->jf_seq_end);
589         jf->jf_write_fd = open(path, O_RDWR|O_CREAT, 0666);
590         if (jf->jf_write_fd < 0 || fstat(jf->jf_write_fd, &st) != 0) {
591             fprintf(stderr, "Unable to open/create %s\n", path);
592             exit(1);
593         }
594         jf->jf_write_pos = st.st_size;
595         lseek(jf->jf_write_fd, jf->jf_write_pos, 0);
596         free(path);
597     }
598
599     /*
600      * Each file must contain at least one raw record, even if it exceeds
601      * the user-requested record-size.  Apart from that, we cycle to the next
602      * file when its size would exceed the user-specified 
603      */
604     if (jf->jf_write_pos > 0 && 
605         jf->jf_write_pos + jd->jd_size > prefix_file_size
606     ) {
607         close(jf->jf_write_fd);
608         jf->jf_write_fd = -1;
609         ++jf->jf_seq_end;
610         goto again;
611     }
612
613     /*
614      * Terminate if a failure occurs (for now).
615      */
616     n = write(jf->jf_write_fd, jd->jd_data, jd->jd_size);
617     if (n != jd->jd_size) {
618         ftruncate(jf->jf_write_fd, jf->jf_write_pos);
619         fprintf(stderr, "jwrite: failed %s\n", strerror(errno));
620         exit(1);
621     }
622     jf->jf_write_pos += n;
623     jf->jf_last_transid = jd->jd_transid;
624 }
625
626 /*
627  * Attempt to locate and return the record specified by the transid.  The
628  * returned record may be inexact.
629  *
630  * If scanning forwards this function guarentees that no record prior
631  * to the returned record is >= transid.
632  *
633  * If scanning backwards this function guarentees that no record after
634  * the returned record is <= transid.
635  */
636 struct jdata *
637 jseek(struct jfile *jf, int64_t transid, enum jdirection direction)
638 {
639     unsigned int seq;
640     struct jdata *jd = NULL;
641
642     /*
643      * If the input is a pipe we can't seek.
644      */
645     if (jmodes & JMODEF_INPUT_PIPE) {
646         assert(direction == JD_FORWARDS);
647         return (jread(jf, NULL, direction));
648     }
649
650     if (jf->jf_prefix) {
651         /*
652          * If we have a prefix set search the sequence space backwards until
653          * we find the file most likely to contain the transaction id.
654          */
655         if (verbose_opt > 2) {
656             fprintf(stderr, "jseek prefix set %s %08x-%08x\n", jf->jf_prefix,
657                     jf->jf_seq_beg, jf->jf_seq_end);
658         }
659         jd = NULL;
660         for (seq = jf->jf_seq_end; seq != jf->jf_seq_beg - 1; --seq) {
661             if (verbose_opt > 2)
662                 fprintf(stderr, "try seq %08x\n", seq);
663             jf->jf_seq = seq;
664             if ((jd = jread(jf, NULL, JD_SEQFIRST)) != NULL) {
665                 if (jd->jd_transid == transid)
666                     return(jd);
667                 if (jd->jd_transid < transid) {
668                     jfree(jf, jd);
669                     break;
670                 }
671                 jfree(jf, jd);
672             }
673         }
674
675         /*
676          * if transid is less the first file in the sequence space we
677          * return NULL if scanning backwards, indicating no records are
678          * available, or the first record in the sequence space if we
679          * are scanning forwards.
680          */
681         if (seq == jf->jf_seq_beg - 1) {
682             if (direction == JD_BACKWARDS)
683                 return(NULL);
684             else
685                 return(jread(jf, NULL, JD_FORWARDS));
686         }
687         if (verbose_opt > 1)
688             fprintf(stderr, "jseek input prefix set to seq %08x\n", seq);
689     }
690
691     /*
692      * Position us to the end of the current record, then scan backwards
693      * looking for the requested transid.
694      */
695     jd = jread(jf, NULL, JD_SEQLAST);
696     while (jd != NULL) {
697         if (jd->jd_transid <= transid) {
698             if (jd->jd_transid < transid) {
699                 if (direction == JD_FORWARDS)
700                     jd =jread(jf, jd, JD_FORWARDS);
701             }
702             if (verbose_opt > 1) {
703                 fprintf(stderr, "jseek returning seq %08x offset 0x%08llx\n",
704                         jd->jd_seq, jd->jd_pos);
705             }
706             return(jd);
707         }
708         jd = jread(jf, jd, JD_BACKWARDS);
709     }
710
711     /*
712      * We scanned the whole file with no luck, all the transid's are
713      * greater then the requested transid.  If the intended read 
714      * direction is backwards there are no records and we return NULL.
715      * If it is forwards we return the first record.
716      */
717     if (direction == JD_BACKWARDS)
718         return(NULL);
719     else
720         return(jread(jf, NULL, JD_FORWARDS));
721 }
722
723 /*
724  * Data returned by jread() is persistent until released.
725  */
726 struct jdata *
727 jref(struct jdata *jd)
728 {
729     ++jd->jd_refs;
730     return(jd);
731 }
732
733 void
734 jfree(struct jfile *jf __unused, struct jdata *jd)
735 {
736     if (--jd->jd_refs == 0)
737         free(jd);
738 }
739
740 /*
741  * Align us to the next 16 byte boundary.  If scanning forwards we align
742  * forwards if not already aligned.  If scanning backwards we align
743  * backwards if not already aligned.  We only have to synchronize the
744  * seek position with the file seek position for forward scans.
745  */
746 static void
747 jalign(struct jfile *jf, enum jdirection direction)
748 {
749     char dummy[16];
750     int bytes;
751
752     if ((int)jf->jf_pos & 15) {
753         if (direction == JD_FORWARDS) {
754             bytes = 16 - ((int)jf->jf_pos & 15);
755             jreadbuf(jf, direction, dummy, bytes);
756         } else {
757             jf->jf_pos = jf->jf_pos & ~(off_t)15;
758         }
759     }
760 }
761
762 /*
763  * Read the next raw journal record forwards or backwards and return a
764  * pointer to it.  Note that the file pointer's actual seek position does
765  * not match jf_pos in the reverse direction case.
766  */
767 static int
768 jreadbuf(struct jfile *jf, enum jdirection direction, void *buf, int bytes)
769 {
770     int ttl = 0;
771     int n;
772
773     if (jf->jf_fd < 0)
774         return(0);
775
776     if (direction == JD_FORWARDS) {
777         while (ttl != bytes) {
778             n = read(jf->jf_fd, (char *)buf + ttl, bytes - ttl);
779             if (n <= 0) {
780                 if (n < 0 && ttl == 0)
781                     ttl = -errno;
782                 break;
783             }
784             ttl += n;
785             jf->jf_pos += n;
786         }
787     } else {
788         if (jf->jf_pos >= bytes) {
789             jf->jf_pos -= bytes;
790             lseek(jf->jf_fd, jf->jf_pos, 0);
791             while (ttl != bytes) {
792                 n = read(jf->jf_fd, (char *)buf + ttl, bytes - ttl);
793                 if (n <= 0) {
794                     if (n < 0 && ttl == 0)
795                         ttl = -errno;
796                     break;
797                 }
798                 ttl += n;
799             }
800         }
801     }
802     return(ttl);
803 }
804