* Fix a number of alignment errors that was causing garbage to be parsed.
[dragonfly.git] / sbin / jscan / jstream.c
CommitLineData
ce5e5ac4
MD
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 *
36d6bdee 34 * $DragonFly: src/sbin/jscan/jstream.c,v 1.3 2005/07/05 02:38:34 dillon Exp $
ce5e5ac4
MD
35 */
36
37#include "jscan.h"
38
39static struct jhash *JHashAry[JHASH_SIZE];
40
41static struct jstream *jaddrecord(struct jfile *jf, struct jstream *js);
712e03b0 42static void jnormalize(struct jstream *js);
ce5e5ac4
MD
43
44/*
45 * Locate the next (or previous) complete virtual stream transaction given a
46 * file descriptor and direction. Keep track of partial stream records as
47 * a side effect.
48 *
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.
53 *
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.
60 */
61struct jstream *
62jscan_stream(struct jfile *jf)
63{
64 struct journal_rawrecbeg head;
65 struct journal_rawrecend tail;
66 int recsize;
67 int search;
68 int error;
69 struct jstream *js;
70
71 /*
72 * Get the current offset and make sure it is 16-byte aligned. If it
73 * isn't, align it and enter search mode.
74 */
75 if (jf->jf_pos & 15) {
76 jf_warn(jf, "realigning bad offset and entering search mode");
77 jalign(jf);
78 search = 1;
79 } else {
80 search = 0;
81 }
82
83 error = 0;
84 js = NULL;
85
86 if (jf->jf_direction == JF_FORWARDS) {
87 /*
88 * Scan the journal forwards. Note that the file pointer might not
89 * be seekable.
90 */
91 while ((error = jread(jf, &head, sizeof(head))) == 0) {
92 if (head.begmagic != JREC_BEGMAGIC) {
93 if (search == 0)
94 jf_warn(jf, "bad beginmagic, searching for new record");
95 search = 1;
96 jalign(jf);
97 continue;
98 }
99 recsize = (head.recsize + 15) & ~15;
100 if (recsize <= 0) {
101 jf_warn(jf, "bad recordsize: %d\n", recsize);
102 search = 1;
103 jalign(jf);
104 continue;
105 }
106 jset(jf);
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));
111 if (error) {
112 jf_warn(jf, "Incomplete stream record\n");
113 jreturn(jf);
114 free(js);
115 js = NULL;
116 break;
117 }
36d6bdee
MD
118
119 /*
120 * note: recsize is aligned (the actual record size),
121 * head.recsize is unaligned (the actual payload size).
122 */
123 js->js_size = head.recsize;
ce5e5ac4
MD
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");
127 search = 1;
128 jreturn(jf);
129 free(js);
130 js = NULL;
131 continue;
132 }
133 jflush(jf);
134 if ((js = jaddrecord(jf, js)) != NULL)
135 break;
136 }
137 } else {
138 /*
139 * Scan the journal backwards. Note that jread()'s reverse-seek and
140 * read. The data read will be forward ordered, however.
141 */
142 while ((error = jread(jf, &tail, sizeof(tail))) == 0) {
143 if (tail.endmagic != JREC_ENDMAGIC) {
144 if (search == 0)
145 jf_warn(jf, "bad endmagic, searching for new record");
146 search = 1;
147 jalign(jf);
148 continue;
149 }
150 recsize = (tail.recsize + 15) & ~15;
151 if (recsize <= 0) {
152 jf_warn(jf, "bad recordsize: %d\n", recsize);
153 search = 1;
154 jalign(jf);
155 continue;
156 }
157 jset(jf);
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));
162
163 if (error) {
164 jf_warn(jf, "Incomplete stream record\n");
165 jreturn(jf);
166 free(js);
167 js = NULL;
168 break;
169 }
36d6bdee 170 js->js_size = tail.recsize;
ce5e5ac4
MD
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");
175 search = 1;
176 jreturn(jf);
177 free(js);
178 continue;
179 }
180 jflush(jf);
181 if ((js = jaddrecord(jf, js)) != NULL)
182 break;
183 }
184 }
185 jf->jf_error = error;
186 return(js);
187}
188
189/*
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.
194 *
195 * XXX we need to store the data for very large multi-record transactions
196 * separately since it might not fit into memory.
197 */
198static struct jstream *
199jaddrecord(struct jfile *jf, struct jstream *js)
200{
201 struct journal_rawrecbeg *head = (void *)js->js_data;
202 struct jhash *jh;
203 struct jhash **jhp;
ce5e5ac4
MD
204
205 /*
206 * Check for a completely self-contained transaction, just return the
207 * js if possible.
208 */
209 if ((head->streamid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
210 (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
211 ) {
712e03b0 212 jnormalize(js);
ce5e5ac4
MD
213 return (js);
214 }
215
216 /*
217 * Check for an open transaction in the hash table, create a new one
218 * if necessary.
219 */
220 jhp = &JHashAry[head->streamid & JHASH_MASK];
221 while ((jh = *jhp) != NULL) {
222 if (((jh->jh_transid ^ head->streamid) & JREC_STREAMID_MASK) == 0)
223 break;
224 jhp = &jh->jh_hash;
225 }
226 if (jh == NULL) {
227 jh = malloc(sizeof(*jh));
228 bzero(jh, sizeof(*jh));
229 *jhp = jh;
230 jh->jh_first = js;
231 jh->jh_last = js;
232 jh->jh_transid = head->streamid;
233 return (NULL);
234 }
235
236 /*
237 * Emplace the stream segment
238 */
239 jh->jh_transid |= head->streamid & JREC_STREAMCTL_MASK;
240 if (jf->jf_direction == JF_FORWARDS) {
241 jh->jh_last->js_next = js;
242 jh->jh_last = js;
243 } else {
244 js->js_next = jh->jh_first;
245 jh->jh_first = js;
246 }
247
248 /*
249 * If the transaction is complete, remove the hash entry and return the
250 * js representing the beginning of the transaction.
251 */
252 if ((jh->jh_transid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) ==
253 (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)
254 ) {
255 *jhp = jh->jh_hash;
256 js = jh->jh_first;
257 free(jh);
712e03b0
MD
258
259 jnormalize(js);
ce5e5ac4
MD
260 } else {
261 js = NULL;
262 }
263 return (js);
264}
265
712e03b0
MD
266/*
267 * Renormalize the jscan list to remove all the meta record headers
268 * and trailers except for the very first one.
269 */
270static
271void
272jnormalize(struct jstream *js)
273{
274 struct jstream *jscan;
275 off_t off;
276
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;
291 }
292}
293
ce5e5ac4
MD
294void
295jscan_dispose(struct jstream *js)
296{
297 struct jstream *jnext;
298
299 if (js->js_alloc_buf) {
300 free(js->js_alloc_buf);
301 js->js_alloc_buf = NULL;
302 js->js_alloc_size = 0;
303 }
304
305 while (js) {
306 jnext = js->js_next;
307 free(js);
308 js = jnext;
309 }
310}
311
312/*
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.
315 */
316int
317jsread(struct jstream *js, off_t off, void *buf, int bytes)
318{
319 const void *ptr;
320 int n;
321
322 while (bytes) {
323 n = jsreadany(js, off, &ptr);
324 if (n == 0)
325 return (ENOENT);
326 if (n > bytes)
327 n = bytes;
328 bcopy(ptr, buf, n);
329 buf = (char *)buf + n;
330 off += n;
331 bytes -= n;
332 }
333 return(0);
334}
335
336/*
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.
341 */
342int
343jsreadp(struct jstream *js, off_t off, const void **bufp,
344 int bytes)
345{
712e03b0 346 int error = 0;
ce5e5ac4 347 int n;
ce5e5ac4
MD
348
349 n = jsreadany(js, off, bufp);
350 if (n < bytes) {
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;
712e03b0 356 assert(js->js_alloc_buf != NULL);
ce5e5ac4
MD
357 }
358 error = jsread(js, off, js->js_alloc_buf, bytes);
359 if (error) {
360 *bufp = NULL;
361 } else {
362 *bufp = js->js_alloc_buf;
363 }
364 }
365 return(error);
366}
367
36d6bdee
MD
368int
369jsreadcallback(struct jstream *js, ssize_t (*func)(int, const void *, size_t),
370 int fd, off_t off, int bytes)
371{
372 const void *bufp;
373 int res;
374 int n;
375 int r;
376
377 res = 0;
378 while (bytes && (n = jsreadany(js, off, &bufp)) > 0) {
379 if (n > bytes)
380 n = bytes;
381 r = func(fd, bufp, n);
382 if (r != n) {
383 if (res == 0)
384 res = -1;
385 }
386 res += n;
387 bytes -= n;
388 off += n;
389 }
390 return(res);
391}
392
ce5e5ac4
MD
393/*
394 * Return the largest contiguous buffer starting at the specified offset,
395 * or 0.
396 */
397int
398jsreadany(struct jstream *js, off_t off, const void **bufp)
399{
400 struct jstream *scan;
401 int n;
402
36d6bdee 403 if ((scan = js->js_cache) == NULL || scan->js_normalized_off > off)
ce5e5ac4 404 scan = js;
712e03b0 405 while (scan && scan->js_normalized_off <= off) {
ce5e5ac4 406 js->js_cache = scan;
712e03b0
MD
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);
ce5e5ac4
MD
411 }
412 scan = scan->js_next;
413 }
414 return(0);
415}
416