ref: cd8c54c512155e589f46bffde65dadd7653f42eb
dir: /clone.c/
#include <u.h> #include <libc.h> #include <thread.h> enum { Nfileprocs = 4, Nblkprocs = 16, Blksz = 128*1024, Blkdone = 1, End = 1, }; typedef struct { char *src, *dst; int sfd, dfd; ulong mode; vlong length; Channel *c; } File; typedef struct { File *f; vlong offset; } Blk; int keepmode = 0; int blksz = Blksz; int fileprocs = Nfileprocs; int blkprocs = Nblkprocs; Dir *skipdir; Channel *filechan; /* chan(File*) */ Channel *blkchan; /* chan(Blk*) */ Channel *endchan; /* chan(ulong) */ void usage(void); void error(char*, ...); void *emalloc(ulong); char *estrdup(char*); char *filename(char*); Dir *mkdir(char*, Dir*, int); void chmod(char*, ulong); int same(Dir*, Dir*); void clone(char*, char*); void clonedir(char*, char*); void clonefile(File*); File *filenew(char*, char*, Dir*); void filefree(File*); void fileproc(void*); vlong blklist(File*, Blk**); void blkproc(void*); void usage(void) { fprint(2, "%s src dst\n", argv0); error("usage"); } void error(char *fmt, ...) { va_list arg; char err[ERRMAX]; snprint(err, sizeof err, "%s: %s: %r\n", argv0, fmt); va_start(arg, fmt); vfprint(2, err, arg); va_end(arg); threadexitsall(err); } void * emalloc(ulong n) { void *p; p = malloc(n); if(p == nil) error("out of memory"); return p; } char * estrdup(char *s) { char *p; p = strdup(s); if(p == nil) error("out of memory"); return p; } char * filename(char *s) { char *p; p = strrchr(s, '/'); if(p == nil || p == s) return s; if(p[1] == 0){ *p = 0; return filename(s); } return p + 1; } Dir * mkdir(char *name, Dir *d, int dostat) { int fd; Dir *dd; dd = nil; fd = create(name, 0, d->mode | 0200); if(fd < 0) error("can't create destination directory"); if(dostat){ dd = dirfstat(fd); if(dd == nil) error("can't stat"); } close(fd); return dd; } void chmod(char *name, ulong mode) { Dir d; nulldir(&d); d.mode = mode | 0200; if(dirwstat(name, &d) < 0) error("can't wstat"); } int same(Dir *a, Dir *b) { if(a->type == b->type && a->dev == b->dev && a->qid.path == b->qid.path && a->qid.type == b->qid.type && a->qid.vers == b->qid.vers) return 1; return 0; } File * filenew(char *src, char *dst, Dir *d) { File *f; f = emalloc(sizeof(File)); f->src = estrdup(src); f->dst = estrdup(dst); f->mode = d->mode; f->length = d->length; f->sfd = -1; f->dfd = -1; f->c = nil; return f; } void filefree(File *f) { if(f->sfd >= 0) close(f->sfd); if(f->dfd >= 0) close(f->dfd); free(f->src); free(f->dst); free(f); } void clone(char *src, char *dst) { Dir *sd, *dd; File *f; sd = dirstat(src); if(sd == nil) error("can't stat"); dd = nil; if(access(dst, AEXIST) >= 0){ dd = dirstat(dst); if(dd == nil) error("can't stat"); } /* clone a file */ if(!(sd->mode & DMDIR)){ if(dd && dd->mode & DMDIR) dst = smprint("%s/%s", dst, filename(src)); f = filenew(src, dst, sd); sendp(filechan, f); return; } /* clone a directory */ if(dd) dst = smprint("%s/%s", dst, filename(src)); skipdir = mkdir(dst, sd, 1); clonedir(src, dst); } void clonedir(char *src, char *dst) { int fd; long n; char *sn, *dn; Dir *dirs, *d; File *f; fd = open(src, OREAD); if(fd < 0) error("can't open"); n = dirreadall(fd, &dirs); if(n < 0) error("can't read directory"); close(fd); for(d = dirs; n; n--, d++){ if(d->mode & DMDIR && same(skipdir, d)) continue; sn = smprint("%s/%s", src, d->name); dn = smprint("%s/%s", dst, d->name); if(d->mode & DMDIR){ mkdir(dn, d, 0); clonedir(sn, dn); if(keepmode) chmod(dn, d->mode); }else{ f = filenew(sn, dn, d); sendp(filechan, f); } free(sn); free(dn); } free(dirs); } vlong blklist(File *f, Blk **bp) { vlong i, nblk; Blk *b, *p; if(f->length == 0) return 0; nblk = f->length / blksz; if(nblk == 0) nblk = 1; else if(nblk % blksz > 0) nblk++; b = p = emalloc(sizeof(Blk) * nblk); for(i = 0; i < nblk; i++, p++){ p->f = f; p->offset = blksz * i; } *bp = b; return nblk; } void clonefile(File *f) { vlong n, done; Blk *blks, *b, *be; enum {Anext, Adone, Aend}; Alt alts[] = { [Anext] {blkchan, &b, CHANSND}, [Adone] {f->c, nil, CHANRCV}, [Aend] {nil, nil, CHANEND}, }; n = blklist(f, &blks); if(n == 0) return; b = blks; be = blks + n; done = 0; while(done < n){ switch(alt(alts)){ case Anext: ++b; if(b == be) alts[Anext].op = CHANNOP; break; case Adone: ++done; break; } } free(blks); } void blkproc(void *) { int sfd, dfd; long n; vlong off; char *buf; Blk *b; buf = emalloc(blksz); for(;;){ b = recvp(blkchan); if(b == nil) break; sfd = b->f->sfd; dfd = b->f->dfd; off = b->offset; if((n = pread(sfd, buf, blksz, off)) < 0) error("blkproc: read error"); if(n > 0) if(pwrite(dfd, buf, n, off) < n) error("blkproc: write error"); sendul(b->f->c, Blkdone); } } void fileproc(void *) { Channel *c; File *f; c = chancreate(sizeof(ulong), blkprocs); for(;;){ f = recvp(filechan); if(f == nil){ sendul(endchan, End); return; } f->c = c; f->sfd = open(f->src, OREAD); if(f->sfd < 0) error("fileproc: can't open"); f->dfd = create(f->dst, OWRITE, f->mode); if(f->dfd < 0) error("fileproc: can't create"); clonefile(f); if(keepmode) chmod(f->dst, f->mode); filefree(f); } } void threadmain(int argc, char *argv[]) { int i; char *src, *dst, *p; ARGBEGIN{ case 'b': blksz = strtol(EARGF(usage()), nil, 0); break; case 'p': fileprocs = strtol(EARGF(usage()), &p, 0); *p++ = 0; blkprocs = strtol(p, nil, 0); break; case 'm': keepmode = 1; break; }ARGEND; if(argc < 2) usage(); src = argv[0]; dst = argv[1]; filechan = chancreate(sizeof(File*), fileprocs); blkchan = chancreate(sizeof(Blk*), blkprocs); endchan = chancreate(sizeof(ulong), 0); for(i = 0; i < fileprocs; i++) proccreate(fileproc, nil, mainstacksize); for(i = 0; i < blkprocs; i++) proccreate(blkproc, nil, mainstacksize); clone(src, dst); for(i = 0; i < fileprocs; i++){ sendp(filechan, nil); recvul(endchan); } threadexitsall(nil); }