Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "stdinc.h"
#include <fcall.h>      /* dirmodefmt */
#include "vac.h"

#pragma varargck type "t" ulong

VacFs *fs;
int tostdout;
int diff;
int nwant;
char **want;
int *found;
int chatty;
VtConn *conn;
int errors;
int settimes;
int table;

int mtimefmt(Fmt*);
void unvac(VacFile*, char*, VacDir*);

void
usage(void)
{
        fprint(2, "usage: unvac [-TVcdtv] [-h host] file.vac [file ...]\n");
        threadexitsall("usage");
}

struct
{
        vlong data;
        vlong skipdata;
} stats;

void
threadmain(int argc, char *argv[])
{
        int i, printstats;
        char *host;
        VacFile *f;

        fmtinstall('H', encodefmt);
        fmtinstall('V', vtscorefmt);
        fmtinstall('F', vtfcallfmt);
        fmtinstall('t', mtimefmt);
        fmtinstall('M', dirmodefmt);
        
        host = nil;
        printstats = 0;

        ARGBEGIN{
        case 'T':
                settimes = 1;
                break;
        case 'V':
                chattyventi = 1;
                break;
        case 'c':
                tostdout++;
                break;
        case 'd':
                diff++;
                break;
        case 'h':
                host = EARGF(usage());
                break;
        case 's':
                printstats++;
                break;
        case 't':
                table++;
                break;
        case 'v':
                chatty++;
                break;
        default:
                usage();
        }ARGEND

        if(argc < 1)
                usage();

        if(tostdout && diff){
                fprint(2, "cannot use -c with -d\n");
                usage();
        }

        conn = vtdial(host);
        if(conn == nil)
                sysfatal("could not connect to server: %r");

        if(vtconnect(conn) < 0)
                sysfatal("vtconnect: %r");

        fs = vacfsopen(conn, argv[0], VtOREAD, 128);
        if(fs == nil)
                sysfatal("vacfsopen: %r");

        nwant = argc-1;
        want = argv+1;
        found = vtmallocz(nwant*sizeof found[0]);

        if((f = vacfsgetroot(fs)) == nil)
                sysfatal("vacfsgetroot: %r");
        
        unvac(f, nil, nil);
        for(i=0; i<nwant; i++){
                if(want[i] && !found[i]){
                        fprint(2, "warning: didn't find %s\n", want[i]);
                        errors++;
                }
        }
        if(errors)
                threadexitsall("errors");
        if(printstats)
                fprint(2, "%lld bytes read, %lld bytes skipped\n",
                        stats.data, stats.skipdata);
        threadexitsall(0);
}

int
writen(int fd, char *buf, int n)
{
        int m;
        int oldn;
        
        oldn = n;
        while(n > 0){
                m = write(fd, buf, n);
                if(m <= 0)
                        return -1;
                buf += m;
                n -= m;
        }
        return oldn;
}

int
wantfile(char *name)
{
        int i, namelen, n;
        
        if(nwant == 0)
                return 1;

        namelen = strlen(name);
        for(i=0; i<nwant; i++){
                if(want[i] == nil)
                        continue;
                n = strlen(want[i]);
                if(n < namelen && name[n] == '/' && memcmp(name, want[i], n) == 0)
                        return 1;
                if(namelen < n && want[i][namelen] == '/' && memcmp(want[i], name, namelen) == 0)
                        return 1;
                if(n == namelen && memcmp(name, want[i], n) == 0){
                        found[i] = 1;
                        return 1;
                }
        }
        return 0;
}

