URI: 
       tventi/copy: synchronize with Plan 9; indent in verbose mode - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit da0a205ed60d81a85e1c71e0f31571337ba390a5
   DIR parent c0cc7cc4da05b5e0c20937a2e1097d4ec200b996
  HTML Author: Venkatesh Srinivas <extrudedaluminiu@gmail.com>
       Date:   Fri, 21 Aug 2009 15:55:56 -0400
       
       venti/copy: synchronize with Plan 9; indent in verbose mode
       
       http://codereview.appspot.com/110062
       
       Diffstat:
         M man/man1/venti.1                    |      39 +++++++++++++++++++++++++++----
         M src/cmd/venti/copy.c                |     121 ++++++++++++++++++++++++++++---
       
       2 files changed, 146 insertions(+), 14 deletions(-)
       ---
   DIR diff --git a/man/man1/venti.1 b/man/man1/venti.1
       t@@ -28,7 +28,7 @@ read, write, copy \- simple Venti clients
        .br
        .B venti/copy
        [
       -.B -fir
       +.B -fimrVv
        ]
        [
        .B -t
       t@@ -99,14 +99,35 @@ the root block from the server
        to the server
        .IR dsthost .
        .PP
       +Venti's blocks are arranged in a directed acyclic graph (see venti(6)); 
       +there may be multiple paths from a root score to an 
       +interior block (for example, if the same file contents are stored
       +under multiple names in an archive).
       +.I Copy
       +runs more efficiently if it does not copy blocks 
       +(and all their children) multiple times.
        The
        .B -f
        option causes
        .I copy
       -to run in `fast' mode,
       -assuming that if a block already exists on the
       -destination Venti server, all its children also
       -exist and need not be checked.
       +to assume that if a block already exists on the destination
       +Venti server, all its children also exist and need not be considered.
       +The
       +.B -m
       +option causes
       +.I copy
       +to maintain an in-memory list of blocks it has copied
       +and avoid considering the same block multiple times.
       +The
       +.B -f
       +option is only useful if the destination Venti server is
       +known not to have lost any blocks due to disk corruption
       +or other failures.
       +The
       +.B -m
       +option is only useful if enough memory is available to
       +hold the block list, which typically requires about 1%
       +of the total number of bytes being copied.
        .PP
        The
        .B -i
       t@@ -135,6 +156,14 @@ option is given,
        replaces pointers to unreadable blocks with
        pointers to the zero block.
        It writes the new root score to standard output.
       +The
       +.B -v
       +option prints scores as it copies them, total writes, and other
       +debugging information.
       +The
       +.B -V
       +option prints debugging information about the Venti protocol
       +messages send/received.
        .SH SOURCE
        .B \*9/src/cmd/venti
        .SH SEE ALSO
   DIR diff --git a/src/cmd/venti/copy.c b/src/cmd/venti/copy.c
       t@@ -3,6 +3,8 @@
        #include <venti.h>
        #include <libsec.h>
        #include <thread.h>
       +#include <avl.h>
       +#include <bin.h>
        
        enum
        {
       t@@ -15,25 +17,93 @@ int rewrite;
        int ignoreerrors;
        int fast;
        int verbose;
       +int nskip;
       +int nwrite;
       +
        VtConn *zsrc, *zdst;
       +uchar zeroscore[VtScoreSize];        /* all zeros */
       +
       +typedef struct ScoreTree ScoreTree;
       +struct ScoreTree
       +{
       +        Avl avl;
       +        uchar score[VtScoreSize];
       +        int type;
       +};
       +
       +Avltree *scoretree;
       +Bin *scorebin;
       +
       +static int
       +scoretreecmp(Avl *va, Avl *vb)
       +{
       +        ScoreTree *a, *b;
       +        int i;
       +
       +        a = (ScoreTree*)va;
       +        b = (ScoreTree*)vb;
       +
       +        i = memcmp(a->score, b->score, VtScoreSize);
       +        if(i != 0)
       +                return i;
       +        return a->type - b->type;
       +}
       +
       +static int
       +havevisited(uchar score[VtScoreSize], int type)
       +{
       +        ScoreTree a;
       +        
       +        if(scoretree == nil)
       +                return 0;
       +        memmove(a.score, score, VtScoreSize);
       +        a.type = type;
       +        return lookupavl(scoretree, &a.avl) != nil;
       +}
       +
       +static void
       +markvisited(uchar score[VtScoreSize], int type)
       +{
       +        ScoreTree *a;
       +        Avl *old;
       +
       +        if(scoretree == nil)
       +                return;
       +        a = binalloc(&scorebin, sizeof *a, 1);
       +        memmove(a->score, score, VtScoreSize);
       +        a->type = type;
       +        insertavl(scoretree, &a->avl, &old);
       +}
        
        void
        usage(void)
        {
       -        fprint(2, "usage: copy [-fir] [-t type] srchost dsthost score\n");
       +        fprint(2, "usage: copy [-fimrVv] [-t type] srchost dsthost score\n");
                threadexitsall("usage");
        }
        
        void
       -walk(uchar score[VtScoreSize], uint type, int base)
       +walk(uchar score[VtScoreSize], uint type, int base, int depth)
        {
                int i, n;
                uchar *buf;
       +        uchar nscore[VtScoreSize];
                VtEntry e;
                VtRoot root;
        
       -        if(memcmp(score, vtzeroscore, VtScoreSize) == 0)
       +        if(verbose){
       +                for(i = 0; i < depth; i++)
       +                        fprint(2, " ");
       +                fprint(2, "-> %d %d %d %V\n", depth, type, base, score);
       +        }
       +
       +        if(memcmp(score, vtzeroscore, VtScoreSize) == 0 || memcmp(score, zeroscore, VtScoreSize) == 0)
       +                return;
       +        
       +        if(havevisited(score, type)){
       +                nskip++;
                        return;
       +        }
        
                buf = vtmallocz(VtMaxLumpSize);
                if(fast && vtread(zdst, score, type, buf, VtMaxLumpSize) >= 0){
       t@@ -59,8 +129,8 @@ walk(uchar score[VtScoreSize], uint type, int base)
                                fprint(2, "warning: could not unpack root in %V %d\n", score, type);
                                break;
                        }
       -                walk(root.score, VtDirType, 0);
       -                walk(root.prev, VtRootType, 0);
       +                walk(root.prev, VtRootType, 0, depth+1);
       +                walk(root.score, VtDirType, 0, depth+1);
                        if(rewrite)
                                vtrootpack(&root, buf);        /* walk might have changed score */
                        break;
       t@@ -73,7 +143,14 @@ walk(uchar score[VtScoreSize], uint type, int base)
                                }
                                if(!(e.flags & VtEntryActive))
                                        continue;
       -                        walk(e.score, e.type, e.type&VtTypeBaseMask);
       +                        walk(e.score, e.type, e.type&VtTypeBaseMask, depth+1);
       +                        /*
       +                         * Don't repack unless we're rewriting -- some old 
       +                         * vac files have psize==0 and dsize==0, and these
       +                         * get rewritten by vtentryunpack to have less strange
       +                         * block sizes.  So vtentryunpack; vtentrypack does not
       +                         * guarantee to preserve the exact bytes in buf.
       +                         */
                                if(rewrite)
                                        vtentrypack(&e, buf, i);
                        }
       t@@ -85,17 +162,31 @@ walk(uchar score[VtScoreSize], uint type, int base)
                default:        /* pointers */
                        for(i=0; i<n; i+=VtScoreSize)
                                if(memcmp(buf+i, vtzeroscore, VtScoreSize) != 0)
       -                                walk(buf+i, type-1, base);
       +                                walk(buf+i, type-1, base, depth+1);
                        break;
                }
        
       -        if(vtwrite(zdst, score, type, buf, n) < 0){
       +        nwrite++;
       +        if(vtwrite(zdst, nscore, type, buf, n) < 0){
                        /* figure out score for better error message */
                        /* can't use input argument - might have changed contents */
                        n = vtzerotruncate(type, buf, n);
                        sha1(buf, n, score, nil);
                        sysfatal("writing block %V (type %d): %r", score, type);
                }
       +        if(!rewrite && memcmp(score, nscore, VtScoreSize) != 0)
       +                sysfatal("not rewriting: wrote %V got %V", score, nscore);        
       +
       +        if((type !=0 || base !=0) && verbose){
       +                n = vtzerotruncate(type, buf, n);
       +                sha1(buf, n, score, nil);
       +
       +                for(i = 0; i < depth; i++)
       +                        fprint(2, " ");
       +                fprint(2, "<- %V\n", score);
       +        }
       +        
       +        markvisited(score, type);
                free(buf);
        }
        
       t@@ -112,6 +203,9 @@ threadmain(int argc, char *argv[])
        
                type = -1;
                ARGBEGIN{
       +        case 'V':
       +                chattyventi++;
       +                break;
                case 'f':
                        fast = 1;
                        break;
       t@@ -120,6 +214,9 @@ threadmain(int argc, char *argv[])
                                usage();
                        ignoreerrors = 1;
                        break;
       +        case 'm':
       +                scoretree = mkavltree(scoretreecmp);
       +                break;
                case 'r':
                        if(ignoreerrors)
                                usage();
       t@@ -128,6 +225,9 @@ threadmain(int argc, char *argv[])
                case 't':
                        type = atoi(EARGF(usage()));
                        break;
       +        case 'v':
       +                verbose = 1;
       +                break;
                default:
                        usage();
                        break;
       t@@ -167,10 +267,13 @@ threadmain(int argc, char *argv[])
                                sysfatal("could not find block %V of any type", score);
                }
        
       -        walk(score, type, VtDirType);
       +        walk(score, type, VtDirType, 0);
                if(changes)
                        print("%s:%V (%d pointers rewritten)\n", prefix, score, changes);
        
       +        if(verbose)
       +                print("%d skipped, %d written\n", nskip, nwrite);
       +
                if(vtsync(zdst) < 0)
                        sysfatal("could not sync dst server: %r");