ref: cd8c54c512155e589f46bffde65dadd7653f42eb
author: kvik <kvik@a-b.xyz>
date: Tue Oct 30 11:10:15 EDT 2018
import
--- /dev/null
+++ b/clone.c
@@ -1,0 +1,409 @@
+#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);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+TARG=clone
+OFILES=clone.$O
+BIN=/$objtype/bin/
+
+</sys/src/cmd/mkone