void
unvac(VacFile *f, char *name, VacDir *vdir)
{
        static char buf[65536];
        int fd, n, m,  bsize;
        ulong mode, mode9;
        char *newname;
        char *what;
        vlong off;
        Dir d, *dp;
        VacDirEnum *vde;
        VacDir newvdir;
        VacFile *newf;

        if(vdir)
                mode = vdir->mode;
        else
                mode = vacfilegetmode(f);

        if(vdir){
                if(table){
                        if(chatty){
                                mode9 = vdir->mode&0777;
                                if(mode&ModeDir)
                                        mode9 |= DMDIR;
                                if(mode&ModeAppend)
                                        mode9 |= DMAPPEND;
                                if(mode&ModeExclusive)
                                        mode9 |= DMEXCL;
                                print("%M %-10s %-10s %11lld %t %s\n",
                                        mode9, vdir->uid, vdir->gid, vdir->size,
                                        vdir->mtime, name);
                        }else
                                print("%s%s\n", name, (mode&ModeDir) ? "/" : "");
                }
                else if(chatty)
                        fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : "");
        }

        if(mode&(ModeDevice|ModeLink|ModeNamedPipe|ModeExclusive)){
                if(table)
                        return;
                if(mode&ModeDevice)
                        what = "device";
                else if(mode&ModeLink)
                        what = "link";
                else if(mode&ModeNamedPipe)
                        what = "named pipe";
                else if(mode&ModeExclusive)
                        what = "lock";
                else
                        what = "unknown type of file";
                fprint(2, "warning: ignoring %s %s\n", what, name);
                return;
        }
        
        if(mode&ModeDir){
                if((vde = vdeopen(f)) == nil){
                        fprint(2, "vdeopen %s: %r", name);
                        errors++;
                        return;
                }
                if(!table && !tostdout && vdir){
                        // create directory
                        if((dp = dirstat(name)) == nil){
                                if((fd = create(name, OREAD, DMDIR|(mode&0777))) < 0){
                                        fprint(2, "mkdir %s: %r\n", name);
                                        vdeclose(vde);
                                }
                                close(fd);
                        }else{
                                if(!(dp->mode&DMDIR)){
                                        fprint(2, "%s already exists and is not a directory\n", name);
                                        errors++;
                                        free(dp);
                                        vdeclose(vde);
                                        return;
                                }
                                free(dp);
                        }
                }
                while(vderead(vde, &newvdir) > 0){
                        if(name == nil)
                                newname = newvdir.elem;
                        else
                                newname = smprint("%s/%s", name, newvdir.elem);
                        if(wantfile(newname)){
                                if((newf = vacfilewalk(f, newvdir.elem)) == nil){
                                        fprint(2, "walk %s: %r\n", name);
                                        errors++;
                                }else if(newf == f){
                                        fprint(2, "walk loop: %s\n", newname);
                                        vacfiledecref(newf);
                                }else{
                                        unvac(newf, newname, &newvdir);
                                        vacfiledecref(newf);
                                }
                        }
                        if(newname != newvdir.elem)
                                free(newname);
                        vdcleanup(&newvdir);
                }
                vdeclose(vde);
        }else{
                if(!table){
                        off = 0;
                        if(tostdout)
                                fd = dup(1, -1);
                        else if(diff && (fd = open(name, ORDWR)) >= 0){
                                bsize = vacfiledsize(f);
                                while((n = readn(fd, buf, bsize)) > 0){
                                        if(sha1matches(f, off/bsize, (uchar*)buf, n)){
                                                off += n;
                                                stats.skipdata += n;
                                                continue;
                                        }
                                        seek(fd, off, 0);
                                        if((m = vacfileread(f, buf, n, off)) < 0)
                                                break;
                                        if(writen(fd, buf, m) != m){
                                                fprint(2, "write %s: %r\n", name);
                                                goto Err;
                                        }
                                        off += m;
                                        stats.data += m;
                                        if(m < n){
                                                nulldir(&d);
                                                d.length = off;
                                                if(dirfwstat(fd, &d) < 0){
                                                        fprint(2, "dirfwstat %s: %r\n", name);
                                                        goto Err;
                                                }
                                                break;
                                        }
                                }
                        }
                        else if((fd = create(name, OWRITE, mode&0777)) < 0){
                                fprint(2, "create %s: %r\n", name);
                                errors++;
                                return;
                        }
                        while((n = vacfileread(f, buf, sizeof buf, off)) > 0){
                                if(writen(fd, buf, n) != n){
                                        fprint(2, "write %s: %r\n", name);
                                Err:
                                        errors++;
                                        close(fd);
                                        remove(name);
                                        return;
                                }
                                off += n;
                                stats.data += n;
                        }
                        close(fd);
                }
        }
        if(vdir && settimes && !tostdout){
                nulldir(&d);
                d.mtime = vdir->mtime;
                if(dirwstat(name, &d) < 0)
                        fprint(2, "warning: setting mtime on %s: %r", name);
        }
}

int
mtimefmt(Fmt *f)
{
        Tm *tm;
        
        tm = localtime(va_arg(f->args, ulong));
        fmtprint(f, "%04d-%02d-%02d %02d:%02d",
                tm->year+1900, tm->mon+1, tm->mday,
                tm->hour, tm->min);
        return 0;
}