ref: b25f9d624d037d6dc7eb1e0762cfbd941cb99ffa
dir: /clone.c/
#include <u.h> #include <libc.h> #include <thread.h> enum { Nfileprocs = 4, Nblkprocs = 16, Blksz = 128*1024, }; typedef struct { Rendez; QLock; Ref; } WaitGroup; typedef struct { Dir; WaitGroup wg; char *src, *dst; int sfd, dfd; } File; typedef struct { File *f; vlong offset; } Blk; int multisrc = 0; int keepmode = 0; int keepmtime = 0; int keepuser = 0; int keepgroup = 0; int blksz = Blksz; int fileprocs = Nfileprocs; int blkprocs = Nblkprocs; Dir *skipdir; Channel *filechan; /* chan(File*) */ Channel *blkchan; /* chan(Blk*) */ void usage(void); void *emalloc(ulong); char *estrdup(char*); extern int cas(long *p, long ov, long nv); void wginit(WaitGroup*, long); void wgadd(WaitGroup*, long); void wgdone(WaitGroup*); void wgwait(WaitGroup*); char *filename(char*); Dir *mkdir(char*, Dir*, int); int same(Dir*, Dir*); void clone(char*, char*); void cloneattr(int, Dir*); 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, "usage: %s [-gux] [-b blocksize] [-p fileprocs:blockprocs] from ... to\n", argv0); sysfatal("usage"); } void * emalloc(ulong n) { void *p; p = malloc(n); if(p == nil) sysfatal("malloc: %r"); return p; } char * estrdup(char *s) { char *p; p = strdup(s); if(p == nil) sysfatal("strdup: %r"); return p; } void wginit(WaitGroup *wg, long n) { memset(wg, 0, sizeof(*wg)); wg->l = &wg->QLock; if(cas(&wg->ref, 0, n) == 0) sysfatal("wginit: cas failed"); } void wgadd(WaitGroup *wg, long n) { long v; v = wg->ref; while(cas(&wg->ref, v, v+n) == 0) v = wg->ref; } void wgdone(WaitGroup *wg) { if(decref(wg) < 0) sysfatal("wgdone: negative WaitGroup count"); qlock(wg); rwakeupall(wg); qunlock(wg); } void wgwait(WaitGroup *wg) { qlock(wg); while(!(wg->ref == 0)) rsleep(wg); qunlock(wg); } 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 dn; Dir *dd; dd = nil; dn = *d; dn.mode = dn.mode | DMDIR | 0200; fd = create(name, 0, dn.mode); if(fd < 0) sysfatal("can't create destination directory: %r"); cloneattr(fd, &dn); if(dostat){ dd = dirfstat(fd); if(dd == nil) sysfatal("can't stat: %r"); } close(fd); return dd; } 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)); memmove(f, d, sizeof(Dir)); f->uid = estrdup(d->uid); f->gid = estrdup(d->gid); f->src = estrdup(src); f->dst = estrdup(dst); f->sfd = -1; f->dfd = -1; return f; } void filefree(File *f) { if(f->sfd >= 0) close(f->sfd); if(f->dfd >= 0) close(f->dfd); free(f->uid); free(f->gid); free(f->src); free(f->dst); free(f); } void cloneattr(int fd, Dir *d) { Dir dd; if(!(keepmode || keepuser || keepgroup || keepmtime)) return; nulldir(&dd); if(keepmode) dd.mode = d->mode & DMDIR ? d->mode|0200 : d->mode; if(keepmtime) dd.mtime = d->mtime; if(keepuser) dd.uid = d->uid; if(keepgroup) dd.gid = d->gid; if(dirfwstat(fd, &dd) < 0) sysfatal("can't wstat: %r"); } void clone(char *src, char *dst) { Dir *sd, *dd; File *f; sd = dirstat(src); if(sd == nil){ fprint(2, "clone: can't stat: %r\n"); return; } dd = nil; if(access(dst, AEXIST) >= 0){ dd = dirstat(dst); if(dd == nil) sysfatal("can't stat: %r"); }else if(multisrc) dd = skipdir = mkdir(dst, sd, 1); /* 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)); if(skipdir) mkdir(dst, sd, 0); else 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) sysfatal("can't open: %r"); n = dirreadall(fd, &dirs); if(n < 0) sysfatal("can't read directory: %r"); 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); }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; Blk *blks, *b, *be; n = blklist(f, &blks); if(n == 0) return; wginit(&f->wg, n); for(b = blks, be = b + n; b != be; b++) sendp(blkchan, b); wgwait(&f->wg); 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) sysfatal("blkproc: read error: %r"); if(n > 0) if(pwrite(dfd, buf, n, off) < n) sysfatal("blkproc: write error: %r"); wgdone(&b->f->wg); } } void fileproc(void *v) { File *f; WaitGroup *wg; wg = v; for(;;){ f = recvp(filechan); if(f == nil) break; f->sfd = open(f->src, OREAD); if(f->sfd < 0) sysfatal("fileproc: can't open: %r"); f->dfd = create(f->dst, OWRITE, f->mode); if(f->dfd < 0) sysfatal("fileproc: can't create: %r"); clonefile(f); cloneattr(f->dfd, f); filefree(f); } wgdone(wg); } void threadmain(int argc, char *argv[]) { int i; char *dst, *p; WaitGroup filewg; 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 'x': keepmode = keepmtime = 1; break; case 'u': keepuser = 1; break; case 'g': keepgroup = 1; break; }ARGEND; if(argc < 2) usage(); if(argc > 2) multisrc = 1; dst = argv[argc - 1]; filechan = chancreate(sizeof(File*), fileprocs); blkchan = chancreate(sizeof(Blk*), blkprocs); wginit(&filewg, fileprocs); for(i = 0; i < fileprocs; i++) proccreate(fileproc, &filewg, mainstacksize); for(i = 0; i < blkprocs; i++) proccreate(blkproc, nil, mainstacksize); for(i = 0; i < argc -1; i++) clone(argv[i], dst); chanclose(filechan); wgwait(&filewg); threadexitsall(nil); }