Major continuing work on jscan, the userland backend for the journaling
authorMatthew Dillon <dillon@dragonflybsd.org>
Tue, 5 Jul 2005 00:26:03 +0000 (00:26 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Tue, 5 Jul 2005 00:26:03 +0000 (00:26 +0000)
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]

sbin/jscan/Makefile
sbin/jscan/dump_debug.c
sbin/jscan/dump_mirror.c [new file with mode: 0644]
sbin/jscan/jattr.h [copied from sbin/jscan/jscan.c with 55% similarity]
sbin/jscan/jfile.c
sbin/jscan/jscan.8
sbin/jscan/jscan.c
sbin/jscan/jscan.h
sbin/jscan/jstream.c
sbin/jscan/subs.c

index c2a3d0a..404dcf4 100644 (file)
@@ -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 <bsd.prog.mk>
index 0a71b81..4cfca45 100644 (file)
  * 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 (file)
index 0000000..13b470e
--- /dev/null
@@ -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 <dillon@backplane.com>
+ * 
+ * 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);
+}
+
similarity index 55%
copy from sbin/jscan/jscan.c
copy to sbin/jscan/jattr.h
index 4fac95c..39c8108 100644 (file)
@@ -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 <dillon@backplane.com>
  * 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;
+};
 
index 4409288..f4efb71 100644 (file)
@@ -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)
index 52232eb..3a24ffe 100644 (file)
@@ -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
index 4fac95c..b52fb9f 100644 (file)
  * 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);
 }
 
index 10183e9..3f964c7 100644 (file)
  * 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 <sys/types.h>
 #include <sys/time.h>
 #include <sys/journal.h>
+#include <sys/stat.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdlib.h>
@@ -46,6 +47,9 @@
 #include <time.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#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);
index 2f3826c..57e6fd5 100644 (file)
@@ -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;
     }
index 3486bf5..fc849cf 100644 (file)
@@ -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);
+}