shithub: clone

Download patch

ref: b5bcf2e7a3676b4dac35d73604d1ded5fba7ab5c
parent: e13af7f16f7c632d653222d58a61b2945ffe1da7
author: kvik <kvik@a-b.xyz>
date: Tue Nov 6 17:44:26 EST 2018

graceful error handling

--- a/clone.c
+++ b/clone.c
@@ -18,6 +18,7 @@
 typedef struct {
 	Dir;
 	WaitGroup wg;
+	Channel *errchan;
 	char *src, *dst;
 	int sfd, dfd;
 } File;
@@ -27,6 +28,7 @@
 	vlong offset;
 } Blk;
 
+int errors = 0;
 int multisrc = 0;
 int keepmode = 0;
 int keepmtime = 0;
@@ -51,12 +53,12 @@
 void wgwait(WaitGroup*);
 
 char *filename(char*);
-Dir *mkdir(char*, Dir*, int);
+int mkdir(char*, char*, Dir*, Dir**);
 int same(Dir*, Dir*);
 void clone(char*, char*);
-void cloneattr(int, Dir*);
+int cloneattr(int, Dir*);
 void clonedir(char*, char*);
-void clonefile(File*);
+int clonefile(File*);
 File *filenew(char*, char*, Dir*);
 void filefree(File*);
 void fileproc(void*);
@@ -70,6 +72,19 @@
 	sysfatal("usage");
 }
 
+void
+error(char *fmt, ...)
+{
+	va_list arg;
+	char err[ERRMAX];
+
+	errors = 1;
+	snprint(err, sizeof err, "%s: %s\n", argv0, fmt);
+	va_start(arg, fmt);
+	vfprint(2, err, arg);
+	va_end(arg);
+}
+
 void *
 emalloc(ulong n)
 {
@@ -145,27 +160,36 @@
 	return p + 1;
 }
 
-Dir *
-mkdir(char *name, Dir *d, int dostat)
+int
+mkdir(char *src, char *dst, Dir *sd, Dir **dd)
 {
 	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");
+	Dir d;
+	
+	if(!(sd->mode & 0400)){
+		error("can't clone directory: '%s' permission denied", src);
+		return -1;
 	}
-	close(fd);
-	return dd;
+	d = *sd;
+	d.mode = d.mode | DMDIR | 0200;
+	fd = create(dst, 0, d.mode);
+	if(fd < 0){
+		error("can't create directory: %r");
+		return -1;
+	}
+	if(cloneattr(fd, &d) < 0){
+		close(fd);
+		return -1;
+	}
+	if(dd){
+		*dd = dirfstat(fd);
+		if(*dd == nil){
+			error("can't stat: %r");
+			close(fd);
+			return -1;
+		}
+	}
+	return 1;
 }
 
 int
@@ -193,6 +217,7 @@
 	f->dst = estrdup(dst);
 	f->sfd = -1;
 	f->dfd = -1;
+	f->errchan = chancreate(sizeof(ulong), 0);
 
 	return f;
 }
@@ -208,16 +233,17 @@
 	free(f->gid);
 	free(f->src);
 	free(f->dst);
+	chanfree(f->errchan);
 	free(f);
 }
 
-void
+int
 cloneattr(int fd, Dir *d)
 {
 	Dir dd;
 
 	if(!(keepmode || keepuser || keepgroup || keepmtime))
-		return;
+		return 1;
 	nulldir(&dd);
 	if(keepmode)
 		dd.mode = d->mode & DMDIR ? d->mode|0200 : d->mode;
@@ -227,8 +253,11 @@
 		dd.uid = d->uid;
 	if(keepgroup)
 		dd.gid = d->gid;
-	if(dirfwstat(fd, &dd) < 0)
-		sysfatal("can't wstat: %r");
+	if(dirfwstat(fd, &dd) < 0){
+		error("can't wstat: %r");
+		return -1;
+	}
+	return 1;
 }
 
 void
@@ -237,18 +266,23 @@
 	Dir *sd, *dd;
 	File *f;
 	
+	dd = nil;
 	sd = dirstat(src);
 	if(sd == nil){
-		fprint(2, "clone: can't stat: %r\n");
+		error("can't stat: %r");
 		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);
+		if(dd == nil){
+			error("can't stat: %r");
+			goto End;
+		}
+	}else if(multisrc){
+		if(mkdir(src, dst, sd, &dd) < 0)
+			goto End;
+		skipdir = dd;
+	}
 
 	/* clone a file */
 	if(!(sd->mode & DMDIR)){
@@ -256,17 +290,24 @@
 			dst = smprint("%s/%s", dst, filename(src));
 		f = filenew(src, dst, sd);
 		sendp(filechan, f);
-		return;
+		goto End;
 	}
 
 	/* clone a directory */
 	if(dd)
 		dst = smprint("%s/%s", dst, filename(src));
