From: Matthew Dillon Date: Tue, 5 Jul 2005 00:26:03 +0000 (+0000) Subject: Major continuing work on jscan, the userland backend for the journaling X-Git-Tag: v2.0.1~6700 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/712e03b015ebcc05ff92d5102abb8bad7ba85969 Major continuing work on jscan, the userland backend for the journaling system. * Fix bugs in the stream demultiplexing code. Only the first stream header is supposed to be passed to the virtualized stream code. We were passing the stream header and trailer for each meta record and the extra meta-data really confused the virtualized stream scanning code. * Add support for unknown nesting record sizes. This occurs when the nesting record in the virtual stream is too large to fit in the kernel's in-memory journal FIFO. This allows us to arbitrarily push and pop elements of a transaction without having to know the size of the completed transaction in advance. * Use a 64 bit integer to calculate the completed transaction size. Note however that currently the stream reconstruction code uses malloc so there are serious limits to what it can reconstruct. * Implement (partial) support for mirroring. Only basic file and directory creation, write, remove, and rename is currently decoded. The TODO list is long and varied. We need to use mmap() instead of malloc() to reference journal data. We need to finish implementing mirror mode. We need a catch-up or restart mode for the mirror. We need raw journal data logging, we need stream ackknowledgements, and a thousand other things. This example will maintain a mirror of /usr in /usr_mirror in real time. Again, remember that there is a lot more work to do.... the mirroring isn't perfect by a long shot. We don't do symlinks for example, yet, and there are many other issues. # Warning: do not ^Z mountctl! cpdup /usr /usr_mirror cd /usr_mirror mountctl -a /usr:test | jscan -m stdin [does not return] [to terminate, type 'mountctl -d test' in another shell window] --- diff --git a/sbin/jscan/Makefile b/sbin/jscan/Makefile index c2a3d0a0ed..404dcf4c90 100644 --- a/sbin/jscan/Makefile +++ b/sbin/jscan/Makefile @@ -1,8 +1,9 @@ # -# $DragonFly: src/sbin/jscan/Makefile,v 1.1 2005/03/07 02:38:28 dillon Exp $ +# $DragonFly: src/sbin/jscan/Makefile,v 1.2 2005/07/05 00:26:03 dillon Exp $ PROG= jscan -SRCS= jscan.c jfile.c jstream.c subs.c dump_debug.c +SRCS= jscan.c jfile.c jstream.c subs.c dump_debug.c dump_mirror.c MAN= jscan.8 +WARNS?= 6 .include diff --git a/sbin/jscan/dump_debug.c b/sbin/jscan/dump_debug.c index 0a71b81909..4cfca45794 100644 --- a/sbin/jscan/dump_debug.c +++ b/sbin/jscan/dump_debug.c @@ -31,15 +31,14 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/dump_debug.c,v 1.2 2005/03/07 05:05:04 dillon Exp $ + * $DragonFly: src/sbin/jscan/dump_debug.c,v 1.3 2005/07/05 00:26:03 dillon Exp $ */ #include "jscan.h" static void dump_debug_stream(struct jstream *js); -static int dump_subrecord(struct jstream *js, off_t off, int recsize, int level); -static int dump_payload(int16_t rectype, struct jstream *js, off_t off, - int recsize, int level); +static int dump_debug_subrecord(struct jstream *js, off_t *off, + off_t recsize, int level); void dump_debug(struct jfile *jf) @@ -63,9 +62,10 @@ dump_debug_stream(struct jstream *js) sid = head.streamid & JREC_STREAMID_MASK; printf("STREAM %04x {\n", (int)(u_int16_t)head.streamid); if (sid >= JREC_STREAMID_JMIN && sid < JREC_STREAMID_JMAX) { - dump_subrecord(js, sizeof(head), - head.recsize - sizeof(struct journal_rawrecbeg) - - sizeof(struct journal_rawrecend), + off_t off = sizeof(head); + + dump_debug_subrecord(js, &off, + js->js_normalized_total - sizeof(head), 1); } else { switch(head.streamid & JREC_STREAMID_MASK) { @@ -90,54 +90,75 @@ dump_debug_stream(struct jstream *js) } static int -dump_subrecord(struct jstream *js, off_t off, int recsize, int level) +dump_debug_subrecord(struct jstream *js, off_t *off, off_t recsize, int level) { struct journal_subrecord sub; int payload; int subsize; int error; - int i; + off_t base = *off; error = 0; while (recsize > 0) { - if ((error = jsread(js, off, &sub, sizeof(sub))) != 0) + if ((error = jsread(js, base, &sub, sizeof(sub))) != 0) { break; + } printf("%*.*s", level * 4, level * 4, ""); - printf("@%lld ", off); + printf("@%lld ", base); printf("RECORD %s [%04x/%d]", type_to_name(sub.rectype), (int)(u_int16_t)sub.rectype, sub.recsize); - payload = sub.recsize - sizeof(sub); - subsize = (sub.recsize + 7) & ~7; + if (sub.recsize == -1) { + if ((sub.rectype & JMASK_NESTED) == 0) { + printf("Record size of -1 only works for nested records\n"); + error = -1; + break; + } + payload = 0x7FFFFFFF; + subsize = 0x7FFFFFFF; + } else { + payload = sub.recsize - sizeof(sub); + subsize = (sub.recsize + 7) & ~7; + } if (sub.rectype & JMASK_NESTED) { printf(" {\n"); - if (payload) - error = dump_subrecord(js, off + sizeof(sub), payload, level + 1); + if (payload) { + *off = base + sizeof(sub); + error = dump_debug_subrecord(js, off, payload, level + 1); + } printf("%*.*s}\n", level * 4, level * 4, ""); } else if (sub.rectype & JMASK_SUBRECORD) { printf(" DATA (%d)", payload); - error = dump_payload(sub.rectype, js, off + sizeof(sub), payload, level); + error = dump_debug_payload(sub.rectype, js, base + sizeof(sub), payload, level); + *off = base + sizeof(sub) + payload; printf("\n"); - if (error) - break; } else { - printf("[unknown content]\n", sub.recsize); + printf("[%d bytes of unknown content]\n", sub.recsize); } - if (subsize == 0) + if (error) + break; + if (sub.recsize == -1) { + recsize -= ((*off + 7) & ~7) - base; + base = (*off + 7) & ~7; + } else { + if (subsize == 0) subsize = sizeof(sub); - recsize -= subsize; - off += subsize; + recsize -= subsize; + base += subsize; + } + if (sub.rectype & JMASK_LAST) + break; } + *off = base; return(error); } -static int -dump_payload(int16_t rectype, struct jstream *js, off_t off, +int +dump_debug_payload(int16_t rectype, struct jstream *js, off_t off, int recsize, int level) { enum { DT_NONE, DT_STRING, DT_DEC, DT_HEX, DT_OCT, DT_DATA, DT_TIMESTAMP } dt = DT_DATA; const char *buf; - int didalloc; int error; int i; int j; @@ -160,6 +181,7 @@ dump_payload(int16_t rectype, struct jstream *js, off_t off, break; case JLEAF_UID: case JLEAF_GID: + case JLEAF_VTYPE: dt = DT_DEC; break; case JLEAF_MODES: @@ -233,16 +255,16 @@ dump_payload(int16_t rectype, struct jstream *js, off_t off, case DT_DEC: switch(recsize) { case 1: - printf(" %d", (int)*(u_int8_t *)buf); + printf(" %d", (int)*(const u_int8_t *)buf); break; case 2: - printf(" %d", (int)*(u_int16_t *)buf); + printf(" %d", (int)*(const u_int16_t *)buf); break; case 4: - printf(" %d", (int)*(u_int32_t *)buf); + printf(" %d", (int)*(const u_int32_t *)buf); break; case 8: - printf(" %d", (int64_t)*(u_int64_t *)buf); + printf(" %lld", (int64_t)*(const u_int64_t *)buf); break; default: printf(" ?"); @@ -252,16 +274,16 @@ dump_payload(int16_t rectype, struct jstream *js, off_t off, case DT_HEX: switch(recsize) { case 1: - printf(" 0x%02x", (int)*(u_int8_t *)buf); + printf(" 0x%02x", (int)*(const u_int8_t *)buf); break; case 2: - printf(" 0x%04x", (int)*(u_int16_t *)buf); + printf(" 0x%04x", (int)*(const u_int16_t *)buf); break; case 4: - printf(" 0x%08x", (int)*(u_int32_t *)buf); + printf(" 0x%08x", (int)*(const u_int32_t *)buf); break; case 8: - printf(" 0x%016llx", (int64_t)*(u_int64_t *)buf); + printf(" 0x%016llx", (int64_t)*(const u_int64_t *)buf); break; default: printf(" ?"); @@ -271,16 +293,16 @@ dump_payload(int16_t rectype, struct jstream *js, off_t off, case DT_OCT: switch(recsize) { case 1: - printf(" %03o", (int)*(u_int8_t *)buf); + printf(" %03o", (int)*(const u_int8_t *)buf); break; case 2: - printf(" %06o", (int)*(u_int16_t *)buf); + printf(" %06o", (int)*(const u_int16_t *)buf); break; case 4: - printf(" %011o", (int)*(u_int32_t *)buf); + printf(" %011o", (int)*(const u_int32_t *)buf); break; case 8: - printf(" %022llo", (int64_t)*(u_int64_t *)buf); + printf(" %022llo", (int64_t)*(const u_int64_t *)buf); break; default: printf(" ?"); @@ -290,7 +312,7 @@ dump_payload(int16_t rectype, struct jstream *js, off_t off, case DT_TIMESTAMP: { struct tm *tp; - time_t t = ((struct timespec *)buf)->tv_sec; + time_t t = ((const struct timespec *)buf)->tv_sec; char outbuf[64]; tp = localtime(&t); diff --git a/sbin/jscan/dump_mirror.c b/sbin/jscan/dump_mirror.c new file mode 100644 index 0000000000..13b470e0ac --- /dev/null +++ b/sbin/jscan/dump_mirror.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2005 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $DragonFly: src/sbin/jscan/dump_mirror.c,v 1.1 2005/07/05 00:26:03 dillon Exp $ + */ + +#include "jscan.h" + +static void dump_mirror_stream(struct jstream *js); +static int dump_mirror_toprecord(struct jstream *js, off_t *off, + off_t recsize, int level); +static int dump_mirror_subrecord(struct jstream *js, off_t *off, + off_t recsize, int level, struct jattr *jattr); +static int dump_mirror_payload(int16_t rectype, struct jstream *js, off_t off, + int recsize, int level, struct jattr *jattr); +static int dump_mirror_rebuild(u_int16_t rectype, struct jattr *jattr); + +void +dump_mirror(struct jfile *jf) +{ + struct jstream *js; + + while ((js = jscan_stream(jf)) != NULL) { + dump_mirror_stream(js); + jscan_dispose(js); + } +} + +static void +dump_mirror_stream(struct jstream *js) +{ + struct journal_rawrecbeg head; + int16_t sid; + mode_t save_umask; + + save_umask = umask(0); + jsread(js, 0, &head, sizeof(head)); + + sid = head.streamid & JREC_STREAMID_MASK; + if (debug_opt) + printf("STREAM %04x {\n", (int)(u_int16_t)head.streamid); + if (sid >= JREC_STREAMID_JMIN && sid < JREC_STREAMID_JMAX) { + off_t off = sizeof(head); + dump_mirror_toprecord(js, &off, js->js_normalized_total - + sizeof(struct journal_rawrecbeg), + 1); + } else { + switch(head.streamid & JREC_STREAMID_MASK) { + case JREC_STREAMID_SYNCPT & JREC_STREAMID_MASK: + if (debug_opt) + printf(" SYNCPT\n"); + break; + case JREC_STREAMID_PAD & JREC_STREAMID_MASK: + if (debug_opt) + printf(" PAD\n"); + break; + case JREC_STREAMID_DISCONT & JREC_STREAMID_MASK: + if (debug_opt) + printf(" DISCONT\n"); + break; + case JREC_STREAMID_ANNOTATE & JREC_STREAMID_MASK: + if (debug_opt) + printf(" ANNOTATION\n"); + break; + default: + if (debug_opt) + printf(" UNKNOWN\n"); + break; + } + } + umask(save_umask); + if (debug_opt) + printf("}\n"); +} + +static int +dump_mirror_toprecord(struct jstream *js, off_t *off, off_t recsize, int level) +{ + struct journal_subrecord sub; + struct jattr jattr; + int payload; + int subsize; + int error; + off_t base = *off; + + error = 0; + bzero(&jattr, sizeof(jattr)); + jattr_reset(&jattr); + + while (recsize > 0) { + if ((error = jsread(js, base, &sub, sizeof(sub))) != 0) + break; + if (debug_opt) { + printf("%*.*s", level * 4, level * 4, ""); + printf("@%lld ", base); + printf("RECORD %s [%04x/%d]", type_to_name(sub.rectype), + (int)(u_int16_t)sub.rectype, sub.recsize); + } + if (sub.recsize == -1) { + if ((sub.rectype & JMASK_NESTED) == 0) { + printf("Record size of -1 only works for nested records\n"); + error = -1; + break; + } + payload = 0x7FFFFFFF; + subsize = 0x7FFFFFFF; + } else { + payload = sub.recsize - sizeof(sub); + subsize = (sub.recsize + 7) & ~7; + } + if (sub.rectype & JMASK_NESTED) { + if (debug_opt) + printf(" {\n"); + *off = base + sizeof(sub); + error = dump_mirror_subrecord(js, off, + payload, level + 1, &jattr); + if (debug_opt) + printf("%*.*s}\n", level * 4, level * 4, ""); + } else if (sub.rectype & JMASK_SUBRECORD) { + if (debug_opt) { + printf(" DATA (%d)", payload); + error = dump_debug_payload(sub.rectype, js, base + sizeof(sub), payload, level); + } + *off = base + sizeof(sub) + payload; + if (debug_opt) + printf("\n"); + } else { + if (debug_opt) + printf("[%d bytes of unknown content]\n", sub.recsize); + } + dump_mirror_rebuild(sub.rectype, &jattr); + jattr_reset(&jattr); + if (error) + break; + if (sub.recsize == -1) { + if ((sub.rectype & JMASK_NESTED) == 0) { + printf("Record size of -1 only works for nested records\n"); + error = -1; + break; + } + recsize -= ((*off + 7) & ~7) - base; + base = (*off + 7) & ~7; + } else { + if (subsize == 0) + subsize = sizeof(sub); + recsize -= subsize; + base += subsize; + } + if (sub.rectype & JMASK_LAST) + break; + } + *off = base; + return(error); +} + +static int +dump_mirror_subrecord(struct jstream *js, off_t *off, off_t recsize, int level, + struct jattr *jattr) +{ + struct journal_subrecord sub; + int payload; + int subsize; + int error; + u_int16_t rectype; + off_t base = *off; + + error = 0; + while (recsize > 0) { + if ((error = jsread(js, base, &sub, sizeof(sub))) != 0) + break; + rectype = sub.rectype & JTYPE_MASK; + if (debug_opt) { + printf("%*.*s", level * 4, level * 4, ""); + printf("@%lld ", base); + printf("RECORD %s [%04x/%d]", type_to_name(sub.rectype), + (int)(u_int16_t)sub.rectype, sub.recsize); + } + if (sub.recsize == -1) { + payload = 0x7FFFFFFF; + subsize = 0x7FFFFFFF; + } else { + payload = sub.recsize - sizeof(sub); + subsize = (sub.recsize + 7) & ~7; + } + if (sub.rectype & JMASK_NESTED) { + if (debug_opt) + printf(" {\n"); + + /* + * Only recurse through vattr records. XXX currently assuming + * only on VATTR subrecord. + */ + *off = base + sizeof(sub); + if (payload && rectype == JTYPE_VATTR) { + error = dump_mirror_subrecord(js, off, + payload, level + 1, jattr); + } else { + error = dump_mirror_subrecord(js, off, + payload, level + 1, NULL); + } + if (debug_opt) + printf("%*.*s}\n", level * 4, level * 4, ""); + } else if (sub.rectype & JMASK_SUBRECORD) { + if (debug_opt) { + printf(" DATA (%d)", payload); + dump_debug_payload(sub.rectype, js, base + sizeof(sub), + payload, level); + } + error = dump_mirror_payload(sub.rectype, js, base + sizeof(sub), + payload, level, jattr); + *off = base + sizeof(sub) + payload; + if (debug_opt) + printf("\n"); + } else { + if (debug_opt) + printf("[%d bytes of unknown content]\n", sub.recsize); + } + if (error) + break; + if (sub.recsize == -1) { + recsize -= ((*off + 7) & ~7) - base; + base = (*off + 7) & ~7; + } else { + if (subsize == 0) + subsize = sizeof(sub); + recsize -= subsize; + base += subsize; + } + if (sub.rectype & JMASK_LAST) + break; + } + *off = base; + return(error); +} + +static int +dump_mirror_payload(int16_t rectype, struct jstream *js, off_t off, + int recsize, int level __unused, struct jattr *jattr) +{ + const char *buf; + int error; + + if (jattr == NULL) + return (0); + + error = jsreadp(js, off, (const void **)&buf, recsize); + if (error) + return (error); + + switch(rectype & ~JMASK_LAST) { + case JLEAF_PAD: + case JLEAF_ABORT: + break; + case JLEAF_FILEDATA: + jattr->data = dupdata(buf, recsize); + jattr->datalen = recsize; + break; + case JLEAF_PATH1: + jattr->path1 = dupdatapath(buf, recsize); + break; + case JLEAF_PATH2: + jattr->path2 = dupdatapath(buf, recsize); + break; + case JLEAF_PATH3: + jattr->path3 = dupdatapath(buf, recsize); + break; + case JLEAF_PATH4: + jattr->path4 = dupdatapath(buf, recsize); + break; + case JLEAF_UID: + jattr->uid = buf_to_int64(buf, recsize); + break; + case JLEAF_GID: + jattr->gid = buf_to_int64(buf, recsize); + break; + case JLEAF_VTYPE: + jattr->vtype = buf_to_int64(buf, recsize); + break; + case JLEAF_MODES: + jattr->modes = buf_to_int64(buf, recsize); + break; + case JLEAF_FFLAGS: + jattr->fflags = buf_to_int64(buf, recsize); + break; + case JLEAF_PID: + jattr->pid = buf_to_int64(buf, recsize); + break; + case JLEAF_PPID: + jattr->ppid = buf_to_int64(buf, recsize); + break; + case JLEAF_COMM: + jattr->comm = dupdatastr(buf, recsize); + break; + case JLEAF_ATTRNAME: + jattr->attrname = dupdatastr(buf, recsize); + break; + case JLEAF_PATH_REF: + jattr->pathref = dupdatapath(buf, recsize); + break; + case JLEAF_RESERVED_0F: + break; + case JLEAF_SYMLINKDATA: + jattr->data = dupdata(buf, recsize); + jattr->datalen = recsize; + break; + case JLEAF_SEEKPOS: + jattr->seekpos = buf_to_int64(buf, recsize); + break; + case JLEAF_INUM: + jattr->inum = buf_to_int64(buf, recsize); + break; + case JLEAF_NLINK: + jattr->nlink = buf_to_int64(buf, recsize); + break; + case JLEAF_FSID: + jattr->fsid = buf_to_int64(buf, recsize); + break; + case JLEAF_SIZE: + jattr->size = buf_to_int64(buf, recsize); + break; + case JLEAF_ATIME: + jattr->atime = *(const struct timeval *)buf; + break; + case JLEAF_MTIME: + jattr->mtime = *(const struct timeval *)buf; + break; + case JLEAF_CTIME: + jattr->ctime = *(const struct timeval *)buf; + break; + case JLEAF_GEN: + jattr->gen = buf_to_int64(buf, recsize); + break; + case JLEAF_FLAGS: + jattr->flags = buf_to_int64(buf, recsize); + break; + case JLEAF_UDEV: + jattr->udev = buf_to_int64(buf, recsize); + break; + case JLEAF_FILEREV: + jattr->filerev = buf_to_int64(buf, recsize); + break; + default: + break; + } + return (0); +} + +static int +dump_mirror_rebuild(u_int16_t rectype, struct jattr *jattr) +{ + int error = 0; + int fd; + +again: + switch(rectype) { + case JTYPE_SETATTR: + if (jattr->pathref) { + if (jattr->uid != (uid_t)-1) + chown(jattr->pathref, jattr->uid, -1); + if (jattr->gid != (gid_t)-1) + chown(jattr->pathref, -1, jattr->gid); + if (jattr->modes != (mode_t)-1) + chmod(jattr->pathref, jattr->modes); + if (jattr->fflags != -1) + chflags(jattr->pathref, jattr->fflags); + if (jattr->size != -1) + truncate(jattr->pathref, jattr->size); + } + break; + case JTYPE_WRITE: + case JTYPE_PUTPAGES: + if (jattr->pathref && jattr->seekpos != -1 && jattr->data) { + if ((fd = open(jattr->pathref, O_RDWR)) >= 0) { + lseek(fd, jattr->seekpos, 0); + write(fd, jattr->data, jattr->datalen); + close(fd); + } + } + break; + case JTYPE_SETACL: + break; + case JTYPE_SETEXTATTR: + break; + case JTYPE_CREATE: + /* + * note: both path1 and pathref will exist. + */ + if (jattr->path1 && jattr->modes != (mode_t)-1) { + if ((fd = open(jattr->path1, O_CREAT, jattr->modes)) >= 0) { + close(fd); + rectype = JTYPE_SETATTR; + goto again; + } + } + break; + case JTYPE_MKNOD: + break; + case JTYPE_LINK: + break; + case JTYPE_SYMLINK: + break; + case JTYPE_WHITEOUT: + break; + case JTYPE_REMOVE: + if (jattr->path1) { + remove(jattr->path1); + } + break; + case JTYPE_MKDIR: + if (jattr->path1 && jattr->modes != (mode_t)-1) { + mkdir(jattr->path1, jattr->modes); + } + break; + case JTYPE_RMDIR: + if (jattr->path1) { + rmdir(jattr->path1); + } + break; + case JTYPE_RENAME: + if (jattr->path1 && jattr->path2) { + rename(jattr->path1, jattr->path2); + } + break; + } + return(error); +} + diff --git a/sbin/jscan/jscan.c b/sbin/jscan/jattr.h similarity index 55% copy from sbin/jscan/jscan.c copy to sbin/jscan/jattr.h index 4fac95c6da..39c8108a98 100644 --- a/sbin/jscan/jscan.c +++ b/sbin/jscan/jattr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved. + * Copyright (c) 2005 The DragonFly Project. All rights reserved. * * This code is derived from software contributed to The DragonFly Project * by Matthew Dillon @@ -31,74 +31,38 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/jscan.c,v 1.2 2005/03/07 05:05:04 dillon Exp $ + * $DragonFly: src/sbin/jscan/jattr.h,v 1.1 2005/07/05 00:26:03 dillon Exp $ */ -#include "jscan.h" - -enum jmode { JS_NONE, JS_DEBUG }; - -static void usage(const char *av0); - -int -main(int ac, char **av) -{ - int fd; - int ch; - int i; - enum jdirection direction = JF_FORWARDS; - enum jmode jmode = JS_NONE; - struct jfile *jf; - - while ((ch = getopt(ac, av, "dr")) != -1) { - switch(ch) { - case 'd': - if (jmode == JS_NONE) - jmode = JS_DEBUG; - break; - case 'r': - direction = JF_BACKWARDS; - break; - default: - fprintf(stderr, "unknown option: -%c\n", optopt); - usage(av[0]); - } - } - if (jmode == JS_NONE) - usage(av[0]); - - /* - * Using specified input streams. If no files are specified, stdin - * is used. - */ - if (ac == optind) { - usage(av[0]); - } else { - for (i = optind; i < ac; ++i) { - if (strcmp(av[i], "stdin") == 0) - jf = jopen_fp(stdin, direction); - else - jf = jopen_stream(av[i], direction); - if (jf != NULL) { - switch(jmode) { - case JS_DEBUG: - dump_debug(jf); - break; - } - jclose_stream(jf); - } else { - fprintf(stderr, "Unable to open %s: %s\n", - av[i], strerror(errno)); - } - } - } - return(0); -} - -static void -usage(const char *av0) -{ - fprintf(stderr, "%s [-d] [journal files]\n", av0); - exit(1); -} +struct jattr { + struct jattr *undo; + char *path1; + char *path2; + char *path3; + char *path4; + uid_t uid; + gid_t gid; + mode_t modes; + int32_t fflags; + pid_t pid; + pid_t ppid; + char *comm; + char *attrname; + char *pathref; + char *data; + int datalen; + int64_t seekpos; + int64_t inum; + int nlink; + int64_t fsid; + int64_t size; + struct timeval atime; + struct timeval mtime; + struct timeval ctime; + int64_t filerev; + int64_t gen; + int64_t flags; + dev_t udev; + int vtype; +}; diff --git a/sbin/jscan/jfile.c b/sbin/jscan/jfile.c index 440928825e..f4efb711ea 100644 --- a/sbin/jscan/jfile.c +++ b/sbin/jscan/jfile.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/jfile.c,v 1.2 2005/03/07 05:05:04 dillon Exp $ + * $DragonFly: src/sbin/jscan/jfile.c,v 1.3 2005/07/05 00:26:03 dillon Exp $ */ #include "jscan.h" @@ -116,7 +116,7 @@ jread(struct jfile *jf, void *buf, int bytes) return (0); } else { fseeko(jf->jf_fp, jf->jf_pos, SEEK_SET); - return (errno); + return (errno ? errno : ENOENT); } } else { if (bytes > jf->jf_pos) diff --git a/sbin/jscan/jscan.8 b/sbin/jscan/jscan.8 index 52232eba35..3a24ffeaff 100644 --- a/sbin/jscan/jscan.8 +++ b/sbin/jscan/jscan.8 @@ -31,7 +31,7 @@ .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $DragonFly: src/sbin/jscan/jscan.8,v 1.1 2005/03/07 02:38:28 dillon Exp $ +.\" $DragonFly: src/sbin/jscan/jscan.8,v 1.2 2005/07/05 00:26:03 dillon Exp $ .\" .Dd March 6, 2005 .Dt JSCAN 8 @@ -61,7 +61,7 @@ until all stream segments have been collected for them. .Pp .Sh FILES .Sh SEE ALSO -.Xr mountctl 8 , +.Xr mountctl 8 .Sh BUGS .Sh CAVEATS This utility is currently under construction and not all features have been diff --git a/sbin/jscan/jscan.c b/sbin/jscan/jscan.c index 4fac95c6da..b52fb9f06c 100644 --- a/sbin/jscan/jscan.c +++ b/sbin/jscan/jscan.c @@ -31,31 +31,36 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/jscan.c,v 1.2 2005/03/07 05:05:04 dillon Exp $ + * $DragonFly: src/sbin/jscan/jscan.c,v 1.3 2005/07/05 00:26:03 dillon Exp $ */ #include "jscan.h" -enum jmode { JS_NONE, JS_DEBUG }; +enum jmode { JS_NONE, JS_DEBUG, JS_MIRROR }; static void usage(const char *av0); +int debug_opt; + int main(int ac, char **av) { - int fd; int ch; int i; enum jdirection direction = JF_FORWARDS; enum jmode jmode = JS_NONE; struct jfile *jf; - while ((ch = getopt(ac, av, "dr")) != -1) { + while ((ch = getopt(ac, av, "dmr")) != -1) { switch(ch) { case 'd': + debug_opt = 1; if (jmode == JS_NONE) jmode = JS_DEBUG; break; + case 'm': + jmode = JS_MIRROR; + break; case 'r': direction = JF_BACKWARDS; break; @@ -66,6 +71,10 @@ main(int ac, char **av) } if (jmode == JS_NONE) usage(av[0]); + if (jmode == JS_MIRROR && direction == JF_BACKWARDS) { + fprintf(stderr, "Cannot mirror in reverse scan mode\n"); + usage(av[0]); + } /* * Using specified input streams. If no files are specified, stdin @@ -81,9 +90,14 @@ main(int ac, char **av) jf = jopen_stream(av[i], direction); if (jf != NULL) { switch(jmode) { + case JS_MIRROR: + dump_mirror(jf); + break; case JS_DEBUG: - dump_debug(jf); - break; + dump_debug(jf); + break; + case JS_NONE: + break; } jclose_stream(jf); } else { @@ -98,7 +112,7 @@ main(int ac, char **av) static void usage(const char *av0) { - fprintf(stderr, "%s [-d] [journal files]\n", av0); + fprintf(stderr, "%s [-dm] [journal_file/stdin]*\n", av0); exit(1); } diff --git a/sbin/jscan/jscan.h b/sbin/jscan/jscan.h index 10183e9dae..3f964c7617 100644 --- a/sbin/jscan/jscan.h +++ b/sbin/jscan/jscan.h @@ -31,12 +31,13 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/jscan.h,v 1.2 2005/03/07 05:05:04 dillon Exp $ + * $DragonFly: src/sbin/jscan/jscan.h,v 1.3 2005/07/05 00:26:03 dillon Exp $ */ #include #include #include +#include #include #include #include @@ -46,6 +47,9 @@ #include #include #include +#include +#include +#include "jattr.h" enum jdirection { JF_FORWARDS, JF_BACKWARDS}; @@ -68,12 +72,26 @@ struct jdata { struct jstream { struct jstream *js_next; /* linked list / same transaction */ int js_size; /* amount of data, in bytes */ - off_t js_off; char *js_alloc_buf; int js_alloc_size; + + /* + * Normalized fields strip all rawrecbeg, rawrecend, and deadspace except + * for the initial rawrecbeg header. + */ + char *js_normalized_base; + int js_normalized_size; + off_t js_normalized_off; + off_t js_normalized_total; + + /* + * This is used by the first js record only to cache other records in the + * chain. + */ struct jstream *js_cache; off_t js_cache_off; - char js_data[4]; /* variable length */ + + char js_data[4]; /* variable length (original data) */ }; struct jhash { @@ -86,8 +104,16 @@ struct jhash { #define JHASH_SIZE 1024 #define JHASH_MASK (JHASH_SIZE - 1) +extern int debug_opt; + const char *type_to_name(int16_t rectype); void stringout(FILE *fp, char c, int exact); +void jattr_reset(struct jattr *jattr); +int64_t buf_to_int64(const void *buf, int bytes); +void *dupdata(const void *buf, int bytes); +char *dupdatastr(const void *buf, int bytes); +char *dupdatapath(const void *buf, int bytes); + struct jstream *jscan_stream(struct jfile *jf); void jscan_dispose(struct jstream *js); @@ -102,6 +128,9 @@ void jflush(struct jfile *jf); void jf_warn(struct jfile *jf, const char *ctl, ...); void dump_debug(struct jfile *jf); +void dump_mirror(struct jfile *jf); +int dump_debug_payload(int16_t rectype, struct jstream *js, off_t off, + int recsize, int level); int jsreadany(struct jstream *js, off_t off, const void **bufp); int jsreadp(struct jstream *js, off_t off, const void **bufp, int bytes); diff --git a/sbin/jscan/jstream.c b/sbin/jscan/jstream.c index 2f3826c49f..57e6fd5ecd 100644 --- a/sbin/jscan/jstream.c +++ b/sbin/jscan/jstream.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/jstream.c,v 1.1 2005/03/07 02:38:28 dillon Exp $ + * $DragonFly: src/sbin/jscan/jstream.c,v 1.2 2005/07/05 00:26:03 dillon Exp $ */ #include "jscan.h" @@ -39,6 +39,7 @@ static struct jhash *JHashAry[JHASH_SIZE]; static struct jstream *jaddrecord(struct jfile *jf, struct jstream *js); +static void jnormalize(struct jstream *js); /* * Locate the next (or previous) complete virtual stream transaction given a @@ -195,8 +196,6 @@ jaddrecord(struct jfile *jf, struct jstream *js) struct journal_rawrecbeg *head = (void *)js->js_data; struct jhash *jh; struct jhash **jhp; - struct jstream *jscan; - off_t off; /* * Check for a completely self-contained transaction, just return the @@ -205,6 +204,7 @@ jaddrecord(struct jfile *jf, struct jstream *js) if ((head->streamid & (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END)) == (JREC_STREAMCTL_BEGIN|JREC_STREAMCTL_END) ) { + jnormalize(js); return (js); } @@ -250,17 +250,42 @@ jaddrecord(struct jfile *jf, struct jstream *js) *jhp = jh->jh_hash; js = jh->jh_first; free(jh); - off = 0; - for (jscan = js; jscan; jscan = jscan->js_next) { - jscan->js_off = off; - off += jscan->js_size; - } + + jnormalize(js); } else { js = NULL; } return (js); } +/* + * Renormalize the jscan list to remove all the meta record headers + * and trailers except for the very first one. + */ +static +void +jnormalize(struct jstream *js) +{ + struct jstream *jscan; + off_t off; + + js->js_normalized_off = 0; + js->js_normalized_base = js->js_data; + js->js_normalized_size = ((struct journal_rawrecbeg *)js->js_data)->recsize - sizeof(struct journal_rawrecend); + js->js_normalized_total = js->js_normalized_size; + off = js->js_normalized_size; + for (jscan = js->js_next; jscan; jscan = jscan->js_next) { + jscan->js_normalized_off = off; + jscan->js_normalized_base = jscan->js_data + + sizeof(struct journal_rawrecbeg); + jscan->js_normalized_size = jscan->js_size - + sizeof(struct journal_rawrecbeg) - + sizeof(struct journal_rawrecend); + off += jscan->js_normalized_size; + js->js_normalized_total += jscan->js_normalized_size; + } +} + void jscan_dispose(struct jstream *js) { @@ -313,10 +338,8 @@ int jsreadp(struct jstream *js, off_t off, const void **bufp, int bytes) { - char *ptr; - int error; + int error = 0; int n; - int o; n = jsreadany(js, off, bufp); if (n < bytes) { @@ -325,6 +348,7 @@ jsreadp(struct jstream *js, off_t off, const void **bufp, free(js->js_alloc_buf); js->js_alloc_buf = malloc(bytes); js->js_alloc_size = bytes; + assert(js->js_alloc_buf != NULL); } error = jsread(js, off, js->js_alloc_buf, bytes); if (error) { @@ -348,13 +372,13 @@ jsreadany(struct jstream *js, off_t off, const void **bufp) if ((scan = js->js_cache) == NULL || scan->js_cache_off > off) scan = js; - while (scan && scan->js_off <= off) { + while (scan && scan->js_normalized_off <= off) { js->js_cache = scan; - js->js_cache_off = scan->js_off; - if (scan->js_off + scan->js_size > off) { - n = (int)(off - scan->js_off); - *bufp = scan->js_data + n; - return(scan->js_size - n); + js->js_cache_off = scan->js_normalized_off; + if (scan->js_normalized_off + scan->js_normalized_size > off) { + n = (int)(off - scan->js_normalized_off); + *bufp = scan->js_normalized_base + n; + return(scan->js_normalized_size - n); } scan = scan->js_next; } diff --git a/sbin/jscan/subs.c b/sbin/jscan/subs.c index 3486bf562e..fc849cf757 100644 --- a/sbin/jscan/subs.c +++ b/sbin/jscan/subs.c @@ -31,7 +31,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $DragonFly: src/sbin/jscan/subs.c,v 1.2 2005/03/07 05:05:04 dillon Exp $ + * $DragonFly: src/sbin/jscan/subs.c,v 1.3 2005/07/05 00:26:03 dillon Exp $ */ #include "jscan.h" @@ -141,6 +141,9 @@ type_to_name(int16_t rectype) case JLEAF_GID: str = "gid"; break; + case JLEAF_VTYPE: + str = "vtype"; + break; case JLEAF_MODES: str = "modes"; break; @@ -227,4 +230,117 @@ stringout(FILE *fp, char c, int exact) } } +void +jattr_reset(struct jattr *jattr) +{ + struct jattr *undo; + + if (jattr->path1) + free(jattr->path1); + if (jattr->path2) + free(jattr->path2); + if (jattr->path3) + free(jattr->path3); + if (jattr->path4) + free(jattr->path4); + if (jattr->comm) + free(jattr->comm); + if (jattr->attrname) + free(jattr->attrname); + if (jattr->pathref) + free(jattr->pathref); + if (jattr->data) + free(jattr->data); + if ((undo = jattr->undo) != NULL) + jattr_reset(jattr->undo); + bzero(jattr, sizeof(*jattr)); + jattr->undo = undo; + jattr->uid = (uid_t)-1; + jattr->gid = (gid_t)-1; + jattr->size = (off_t)-1; + jattr->modes = -1; + jattr->flags = -1; + jattr->seekpos = -1; +} + +int64_t +buf_to_int64(const void *buf, int bytes) +{ + int64_t v; + + switch(bytes) { + case 1: + v = (int64_t)*(const u_int8_t *)buf; + break; + case 2: + v = (int64_t)*(const u_int16_t *)buf; + break; + case 4: + v = (int64_t)*(const u_int32_t *)buf; + break; + case 8: + v = *(const int64_t *)buf; + break; + default: + v = 0; + } + return(v); +} + +void * +dupdata(const void *buf, int bytes) +{ + void *res; + + res = malloc(bytes); + bcopy(buf, res, bytes); + + return(res); +} + +char * +dupdatastr(const void *buf, int bytes) +{ + char *res; + + res = malloc(bytes + 1); + bcopy(buf, res, bytes); + res[bytes] = 0; + + return(res); +} + +/* + * Similar to dupdatastr() but contains sanity checks. + */ +char * +dupdatapath(const void *buf, int bytes) +{ + char *res; + char *scan; + + res = malloc(bytes + 1); + bcopy(buf, res, bytes); + res[bytes] = 0; + + if (res[0] == '/') { + fprintf(stderr, "Bad path: %s\n", res); + free(res); + return(NULL); + } + scan = res; + for (;;) { + if (scan[0] == '.' && scan[1] == '.' && + (scan[2] == 0 || scan[2] == '/') + ) { + fprintf(stderr, "Bad path: %s\n", res); + free(res); + return(NULL); + } + if ((scan = strchr(scan, '/')) == NULL) + break; + ++scan; + } + return(res); +}