2 * Copyright (c) 2004,2005 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sbin/jscan/jstream.c,v 1.3 2005/07/05 02:38:34 dillon Exp $
39 static struct jhash *JHashAry[JHASH_SIZE];
41 static struct jstream *jaddrecord(struct jfile *jf, struct jstream *js);
42 static void jnormalize(struct jstream *js);
45 * Locate the next (or previous) complete virtual stream transaction given a
46 * file descriptor and direction. Keep track of partial stream records as
49 * Note that a transaction might represent a huge I/O operation, resulting
50 * in an overall node structure that spans gigabytes, but individual
51 * subrecord leaf nodes are limited in size and we depend on this to simplify
52 * the handling of leaf records.
54 * A transaction may cover several raw records. The jstream collection for
55 * a transaction is only returned when the entire transaction has been
56 * successfully scanned. Due to the interleaving of transactions the ordering
57 * of returned JS's may be different (not exactly reversed) when scanning a
58 * journal backwards verses forwards. Since parallel operations are
59 * theoretically non-conflicting, this should not present a problem.
62 jscan_stream(struct jfile *jf)
64 struct journal_rawrecbeg head;
65 struct journal_rawrecend tail;
72 * Get the current offset and make sure it is 16-byte aligned. If it
73 * isn't, align it and enter search mode.
75 if (jf->jf_pos & 15) {
76 jf_warn(jf, "realigning bad offset and entering search mode");
86 if (jf->jf_direction == JF_FORWARDS) {
88 * Scan the journal forwards. Note that the file pointer might not
91 while ((error = jread(jf, &head, sizeof(head))) == 0) {
92 if (head.begmagic != JREC_BEGMAGIC) {
94 jf_warn(jf, "bad beginmagic, searching for new record");
99 recsize = (head.recsize + 15) & ~15;
101 jf_warn(jf, "bad recordsize: %d\n", recsize);
107 js = malloc(offsetof(struct jstream, js_data[recsize]));
108 bzero(js, sizeof(struct jstream));
109 bcopy(&head, js->js_data, sizeof(head));
110 error = jread(jf, js->js_data + sizeof(head), recsize - sizeof(head));
112 jf_warn(jf, "Incomplete stream record\n");
120 * note: recsize is aligned (the actual record size),
121 * head.recsize is unaligned (the actual payload size).
123 js->js_size = head.recsize;
124 bcopy(js->js_data + recsize - sizeof(tail), &tail, sizeof(tail));
125 if (tail.endmagic != JREC_ENDMAGIC) {
126 jf_warn(jf, "bad endmagic, searching for new record");
134 if ((js = jaddrecord(jf, js)) != NULL)
139 * Scan the journal backwards. Note that jread()'s reverse-seek and
140 * read. The data read will be forward ordered, however.
142 while ((error = jread(jf, &tail, sizeof(tail))) == 0) {
143 if (tail.endmagic != JREC_ENDMAGIC) {
145 jf_warn(jf, "bad endmagic, searching for new record");
150 recsize = (tail.recsize + 15) & ~15;
152 jf_warn(jf, "bad recordsize: %d\n", recsize);
158 js = malloc(offsetof(struct jstream, js_data[recsize]));
159 bzero(js, sizeof(struct jstream));
160 bcopy(&tail, js->js_data + recsize - sizeof(tail), sizeof(tail));
161 error = jread(jf, js->js_data, recsize - sizeof(tail));
164 jf_warn(jf, "Incomplete stream record\n");
170 js->js_size = tail.recsize;
171 bcopy(js->js_data + recsize - sizeof(tail), &tail, sizeof(tail));
172 bcopy(js->js_data, &head, sizeof(head));
173 if (head.begmagic != JREC_BEGMAGIC) {
174 jf_warn(jf, "bad begmagic, searching for new record");
181 if ((js = jaddrecord(jf, js)) != NULL)
185 jf->jf_error = error;
190 * Integrate a jstream record. Deal with the transaction begin and end flags
191 * to create a forward-referenced collection of jstream records. If we are
192 * able to complete a transaction, the first js associated with that
193 * transaction is returned.
195 * XXX we need to store the data for very large multi-record transactions
196 * separately since it might not fit into memory.
198 static struct jstream *
199 jaddrecord(struct jfile *jf, struct jstream *js)
201 struct journal_rawrecbeg *head = (void *)js->js_data;
206 * Check for a completely self-contained transaction, just return the
209 if ((head->streamid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
210 (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
217 * Check for an open transaction in the hash table, create a new one
220 jhp = &JHashAry[head->streamid & JHASH_MASK];
221 while ((jh = *jhp) != NULL) {
222 if (((jh->jh_transid ^ head->streamid) & JREC_STREAMID_MASK) == 0)
227 jh = malloc(sizeof(*jh));
228 bzero(jh, sizeof(*jh));
232 jh->jh_transid = head->streamid;
237 * Emplace the stream segment
239 jh->jh_transid |= head->streamid & JREC_STREAMCTL_MASK;
240 if (jf->jf_direction == JF_FORWARDS) {
241 jh->jh_last->js_next = js;
244 js->js_next = jh->jh_first;
249 * If the transaction is complete, remove the hash entry and return the
250 * js representing the beginning of the transaction.
252 if ((jh->jh_transid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
253 (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
267 * Renormalize the jscan list to remove all the meta record headers
268 * and trailers except for the very first one.
272 jnormalize(struct jstream *js)
274 struct jstream *jscan;
277 js->js_normalized_off = 0;
278 js->js_normalized_base = js->js_data;
279 js->js_normalized_size = ((struct journal_rawrecbeg *)js->js_data)->recsize - sizeof(struct journal_rawrecend);
280 js->js_normalized_total = js->js_normalized_size;
281 off = js->js_normalized_size;
282 for (jscan = js->js_next; jscan; jscan = jscan->js_next) {
283 jscan->js_normalized_off = off;
284 jscan->js_normalized_base = jscan->js_data +
285 sizeof(struct journal_rawrecbeg);
286 jscan->js_normalized_size = jscan->js_size -
287 sizeof(struct journal_rawrecbeg) -
288 sizeof(struct journal_rawrecend);
289 off += jscan->js_normalized_size;
290 js->js_normalized_total += jscan->js_normalized_size;
295 jscan_dispose(struct jstream *js)
297 struct jstream *jnext;
299 if (js->js_alloc_buf) {
300 free(js->js_alloc_buf);
301 js->js_alloc_buf = NULL;
302 js->js_alloc_size = 0;
313 * Read the specified block of data out of a linked set of jstream
314 * structures. Returns 0 on success or an error code on error.
317 jsread(struct jstream *js, off_t off, void *buf, int bytes)
323 n = jsreadany(js, off, &ptr);
329 buf = (char *)buf + n;
337 * Read the specified block of data out of a linked set of jstream
338 * structures. Attempt to return a pointer into the data set but
339 * allocate and copy if that is not possible. Returns 0 on success
340 * or an error code on error.
343 jsreadp(struct jstream *js, off_t off, const void **bufp,
349 n = jsreadany(js, off, bufp);
351 if (js->js_alloc_size < bytes) {
352 if (js->js_alloc_buf)
353 free(js->js_alloc_buf);
354 js->js_alloc_buf = malloc(bytes);
355 js->js_alloc_size = bytes;
356 assert(js->js_alloc_buf != NULL);
358 error = jsread(js, off, js->js_alloc_buf, bytes);
362 *bufp = js->js_alloc_buf;
369 jsreadcallback(struct jstream *js, ssize_t (*func)(int, const void *, size_t),
370 int fd, off_t off, int bytes)
378 while (bytes && (n = jsreadany(js, off, &bufp)) > 0) {
381 r = func(fd, bufp, n);
394 * Return the largest contiguous buffer starting at the specified offset,
398 jsreadany(struct jstream *js, off_t off, const void **bufp)
400 struct jstream *scan;
403 if ((scan = js->js_cache) == NULL || scan->js_normalized_off > off)
405 while (scan && scan->js_normalized_off <= off) {
407 if (scan->js_normalized_off + scan->js_normalized_size > off) {
408 n = (int)(off - scan->js_normalized_off);
409 *bufp = scan->js_normalized_base + n;
410 return(scan->js_normalized_size - n);
412 scan = scan->js_next;