Rework and expand the algorithms in JSCAN, part 4/?.
[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.9 2005/09/07 02:34:37 dillon Exp $
35  */
36
37 #include "jscan.h"
38 #include <dirent.h>
39
40 static void jalign(struct jfile *jf);
41 static int jreadbuf(struct jfile *jf, void *buf, int bytes);
42 static void jreset(struct jfile *jf, unsigned int seq, 
43                    enum jdirection direction);
44
45 /*
46  * Open a file descriptor for journal record access. 
47  *
48  * NOTE: only seekable descriptors are supported for backwards scans.
49  */
50 struct jfile *
51 jopen_fd(int fd, enum jdirection direction)
52 {
53     struct jfile *jf;
54
55     jf = malloc(sizeof(struct jfile));
56     bzero(jf, sizeof(struct jfile));
57     jf->jf_fd = fd;
58     jf->jf_write_fd = -1;
59     jf->jf_open_flags = O_RDONLY;
60     if (direction == JD_BACKWARDS) {
61         jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
62     }
63     jf->jf_direction = direction;
64     return(jf);
65 }
66
67 /*
68  * Open a prefix set.  <prefix>.nnnnnnnnn files or a <prefix>.transid file
69  * must exist to succeed.  No file descriptor is actually opened but
70  * the sequence number is initialized to the beginning or end of the set.
71  */
72 struct jfile *
73 jopen_prefix(const char *prefix, enum jdirection direction, int rw)
74 {
75     struct jfile *jf;
76     struct jdata *jd;
77     unsigned int seq_beg = -1;
78     unsigned int seq_end = -1;
79     unsigned int seq;
80     struct stat st;
81     const char *dirname;
82     struct dirent *den;
83     DIR *dir;
84     char *basename;
85     char *data;
86     char *ptr;
87     int hastransid;
88     int baselen;
89     int fd;
90
91     dirname = data = strdup(prefix);
92     if ((basename = strrchr(dirname, '/')) != NULL) {
93         *basename++ = 0;
94     } else {
95         basename = data;
96         dirname = "./";
97     }
98     baselen = strlen(basename);
99     if ((dir = opendir(dirname)) != NULL) {
100         while ((den = readdir(dir)) != NULL) {
101             if (strncmp(den->d_name, basename, baselen) == 0 && 
102                 den->d_name[baselen] == '.'
103             ) {
104                 seq = strtoul(den->d_name + baselen + 1, &ptr, 16);
105                 if (*ptr == 0 && seq != ULONG_MAX) {
106                     if (seq_beg == (unsigned int)-1 || seq_beg > seq)
107                         seq_beg = seq;
108                     if (seq_end == (unsigned int)-1 || seq_end < seq)
109                         seq_end = seq;
110                 }
111             }
112         }
113         closedir(dir);
114     }
115     free(data);
116
117     hastransid = 0;
118     asprintf(&data, "%s.transid", prefix);
119     if (stat(data, &st) == 0)
120         hastransid = 1;
121     free(data);
122
123     if (seq_beg != (unsigned int)-1 || hastransid) {
124         if (seq_beg == (unsigned int)-1) {
125             seq_beg = 0;
126             seq_end = 0;
127             if (rw) {
128                 asprintf(&data, "%s.%08x", prefix, 0);
129                 if ((fd = open(data, O_RDWR|O_CREAT, 0666)) >= 0)
130                     close(fd);
131                 free(data);
132             }
133         }
134         jf = malloc(sizeof(struct jfile));
135         bzero(jf, sizeof(struct jfile));
136         jf->jf_fd = -1;
137         jf->jf_write_fd = -1;
138         jf->jf_prefix = strdup(prefix);
139         jf->jf_seq_beg = seq_beg;
140         jf->jf_seq_end = seq_end;
141         jf->jf_open_flags = rw ? (O_RDWR|O_CREAT) : O_RDONLY;
142         jreset(jf, seq_end, JD_BACKWARDS);
143         if (verbose_opt)
144             fprintf(stderr, "Open prefix set %08x-%08x\n", seq_beg, seq_end);
145         if (jread(jf, &jd, JD_BACKWARDS) == 0) {
146             jf->jf_last_transid = jd->jd_transid;
147             jfree(jf, jd);
148         }
149         if (direction == JD_BACKWARDS)
150             jreset(jf, jf->jf_seq_end, direction);
151         else
152             jreset(jf, jf->jf_seq_beg, direction);
153     } else {
154         jf = NULL;
155     }
156     return(jf);
157 }
158
159 /*
160  * Get a prefix set ready for append.
161  */
162 int
163 jrecord_init(const char *prefix)
164 {
165     struct jfile *jf;
166     struct stat st;
167     char *data;
168     int hasseqspace;
169     int fd;
170
171     /*
172      * Determine whether we already have a prefix set or whether we need
173      * to create one.
174      */
175     jf = jopen_prefix(prefix, 0, 0);
176     hasseqspace = 0;
177     if (jf) {
178         if (jf->jf_seq_beg != (unsigned int)-1)
179             hasseqspace = 1;
180         jclose(jf);
181     }
182     asprintf(&data, "%s.transid", prefix);
183
184     /*
185      * If the sequence exists the transid file must ALREADY exist for us
186      * to be able to safely 'append' to the space.  Locked-down sequence
187      * spaces do not have a transid file.
188      */
189     if (hasseqspace) {
190         fd = open(data, O_RDWR, 0666);
191     } else {
192         fd = open(data, O_RDWR|O_CREAT, 0666);
193     }
194     free(data);
195     if (fd < 0)
196         return(-1);
197     if (fstat(fd, &st) == 0 && st.st_size == 0)
198         write(fd, "0000000000000000\n", 17);    /* starting transid in hex */
199     close(fd);
200     return(0);
201 }
202
203 /*
204  * Close a previously opened journal, clean up any side allocations.
205  */
206 void
207 jclose(struct jfile *jf)
208 {
209     if (jf->jf_fd >= 0) {
210         close(jf->jf_fd);
211         jf->jf_fd = -1;
212     }
213     if (jf->jf_write_fd >= 0) {
214         close(jf->jf_write_fd);
215         jf->jf_write_fd = -1;
216     }
217     free(jf);
218 }
219
220 /*
221  * Locate the next (or previous) complete virtual stream transaction given a
222  * file descriptor and direction.  Keep track of partial stream records as
223  * a side effect.
224  *
225  * Note that a transaction might represent a huge I/O operation, resulting
226  * in an overall node structure that spans gigabytes, but individual
227  * subrecord leaf nodes are limited in size and we depend on this to simplify
228  * the handling of leaf records. 
229  *
230  * A transaction may cover several raw records.  The jstream collection for
231  * a transaction is only returned when the entire transaction has been
232  * successfully scanned.  Due to the interleaving of transactions the ordering
233  * of returned JS's may be different (not exactly reversed) when scanning a
234  * journal backwards verses forwards.  Since parallel operations are 
235  * theoretically non-conflicting, this should not present a problem.
236  *
237  * PAD RECORD SPECIAL CASE.  Pad records can be 16 bytes long, which means
238  * that that rawrecend overlaps the transid field of the rawrecbeg.  Because
239  * the transid is garbage, we must skip and cannot return pad records.
240  */
241 int
242 jread(struct jfile *jf, struct jdata **jdp, enum jdirection direction)
243 {
244     struct journal_rawrecbeg head;
245     struct journal_rawrecbeg *headp;
246     struct journal_rawrecend tail;
247     struct journal_rawrecend *tailp;
248     struct jdata *jd;
249     struct stat st;
250     char *filename;
251     int allocsize;
252     int recsize;
253     int search;
254     int error;
255     int n;
256
257     /*
258      * If changing direction on an open descriptor we have to fixup jf_pos.
259      * When reading backwards the actual file seek position does not match
260      * jf_pos.
261      *
262      * If you read forwards then read backwards, or read backwords then
263      * read forwards, you will get the same record.
264      */
265     if (jf->jf_direction != direction) {
266         if (jf->jf_fd >= 0) {
267             if (direction == JD_FORWARDS) {
268                 lseek(jf->jf_fd, jf->jf_pos, 0);
269             }
270         }
271         jf->jf_direction = direction;
272     }
273
274 top:
275     /*
276      * If reading in prefix mode and we have no descriptor, open
277      * a new descriptor based on the current sequence number.  If
278      * this fails we will fall all the way through to the end which will
279      * setup the next sequence number and loop.
280      */
281     if (jf->jf_fd == -1 && jf->jf_prefix) {
282         asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq);
283         if ((jf->jf_fd = open(filename, O_RDONLY)) >= 0) {
284             if (jf->jf_direction == JD_FORWARDS)
285                 jf->jf_pos = 0;
286             else
287                 jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
288             search = 0;
289         }
290         if (verbose_opt > 1)
291             fprintf(stderr, "Open %s fd %d\n", filename, jf->jf_fd);
292         free(filename);
293     }
294
295     /*
296      * Get the current offset and make sure it is 16-byte aligned.  If it
297      * isn't, align it and enter search mode.
298      */
299     if (jf->jf_pos & 15) {
300         jf_warn(jf, "realigning bad offset and entering search mode");
301         jalign(jf);
302         search = 1;
303     } else {
304         search = 0;
305     }
306
307     error = 0;
308     if (jf->jf_direction == JD_FORWARDS) {
309         /*
310          * Scan the journal forwards.  Note that the file pointer might not
311          * be seekable.
312          */
313         while ((error = jreadbuf(jf, &head, sizeof(head))) == sizeof(head)) {
314             if (head.begmagic != JREC_BEGMAGIC) {
315                 if (search == 0)
316                     jf_warn(jf, "bad beginmagic, searching for new record");
317                 search = 1;
318                 jalign(jf);
319                 continue;
320             }
321
322             /*
323              * The actual record is 16-byte aligned.  head.recsize contains
324              * the unaligned record size.
325              */
326             recsize = (head.recsize + 15) & ~15;
327             if (recsize < JREC_MINRECSIZE || recsize > JREC_MAXRECSIZE) {
328                 if (search == 0)
329                     jf_warn(jf, "bad recordsize: %d\n", recsize);
330                 search = 1;
331                 jalign(jf);
332                 continue;
333             }
334             allocsize = offsetof(struct jdata, jd_data[recsize]);
335             allocsize = (allocsize + 255) & ~255;
336             jd = malloc(allocsize);
337             bzero(jd, offsetof(struct jdata, jd_data[0]));
338             bcopy(&head, jd->jd_data, sizeof(head));
339             n = jreadbuf(jf, jd->jd_data + sizeof(head), 
340                          recsize - sizeof(head));
341             if (n != (int)(recsize - sizeof(head))) {
342                 if (search == 0)
343                     jf_warn(jf, "Incomplete stream record\n");
344                 search = 1;
345                 jalign(jf);
346                 free(jd);
347                 continue;
348             }
349
350             tailp = (void *)(jd->jd_data + recsize - sizeof(*tailp));
351             if (tailp->endmagic != JREC_ENDMAGIC) {
352                 if (search == 0)
353                     jf_warn(jf, "bad endmagic, searching for new record");
354                 search = 1;
355                 jalign(jf);
356                 free(jd);
357                 continue;
358             }
359
360             /*
361              * Skip pad records.
362              */
363             if (head.streamid == JREC_STREAMID_PAD) {
364                 free(jd);
365                 continue;
366             }
367
368             /*
369              * note: recsize is aligned (the actual record size),
370              * head.recsize is unaligned (the actual payload size).
371              */
372             jd->jd_transid = head.transid;
373             jd->jd_alloc = allocsize;
374             jd->jd_size = recsize;
375             jd->jd_refs = 1;
376             *jdp = jd;
377             return(0);
378         }
379     } else {
380         /*
381          * Scan the journal backwards.  Note that jread()'s reverse-seek and
382          * read.  The data read will be forward ordered, however.
383          */
384         while ((error = jreadbuf(jf, &tail, sizeof(tail))) == sizeof(tail)) {
385             if (tail.endmagic != JREC_ENDMAGIC) {
386                 if (search == 0)
387                     jf_warn(jf, "bad endmagic, searching for new record");
388                 search = 1;
389                 jalign(jf);
390                 continue;
391             }
392
393             /*
394              * The actual record is 16-byte aligned.  head.recsize contains
395              * the unaligned record size.
396              */
397             recsize = (tail.recsize + 15) & ~15;
398             if (recsize < JREC_MINRECSIZE || recsize > JREC_MAXRECSIZE) {
399                 if (search == 0)
400                     jf_warn(jf, "bad recordsize: %d\n", recsize);
401                 search = 1;
402                 jalign(jf);
403                 continue;
404             }
405             allocsize = offsetof(struct jdata, jd_data[recsize]);
406             allocsize = (allocsize + 255) & ~255;
407             jd = malloc(allocsize);
408             bzero(jd, offsetof(struct jdata, jd_data[0]));
409             bcopy(&tail, jd->jd_data + recsize - sizeof(tail), sizeof(tail));
410             n = jreadbuf(jf, jd->jd_data, recsize - sizeof(tail));
411             if (n != (int)(recsize - sizeof(tail))) {
412                 if (search == 0)
413                     jf_warn(jf, "Incomplete stream record\n");
414                 search = 1;
415                 jalign(jf);
416                 free(jd);
417                 continue;
418             }
419
420             headp = (void *)jd->jd_data;
421             if (headp->begmagic != JREC_BEGMAGIC) {
422                 if (search == 0)
423                     jf_warn(jf, "bad begmagic, searching for new record");
424                 search = 1;
425                 jalign(jf);
426                 free(jd);
427                 continue;
428             }
429
430             /*
431              * Skip pad records.
432              */
433             if (head.streamid == JREC_STREAMID_PAD) {
434                 free(jd);
435                 continue;
436             }
437
438             /*
439              * note: recsize is aligned (the actual record size),
440              * head.recsize is unaligned (the actual payload size).
441              */
442             jd->jd_transid = headp->transid;
443             jd->jd_alloc = allocsize;
444             jd->jd_size = recsize;
445             jd->jd_refs = 1;
446             *jdp = jd;
447             return(0);
448         }
449     }
450
451     /*
452      * If reading in prefix mode and there is no more data, close the 
453      * current descriptor, adjust the sequence number, and loop.
454      *
455      * If we hit the end of the sequence space and were asked to loop,
456      * check for the next sequence number and adjust jf_seq_end.  Leave
457      * the current descriptor open so we do not loose track of its seek
458      * position, and also to catch a race where another jscan may have
459      * written more data to the current sequence number before rolling
460      * the next sequence number.
461      */
462     if (error == 0 && jf->jf_prefix) {
463         if (jf->jf_direction == JD_FORWARDS) {
464             if (jf->jf_seq < jf->jf_seq_end) {
465                 ++jf->jf_seq;
466                 if (verbose_opt)
467                     fprintf(stderr, "jread: roll to seq %08x\n", jf->jf_seq);
468                 if (jf->jf_fd >= 0) {
469                     close(jf->jf_fd);
470                     jf->jf_fd = -1;
471                 }
472                 goto top;
473             }
474             if (jmodes & JMODEF_LOOP_FOREVER) {
475                 asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq + 1);
476                 if (stat(filename, &st) == 0) {
477                     ++jf->jf_seq_end;
478                     if (verbose_opt)
479                         fprintf(stderr, "jread: roll seq_end to %08x\n",
480                                          jf->jf_seq_end);
481                 } else {
482                     sleep(5);
483                 }
484                 goto top;
485             }
486         } else {
487             if (jf->jf_seq > jf->jf_seq_beg) {
488                 --jf->jf_seq;
489                 if (verbose_opt)
490                     fprintf(stderr, "jread: roll to seq %08x\n", jf->jf_seq);
491                 if (jf->jf_fd >= 0) {
492                     close(jf->jf_fd);
493                     jf->jf_fd = -1;
494                 }
495                 goto top;
496             }
497         }
498     }
499
500     /*
501      * If we hit EOF and were asked to loop forever on the input, leave
502      * the current descriptor open, sleep, and loop.
503      *
504      * We have already handled the prefix case.  This feature only works
505      * when doing forward scans and the input is not a pipe.
506      */
507     if (error == 0 && (jmodes & JMODEF_LOOP_FOREVER) &&
508         !(jmodes & JMODEF_INPUT_PIPE) && jf->jf_direction == JD_FORWARDS &&
509         jf->jf_prefix == NULL
510     ) {
511         sleep(5);
512         goto top;
513     }
514
515     /*
516      * Otherwise there are no more records and we are done.
517      */
518     *jdp = NULL;
519     return(-1);
520 }
521
522 /*
523  * Write a record out.  If this is a prefix set and the file would
524  * exceed record_size, we rotate into a new sequence number.
525  */
526 void
527 jwrite(struct jfile *jf, struct jdata *jd)
528 {
529     struct stat st;
530     char *path;
531     int n;
532
533     assert(jf->jf_prefix);
534
535 again:
536     /*
537      * Open/create a new file in the prefix set
538      */
539     if (jf->jf_write_fd < 0) {
540         asprintf(&path, "%s.%08x", jf->jf_prefix, jf->jf_seq_end);
541         jf->jf_write_fd = open(path, O_RDWR|O_CREAT, 0666);
542         if (jf->jf_write_fd < 0 || fstat(jf->jf_write_fd, &st) != 0) {
543             fprintf(stderr, "Unable to open/create %s\n", path);
544             exit(1);
545         }
546         jf->jf_write_pos = st.st_size;
547         lseek(jf->jf_write_fd, jf->jf_write_pos, 0);
548         free(path);
549     }
550
551     /*
552      * Each file must contain at least one raw record, even if it exceeds
553      * the user-requested record-size.  Apart from that, we cycle to the next
554      * file when its size would exceed the user-specified 
555      */
556     if (jf->jf_write_pos > 0 && 
557         jf->jf_write_pos + jd->jd_size > prefix_file_size
558     ) {
559         close(jf->jf_write_fd);
560         jf->jf_write_fd = -1;
561         ++jf->jf_seq_end;
562         goto again;
563     }
564
565     /*
566      * Terminate if a failure occurs (for now).
567      */
568     n = write(jf->jf_write_fd, jd->jd_data, jd->jd_size);
569     if (n != jd->jd_size) {
570         ftruncate(jf->jf_write_fd, jf->jf_write_pos);
571         fprintf(stderr, "jwrite: failed %s\n", strerror(errno));
572         exit(1);
573     }
574     jf->jf_write_pos += n;
575     jf->jf_last_transid = jd->jd_transid;
576 }
577
578 /*
579  * Reset the direction and seek us to the beginning or end
580  * of the currenet file.  In prefix mode we might as well
581  * just let jsread() do it since it might have to do it 
582  * anyway.
583  */
584 static void
585 jreset(struct jfile *jf, unsigned int seq, enum jdirection direction)
586 {
587     if (jf->jf_prefix) {
588         if (jf->jf_fd >= 0) {
589             close(jf->jf_fd);
590             jf->jf_fd = -1;
591         }
592         jf->jf_pos = -1;
593         jf->jf_seq = seq;
594     } else {
595         if (direction) {
596             jf->jf_pos = lseek(jf->jf_fd, 0L, 0);
597         } else {
598             jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
599         }
600     }
601     jf->jf_direction = direction;
602 }
603
604 /*
605  * Position the file such that the next jread() in the specified
606  * direction will read the record for the specified transaction id.
607  * If the transaction id does not exist the jseek will position the
608  * file at the next higher (if reading forwards) or lower (if reading
609  * backwards) transaction id.
610  *
611  * jseek is not required to be exact.  It is allowed to position the
612  * file at any point <= the transid (forwards) or >= the transid
613  * (backwards).  However, the more off jseek is, the more scanning
614  * the code will have to do to position itself properly.
615  */
616 void
617 jseek(struct jfile *jf, int64_t transid, enum jdirection direction)
618 {
619     int64_t transid_beg;
620     int64_t transid_end;
621     unsigned int seq = (unsigned int)-1;
622     struct jdata *jd;
623
624     /*
625      * If we have a prefix set search the sequence space backwards until
626      * we find the file most likely to contain the transaction id.
627      */
628     if (jf->jf_prefix) {
629         if (verbose_opt > 2) {
630             fprintf(stderr, "jseek prefix set %s %08x-%08x\n", jf->jf_prefix,
631                     jf->jf_seq_beg, jf->jf_seq_end);
632         }
633         for (seq = jf->jf_seq_end; seq != jf->jf_seq_beg - 1; --seq) {
634             jreset(jf, seq, JD_FORWARDS);
635             if (verbose_opt > 2)
636                 fprintf(stderr, "try seq %08x\n", seq);
637             if (jread(jf, &jd, JD_FORWARDS) == 0) {
638                 transid_beg = jd->jd_transid;
639                 if (verbose_opt > 2)
640                     fprintf(stderr, "transid %016llx\n", jd->jd_transid);
641                 jfree(jf, jd);
642                 if (transid_beg == transid) {
643                     jreset(jf, seq, JD_FORWARDS);
644                     break;
645                 }
646                 if (transid_beg < transid)
647                     break;
648             }
649         }
650         if (seq == jf->jf_seq_beg - 1) {
651             seq = jf->jf_seq_beg;
652         }
653         if (verbose_opt > 1)
654             fprintf(stderr, "jseek input prefix set to seq %08x\n", seq);
655     }
656
657     /*
658      * Position us within the current file.
659      */
660     jreset(jf, seq, JD_BACKWARDS);
661     while (jread(jf, &jd, JD_BACKWARDS) == 0) {
662         transid_end = jd->jd_transid;
663         jfree(jf, jd);
664
665         /*
666          * If we are at the sequence number the next forward read
667          * will re-read the record since we were going backwards.  If
668          * the caller wants to go backwards we have to go forwards one
669          * record so the caller gets the transid record when it does
670          * its first backwards read.  Confused yet?
671          *
672          * If we are at a smaller sequence number we need to read forwards
673          * by one so the next forwards read gets the first record > transid,
674          * or the next backwards read gets the first record < transid.
675          */
676         if (transid_end == transid) {
677             if (direction == JD_BACKWARDS) {
678                 if (jread(jf, &jd, JD_FORWARDS) == 0)
679                     jfree(jf, jd);
680             }
681             break;
682         }
683         if (transid_end < transid) {
684             if (jread(jf, &jd, JD_FORWARDS) == 0)
685                 jfree(jf, jd);
686             break;
687         }
688     }
689     if (verbose_opt) {
690         fprintf(stderr, "jseek %s to seq %08x offset 0x%08llx\n",
691                 jf->jf_prefix, jf->jf_seq, jf->jf_pos);
692     }
693 }
694
695 /*
696  * Data returned by jread() is persistent until released.
697  */
698 struct jdata *
699 jref(struct jdata *jd)
700 {
701     ++jd->jd_refs;
702     return(jd);
703 }
704
705 void
706 jfree(struct jfile *jf __unused, struct jdata *jd)
707 {
708     if (--jd->jd_refs == 0)
709         free(jd);
710 }
711
712 /*
713  * Align us to the next 16 byte boundary.  If scanning forwards we align
714  * forwards if not already aligned.  If scanning backwards we align
715  * backwards if not already aligned.  We only have to synchronize the
716  * seek position with the file seek position for forward scans.
717  */
718 static void
719 jalign(struct jfile *jf)
720 {
721     char dummy[16];
722     int bytes;
723
724     if ((int)jf->jf_pos & 15) {
725         if (jf->jf_direction == JD_FORWARDS) {
726             bytes = 16 - ((int)jf->jf_pos & 15);
727             jreadbuf(jf, dummy, bytes);
728         } else {
729             jf->jf_pos = jf->jf_pos & ~(off_t)15;
730         }
731     }
732 }
733
734 /*
735  * Read the next raw journal record forwards or backwards and return a
736  * pointer to it.  Note that the file pointer's actual seek position does
737  * not match jf_pos in the reverse direction case.
738  */
739 static int
740 jreadbuf(struct jfile *jf, void *buf, int bytes)
741 {
742     int ttl = 0;
743     int n;
744
745     if (jf->jf_fd < 0)
746         return(0);
747
748     if (jf->jf_direction == JD_FORWARDS) {
749         while (ttl != bytes) {
750             n = read(jf->jf_fd, (char *)buf + ttl, bytes - ttl);
751             if (n <= 0) {
752                 if (n < 0 && ttl == 0)
753                     ttl = -errno;
754                 break;
755             }
756             ttl += n;
757             jf->jf_pos += n;
758         }
759     } else {
760         if (jf->jf_pos >= bytes) {
761             jf->jf_pos -= bytes;
762             lseek(jf->jf_fd, jf->jf_pos, 0);
763             while (ttl != bytes) {
764                 n = read(jf->jf_fd, (char *)buf + ttl, bytes - ttl);
765                 if (n <= 0) {
766                     if (n < 0 && ttl == 0)
767                         ttl = -errno;
768                     break;
769                 }
770                 ttl += n;
771             }
772         }
773     }
774     return(ttl);
775 }
776