-	if(skipdir)
-		mkdir(dst, sd, 0);
-	else
-		skipdir = mkdir(dst, sd, 1);
+	if(skipdir){
+		if(mkdir(src, dst, sd, nil) < 0)
+			goto End;
+	}else{
+		if(mkdir(src, dst, sd, &skipdir) < 0)
+			goto End;
+	}
 	clonedir(src, dst);
+
+End:
+	if(dd) free(dst);
+	free(sd); free(dd);
 }
 
 void
@@ -278,12 +319,19 @@
 	Dir *dirs, *d;
 	File *f;
 
+	dirs = nil;
+
 	fd = open(src, OREAD);
-	if(fd < 0)
-		sysfatal("can't open: %r");
+	if(fd < 0){
+		error("can't open: %r");
+		return;
+	}
 	n = dirreadall(fd, &dirs);
-	if(n < 0)
-		sysfatal("can't read directory: %r");
+	if(n < 0){
+		error("can't read directory: %r");
+		close(fd);
+		return;
+	}
 	close(fd);
 
 	for(d = dirs; n; n--, d++){
@@ -293,7 +341,8 @@
 		sn = smprint("%s/%s", src, d->name);
 		dn = smprint("%s/%s", dst, d->name);
 		if(d->mode & DMDIR){
-			mkdir(dn, d, 0);
+			if(mkdir(sn, dn, d, nil) < 0)
+				continue;
 			clonedir(sn, dn);
 		}else{
 			f = filenew(sn, dn, d);
@@ -329,20 +378,38 @@
 	return nblk;
 }
 
-void
+int
 clonefile(File *f)
 {
+	int ret;
 	vlong n;
 	Blk *blks, *b, *be;
+	enum {Anext, Aerr, Aend};
+	Alt alts[] = {
+	[Anext] {blkchan, &b, CHANSND},
+	[Aerr] {f->errchan, nil, CHANRCV},
+	[Aend] {nil, nil, CHANEND},
+	};
 
+	ret = 1;
 	n = blklist(f, &blks);
 	if(n == 0)
-		return;
-	wginit(&f->wg, n);
+		return 1;
+	wginit(&f->wg, 0);
 	for(b = blks, be = b + n; b != be; b++)
-		sendp(blkchan, b);
+		switch(alt(alts)){
+		case Anext:
+			wgadd(&f->wg, 1);
+			break;
+		case Aerr:
+			ret = -1;
+			goto End;
+		}
+End:
+	chanclose(f->errchan);
 	wgwait(&f->wg);
 	free(blks);
+	return ret;
 }
 
 void
@@ -352,6 +419,7 @@
 	long n;
 	vlong off;
 	char *buf;
+	File *f;
 	Blk *b;
 
 	buf = emalloc(blksz);
@@ -360,15 +428,20 @@
 		if(b == nil)
 			break;
 
-		sfd = b->f->sfd;
-		dfd = b->f->dfd;
+		f = b->f;
+		sfd = f->sfd;
+		dfd = 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);
+		if((n = pread(sfd, buf, blksz, off)) < 0){
+			error("can't read: %r");
+			sendul(f->errchan, ~0);
+		}
+		if(n > 0 && pwrite(dfd, buf, n, off) < n){
+			error("can't write: %r");
+			sendul(f->errchan, ~0);
+		}
+
+		wgdone(&f->wg);
 	}
 }
 
@@ -385,14 +458,19 @@
 			break;
 
 		f->sfd = open(f->src, OREAD);
-		if(f->sfd < 0)
-			sysfatal("fileproc: can't open: %r");
+		if(f->sfd < 0){
+			error("can't open: %r");
+			goto End;
+		}
 		f->dfd = create(f->dst, OWRITE, f->mode);
-		if(f->dfd < 0)
-			sysfatal("fileproc: can't create: %r");
+		if(f->dfd < 0){
+			error("can't create: %r");
+			goto End;
+		}
 
-		clonefile(f);
-		cloneattr(f->dfd, f);
+		if(clonefile(f) > 0)
+			cloneattr(f->dfd, f);
+End:
 		filefree(f);
 	}
 	wgdone(wg);
@@ -443,5 +521,7 @@
 	chanclose(filechan);
 	wgwait(&filewg);
 
+	if(errors)
+		threadexitsall("errors");
 	threadexitsall(nil);
 }
--- a/clone.man1
+++ b/clone.man1
@@ -88,14 +88,6 @@
 .IR pread(2),
 .IR pwrite(2)
 .SH BUGS
-No graceful error handling.
-An error encountered while copying a
-single file will simply axe all the
-.I clone
-processes, most certainly resulting in
-chaos. If there was an error, remove
-the destination and start all over again.
-.PP
 Preserving the modification time of
 directories does not work. Attributes are
 cloned at directory creation time; meaning, if