Rework and expand the algorithms in JSCAN, part 3/?.
[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.8 2005/09/06 22:33:00 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 int
238 jread(struct jfile *jf, struct jdata **jdp, enum jdirection direction)
239 {
240     struct journal_rawrecbeg head;
241     struct journal_rawrecbeg *headp;
242     struct journal_rawrecend tail;
243     struct journal_rawrecend *tailp;
244     struct jdata *jd;
245     struct stat st;
246     char *filename;
247     int allocsize;
248     int recsize;
249     int search;
250     int error;
251     int n;
252
253     /*
254      * If changing direction on an open descriptor we have to fixup jf_pos.
255      * When reading backwards the actual file seek position does not match
256      * jf_pos.
257      *
258      * If you read forwards then read backwards, or read backwords then
259      * read forwards, you will get the same record.
260      */
261     if (jf->jf_direction != direction) {
262         if (jf->jf_fd >= 0) {
263             if (direction == JD_FORWARDS) {
264                 lseek(jf->jf_fd, jf->jf_pos, 0);
265             }
266         }
267         jf->jf_direction = direction;
268     }
269
270 top:
271     /*
272      * If reading in prefix mode and we have no descriptor, open
273      * a new descriptor based on the current sequence number.  If
274      * this fails we will fall all the way through to the end which will
275      * setup the next sequence number and loop.
276      */
277     if (jf->jf_fd == -1 && jf->jf_prefix) {
278         asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq);
279         if ((jf->jf_fd = open(filename, O_RDONLY)) >= 0) {
280             if (jf->jf_direction == JD_FORWARDS)
281                 jf->jf_pos = 0;
282             else
283                 jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
284             search = 0;
285         }
286         if (verbose_opt > 1)
287             fprintf(stderr, "Open %s fd %d\n", filename, jf->jf_fd);
288         free(filename);
289     }
290
291     /*
292      * Get the current offset and make sure it is 16-byte aligned.  If it
293      * isn't, align it and enter search mode.
294      */
295     if (jf->jf_pos & 15) {
296         jf_warn(jf, "realigning bad offset and entering search mode");
297         jalign(jf);
298         search = 1;
299     } else {
300         search = 0;
301     }
302
303     error = 0;
304     if (jf->jf_direction == JD_FORWARDS) {
305         /*
306          * Scan the journal forwards.  Note that the file pointer might not
307          * be seekable.
308          */
309         while ((error = jreadbuf(jf, &head, sizeof(head))) == sizeof(head)) {
310             if (head.begmagic != JREC_BEGMAGIC) {
311                 if (search == 0)
312                     jf_warn(jf, "bad beginmagic, searching for new record");
313                 search = 1;
314                 jalign(jf);
315                 continue;
316             }
317
318             /*
319              * The actual record is 16-byte aligned.  head.recsize contains
320              * the unaligned record size.
321              */
322             recsize = (head.recsize + 15) & ~15;
323             if (recsize < JREC_MINRECSIZE || recsize > JREC_MAXRECSIZE) {
324                 if (search == 0)
325                     jf_warn(jf, "bad recordsize: %d\n", recsize);
326                 search = 1;
327                 jalign(jf);
328                 continue;
329             }
330             allocsize = offsetof(struct jdata, jd_data[recsize]);
331             allocsize = (allocsize + 255) & ~255;
332             jd = malloc(allocsize);
333             bzero(jd, offsetof(struct jdata, jd_data[0]));
334             bcopy(&head, jd->jd_data, sizeof(head));
335             n = jreadbuf(jf, jd->jd_data + sizeof(head), 
336                          recsize - sizeof(head));
337             if (n != (int)(recsize - sizeof(head))) {
338                 if (search == 0)
339                     jf_warn(jf, "Incomplete stream record\n");
340                 search = 1;
341                 jalign(jf);
342                 free(jd);
343                 continue;
344             }
345
346             tailp = (void *)(jd->jd_data + recsize - sizeof(*tailp));
347             if (tailp->endmagic != JREC_ENDMAGIC) {
348                 if (search == 0)
349                     jf_warn(jf, "bad endmagic, searching for new record");
350                 search = 1;
351                 jalign(jf);
352                 free(jd);
353                 continue;
354             }
355
356             /*
357              * note: recsize is aligned (the actual record size),
358              * head.recsize is unaligned (the actual payload size).
359              */
360             jd->jd_transid = head.transid;
361             jd->jd_alloc = allocsize;
362             jd->jd_size = recsize;
363             jd->jd_refs = 1;
364             *jdp = jd;
365             return(0);
366         }
367     } else {
368         /*
369          * Scan the journal backwards.  Note that jread()'s reverse-seek and
370          * read.  The data read will be forward ordered, however.
371          */
372         while ((error = jreadbuf(jf, &tail, sizeof(tail))) == sizeof(tail)) {
373             if (tail.endmagic != JREC_ENDMAGIC) {
374                 if (search == 0)
375                     jf_warn(jf, "bad endmagic, searching for new record");
376                 search = 1;
377                 jalign(jf);
378                 continue;
379             }
380
381             /*
382              * The actual record is 16-byte aligned.  head.recsize contains
383              * the unaligned record size.
384              */
385             recsize = (tail.recsize + 15) & ~15;
386             if (recsize < JREC_MINRECSIZE || recsize > JREC_MAXRECSIZE) {
387                 if (search == 0)
388                     jf_warn(jf, "bad recordsize: %d\n", recsize);
389                 search = 1;
390                 jalign(jf);
391                 continue;
392             }
393             allocsize = offsetof(struct jdata, jd_data[recsize]);
394             allocsize = (allocsize + 255) & ~255;
395             jd = malloc(allocsize);
396             bzero(jd, offsetof(struct jdata, jd_data[0]));
397             bcopy(&tail, jd->jd_data + recsize - sizeof(tail), sizeof(tail));
398             n = jreadbuf(jf, jd->jd_data, recsize - sizeof(tail));
399             if (n != (int)(recsize - sizeof(tail))) {
400                 if (search == 0)
401                     jf_warn(jf, "Incomplete stream record\n");
402                 search = 1;
403                 jalign(jf);
404                 free(jd);
405                 continue;
406             }
407
408             headp = (void *)jd->jd_data;
409             if (headp->begmagic != JREC_BEGMAGIC) {
410                 if (search == 0)
411                     jf_warn(jf, "bad begmagic, searching for new record");
412                 search = 1;
413                 jalign(jf);
414                 free(jd);
415                 continue;
416             }
417
418             /*
419              * note: recsize is aligned (the actual record size),
420              * head.recsize is unaligned (the actual payload size).
421              */
422             jd->jd_transid = headp->transid;
423             jd->jd_alloc = allocsize;
424             jd->jd_size = recsize;
425             jd->jd_refs = 1;
426             *jdp = jd;
427             return(0);
428         }
429     }
430
431     /*
432      * If reading in prefix mode and there is no more data, close the 
433      * current descriptor, adjust the sequence number, and loop.
434      *
435      * If we hit the end of the sequence space and were asked to loop,
436      * check for the next sequence number and adjust jf_seq_end.  Leave
437      * the current descriptor open so we do not loose track of its seek
438      * position, and also to catch a race where another jscan may have
439      * written more data to the current sequence number before rolling
440      * the next sequence number.
441      */
442     if (error == 0 && jf->jf_prefix) {
443         if (jf->jf_direction == JD_FORWARDS) {
444             if (jf->jf_seq < jf->jf_seq_end) {
445                 ++jf->jf_seq;
446                 if (verbose_opt)
447                     fprintf(stderr, "jread: roll to seq %08x\n", jf->jf_seq);
448                 if (jf->jf_fd >= 0) {
449                     close(jf->jf_fd);
450                     jf->jf_fd = -1;
451                 }
452                 goto top;
453             }
454             if (jmodes & JMODEF_LOOP_FOREVER) {
455                 asprintf(&filename, "%s.%08x", jf->jf_prefix, jf->jf_seq + 1);
456                 if (stat(filename, &st) == 0) {
457                     ++jf->jf_seq_end;
458                     if (verbose_opt)
459                         fprintf(stderr, "jread: roll seq_end to %08x\n",
460                                          jf->jf_seq_end);
461                 } else {
462                     sleep(5);
463                 }
464                 goto top;
465             }
466         } else {
467             if (jf->jf_seq > jf->jf_seq_beg) {
468                 --jf->jf_seq;
469                 if (verbose_opt)
470                     fprintf(stderr, "jread: roll to seq %08x\n", jf->jf_seq);
471                 if (jf->jf_fd >= 0) {
472                     close(jf->jf_fd);
473                     jf->jf_fd = -1;
474                 }
475                 goto top;
476             }
477         }
478     }
479
480     /*
481      * If we hit EOF and were asked to loop forever on the input, leave
482      * the current descriptor open, sleep, and loop.
483      *
484      * We have already handled the prefix case.  This feature only works
485      * when doing forward scans and the input is not a pipe.
486      */
487     if (error == 0 && (jmodes & JMODEF_LOOP_FOREVER) &&
488         !(jmodes & JMODEF_INPUT_PIPE) && jf->jf_direction == JD_FORWARDS &&
489         jf->jf_prefix == NULL
490     ) {
491         sleep(5);
492         goto top;
493     }
494
495     /*
496      * Otherwise there are no more records and we are done.
497      */
498     *jdp = NULL;
499     return(-1);
500 }
501
502 /*
503  * Write a record out.  If this is a prefix set and the file would
504  * exceed record_size, we rotate into a new sequence number.
505  */
506 void
507 jwrite(struct jfile *jf, struct jdata *jd)
508 {
509     struct stat st;
510     char *path;
511     int n;
512
513     assert(jf->jf_prefix);
514
515 again:
516     /*
517      * Open/create a new file in the prefix set
518      */
519     if (jf->jf_write_fd < 0) {
520         asprintf(&path, "%s.%08x", jf->jf_prefix, jf->jf_seq_end);
521         jf->jf_write_fd = open(path, O_RDWR|O_CREAT, 0666);
522         if (jf->jf_write_fd < 0 || fstat(jf->jf_write_fd, &st) != 0) {
523             fprintf(stderr, "Unable to open/create %s\n", path);
524             exit(1);
525         }
526         jf->jf_write_pos = st.st_size;
527         lseek(jf->jf_write_fd, jf->jf_write_pos, 0);
528         free(path);
529     }
530
531     /*
532      * Each file must contain at least one raw record, even if it exceeds
533      * the user-requested record-size.  Apart from that, we cycle to the next
534      * file when its size would exceed the user-specified 
535      */
536     if (jf->jf_write_pos > 0 && 
537         jf->jf_write_pos + jd->jd_size > prefix_file_size
538     ) {
539         close(jf->jf_write_fd);
540         jf->jf_write_fd = -1;
541         ++jf->jf_seq_end;
542         goto again;
543     }
544
545     /*
546      * Terminate if a failure occurs (for now).
547      */
548     n = write(jf->jf_write_fd, jd->jd_data, jd->jd_size);
549     if (n != jd->jd_size) {
550         ftruncate(jf->jf_write_fd, jf->jf_write_pos);
551         fprintf(stderr, "jwrite: failed %s\n", strerror(errno));
552         exit(1);
553     }
554     jf->jf_write_pos += n;
555     jf->jf_last_transid = jd->jd_transid;
556 }
557
558 /*
559  * Reset the direction and seek us to the beginning or end
560  * of the currenet file.  In prefix mode we might as well
561  * just let jsread() do it since it might have to do it 
562  * anyway.
563  */
564 static void
565 jreset(struct jfile *jf, unsigned int seq, enum jdirection direction)
566 {
567     if (jf->jf_prefix) {
568         if (jf->jf_fd >= 0) {
569             close(jf->jf_fd);
570             jf->jf_fd = -1;
571         }
572         jf->jf_pos = -1;
573         jf->jf_seq = seq;
574     } else {
575         if (direction) {
576             jf->jf_pos = lseek(jf->jf_fd, 0L, 0);
577         } else {
578             jf->jf_pos = lseek(jf->jf_fd, 0L, SEEK_END);
579         }
580     }
581     jf->jf_direction = direction;
582 }
583
584 /*
585  * Position the file such that the next jread() in the specified
586  * direction will read the record for the specified transaction id.
587  * If the transaction id does not exist the jseek will position the
588  * file at the next higher (if reading forwards) or lower (if reading
589  * backwards) transaction id.
590  *
591  * jseek is not required to be exact.  It is allowed to position the
592  * file at any point <= the transid (forwards) or >= the transid
593  * (backwards).  However, the more off jseek is, the more scanning
594  * the code will have to do to position itself properly.
595  */
596 void
597 jseek(struct jfile *jf, int64_t transid, enum jdirection direction)
598 {
599     int64_t transid_beg;
600     int64_t transid_end;
601     unsigned int seq = (unsigned int)-1;
602     struct jdata *jd;
603
604     /*
605      * If we have a prefix set search the sequence space backwards until
606      * we find the file most likely to contain the transaction id.
607      */
608     if (jf->jf_prefix) {
609         if (verbose_opt > 2) {
610             fprintf(stderr, "jseek prefix set %s %08x-%08x\n", jf->jf_prefix,
611                     jf->jf_seq_beg, jf->jf_seq_end);
612         }
613         for (seq = jf->jf_seq_end; seq != jf->jf_seq_beg - 1; --seq) {
614             jreset(jf, seq, JD_FORWARDS);
615             if (verbose_opt > 2)
616                 fprintf(stderr, "try seq %08x\n", seq);
617             if (jread(jf, &jd, JD_FORWARDS) == 0) {
618                 transid_beg = jd->jd_transid;
619                 if (verbose_opt > 2)
620                     fprintf(stderr, "transid %016llx\n", jd->jd_transid);
621                 jfree(jf, jd);
622                 if (transid_beg == transid) {
623                     jreset(jf, seq, JD_FORWARDS);
624                     break;
625                 }
626                 if (transid_beg < transid)
627                     break;
628             }
629         }
630         if (seq == jf->jf_seq_beg - 1) {
631             seq = jf->jf_seq_beg;
632         }
633         if (verbose_opt > 1)
634             fprintf(stderr, "jseek input prefix set to seq %08x\n", seq);
635     }
636
637     /*
638      * Position us within the current file.
639      */
640     jreset(jf, seq, JD_BACKWARDS);
641     while (jread(jf, &jd, JD_BACKWARDS) == 0) {
642         transid_end = jd->jd_transid;
643         jfree(jf, jd);
644
645         /*
646          * If we are at the sequence number the next forward read
647          * will re-read the record since we were going backwards.  If
648          * the caller wants to go backwards we have to go forwards one
649          * record so the caller gets the transid record when it does
650          * its first backwards read.  Confused yet?
651          *
652          * If we are at a smaller sequence number we need to read forwards
653          * by one so the next forwards read gets the first record > transid,
654          * or the next backwards read gets the first record < transid.
655          */
656         if (transid_end == transid) {
657             if (direction == JD_BACKWARDS) {
658                 if (jread(jf, &jd, JD_FORWARDS) == 0)
659                     jfree(jf, jd);
660             }
661             break;
662         }
663         if (transid_end < transid) {
664             if (jread(jf, &jd, JD_FORWARDS) == 0)
665                 jfree(jf, jd);
666             break;
667         }
668     }
669     if (verbose_opt) {
670         fprintf(stderr, "jseek %s to seq %08x offset 0x%08llx\n",
671                 jf->jf_prefix, jf->jf_seq, jf->jf_pos);
672     }
673 }
674
675 /*
676  * Data returned by jread() is persistent until released.
677  */
678 struct jdata *
679 jref(struct jdata *jd)
680 {
681     ++jd->jd_refs;
682     return(jd);
683 }
684
685 void
686 jfree(struct jfile *jf __unused, struct jdata *jd)
687 {
688     if (--jd->jd_refs == 0)
689         free(jd);
690 }
691
692 /*
693  * Align us to the next 16 byte boundary.  If scanning forwards we align
694  * forwards if not already aligned.  If scanning backwards we align
695  * backwards if not already aligned.  We only have to synchronize the
696  * seek position with the file seek position for forward scans.
697  */
698 static void
699 jalign(struct jfile *jf)
700 {
701     char dummy[16];
702     int bytes;
703     int n;
704
705     if ((int)jf->jf_pos & 15) {
706         if (jf->jf_direction == JD_FORWARDS) {
707             bytes = 16 - ((int)jf->jf_pos & 15);
708             if ((n = jreadbuf(jf, dummy, bytes)) > 0)
709                 jf->jf_pos += n;
710         } else {
711             jf->jf_pos = jf->jf_pos & ~(off_t)15;
712         }
713     }
714 }
715
716 /*
717  * Read the next raw journal record forwards or backwards and return a
718  * pointer to it.  Note that the file pointer's actual seek position does
719  * not match jf_pos in the reverse direction case.
720  */
721 static int
722 jreadbuf(struct jfile *jf, void *buf, int bytes)
723 {
724     int ttl = 0;
725     int n;
726
727     if (jf->jf_fd < 0)
728         return(0);
729
730     if (jf->jf_direction == JD_FORWARDS) {
731         while (ttl != bytes) {
732             n = read(jf->jf_fd, (char *)buf + ttl, bytes - ttl);
733             if (n <= 0) {
734                 if (n < 0 && ttl == 0)
735                     ttl = -errno;
736                 break;
737             }
738             ttl += n;
739         }
740     } else {
741         if (jf->jf_pos >= bytes) {
742             jf->jf_pos -= bytes;
743             lseek(jf->jf_fd, jf->jf_pos, 0);
744             while (ttl != bytes) {
745                 n = read(jf->jf_fd, (char *)buf + ttl, bytes - ttl);
746                 if (n <= 0) {
747                     if (n < 0 && ttl == 0)
748                         ttl = -errno;
749                     break;
750                 }
751                 ttl += n;
752             }
753         }
754     }
755     return(ttl);
756 }
757