shithub: clone

Download patch

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