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.1 2005/03/07 02:38:28 dillon Exp $
39 static struct jhash *JHashAry[JHASH_SIZE];
41 static struct jstream *jaddrecord(struct jfile *jf, struct jstream *js);
44 * Locate the next (or previous) complete virtual stream transaction given a
45 * file descriptor and direction. Keep track of partial stream records as
48 * Note that a transaction might represent a huge I/O operation, resulting
49 * in an overall node structure that spans gigabytes, but individual
50 * subrecord leaf nodes are limited in size and we depend on this to simplify
51 * the handling of leaf records.
53 * A transaction may cover several raw records. The jstream collection for
54 * a transaction is only returned when the entire transaction has been
55 * successfully scanned. Due to the interleaving of transactions the ordering
56 * of returned JS's may be different (not exactly reversed) when scanning a
57 * journal backwards verses forwards. Since parallel operations are
58 * theoretically non-conflicting, this should not present a problem.
61 jscan_stream(struct jfile *jf)
63 struct journal_rawrecbeg head;
64 struct journal_rawrecend tail;
71 * Get the current offset and make sure it is 16-byte aligned. If it
72 * isn't, align it and enter search mode.
74 if (jf->jf_pos & 15) {
75 jf_warn(jf, "realigning bad offset and entering search mode");
85 if (jf->jf_direction == JF_FORWARDS) {
87 * Scan the journal forwards. Note that the file pointer might not
90 while ((error = jread(jf, &head, sizeof(head))) == 0) {
91 if (head.begmagic != JREC_BEGMAGIC) {
93 jf_warn(jf, "bad beginmagic, searching for new record");
98 recsize = (head.recsize + 15) & ~15;
100 jf_warn(jf, "bad recordsize: %d\n", recsize);
106 js = malloc(offsetof(struct jstream, js_data[recsize]));
107 bzero(js, sizeof(struct jstream));
108 bcopy(&head, js->js_data, sizeof(head));
109 error = jread(jf, js->js_data + sizeof(head), recsize - sizeof(head));
111 jf_warn(jf, "Incomplete stream record\n");
117 js->js_size = recsize;
118 bcopy(js->js_data + recsize - sizeof(tail), &tail, sizeof(tail));
119 if (tail.endmagic != JREC_ENDMAGIC) {
120 jf_warn(jf, "bad endmagic, searching for new record");
128 if ((js = jaddrecord(jf, js)) != NULL)
133 * Scan the journal backwards. Note that jread()'s reverse-seek and
134 * read. The data read will be forward ordered, however.
136 while ((error = jread(jf, &tail, sizeof(tail))) == 0) {
137 if (tail.endmagic != JREC_ENDMAGIC) {
139 jf_warn(jf, "bad endmagic, searching for new record");
144 recsize = (tail.recsize + 15) & ~15;
146 jf_warn(jf, "bad recordsize: %d\n", recsize);
152 js = malloc(offsetof(struct jstream, js_data[recsize]));
153 bzero(js, sizeof(struct jstream));
154 bcopy(&tail, js->js_data + recsize - sizeof(tail), sizeof(tail));
155 error = jread(jf, js->js_data, recsize - sizeof(tail));
158 jf_warn(jf, "Incomplete stream record\n");
164 js->js_size = recsize;
165 bcopy(js->js_data + recsize - sizeof(tail), &tail, sizeof(tail));
166 bcopy(js->js_data, &head, sizeof(head));
167 if (head.begmagic != JREC_BEGMAGIC) {
168 jf_warn(jf, "bad begmagic, searching for new record");
175 if ((js = jaddrecord(jf, js)) != NULL)
179 jf->jf_error = error;
184 * Integrate a jstream record. Deal with the transaction begin and end flags
185 * to create a forward-referenced collection of jstream records. If we are
186 * able to complete a transaction, the first js associated with that
187 * transaction is returned.
189 * XXX we need to store the data for very large multi-record transactions
190 * separately since it might not fit into memory.
192 static struct jstream *
193 jaddrecord(struct jfile *jf, struct jstream *js)
195 struct journal_rawrecbeg *head = (void *)js->js_data;
198 struct jstream *jscan;
202 * Check for a completely self-contained transaction, just return the
205 if ((head->streamid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
206 (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
212 * Check for an open transaction in the hash table, create a new one
215 jhp = &JHashAry[head->streamid & JHASH_MASK];
216 while ((jh = *jhp) != NULL) {
217 if (((jh->jh_transid ^ head->streamid) & JREC_STREAMID_MASK) == 0)
222 jh = malloc(sizeof(*jh));
223 bzero(jh, sizeof(*jh));
227 jh->jh_transid = head->streamid;
232 * Emplace the stream segment
234 jh->jh_transid |= head->streamid & JREC_STREAMCTL_MASK;
235 if (jf->jf_direction == JF_FORWARDS) {
236 jh->jh_last->js_next = js;
239 js->js_next = jh->jh_first;
244 * If the transaction is complete, remove the hash entry and return the
245 * js representing the beginning of the transaction.
247 if ((jh->jh_transid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
248 (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
254 for (jscan = js; jscan; jscan = jscan->js_next) {
256 off += jscan->js_size;
265 jscan_dispose(struct jstream *js)
267 struct jstream *jnext;
269 if (js->js_alloc_buf) {
270 free(js->js_alloc_buf);
271 js->js_alloc_buf = NULL;
272 js->js_alloc_size = 0;
283 * Read the specified block of data out of a linked set of jstream
284 * structures. Returns 0 on success or an error code on error.
287 jsread(struct jstream *js, off_t off, void *buf, int bytes)
293 n = jsreadany(js, off, &ptr);
299 buf = (char *)buf + n;
307 * Read the specified block of data out of a linked set of jstream
308 * structures. Attempt to return a pointer into the data set but
309 * allocate and copy if that is not possible. Returns 0 on success
310 * or an error code on error.
313 jsreadp(struct jstream *js, off_t off, const void **bufp,
321 n = jsreadany(js, off, bufp);
323 if (js->js_alloc_size < bytes) {
324 if (js->js_alloc_buf)
325 free(js->js_alloc_buf);
326 js->js_alloc_buf = malloc(bytes);
327 js->js_alloc_size = bytes;
329 error = jsread(js, off, js->js_alloc_buf, bytes);
333 *bufp = js->js_alloc_buf;
340 * Return the largest contiguous buffer starting at the specified offset,
344 jsreadany(struct jstream *js, off_t off, const void **bufp)
346 struct jstream *scan;
349 if ((scan = js->js_cache) == NULL || scan->js_cache_off > off)
351 while (scan && scan->js_off <= off) {
353 js->js_cache_off = scan->js_off;
354 if (scan->js_off + scan->js_size > off) {
355 n = (int)(off - scan->js_off);
356 *bufp = scan->js_data + n;
357 return(scan->js_size - n);
359 scan = scan->js_next;