shithub: riscv

Download patch

ref: 6118d778581b44e5c41a04a60d4aa8e6cad4a37c
parent: 4cffc0436451cb192fb041a4b2be6fa8db5eda8e
author: cinap_lenrek <[email protected]>
date: Mon Jun 24 16:09:04 EDT 2019

ape: reimplement rename() - fixing compiler warnings and handling more error cases

handle empty filename, dot and dotdot. handle mismatching from/to directory/file
type. cleanup destination file on error. error when attempting to copy non-empty
directories.

little test program:

#include <unistd.h>
#include <stdio.h>

int
main(int argc, char *argv[])
{
	if(argc != 3){
		fprintf(stderr, "usage: %s old new\n", argv[0]);
		return 1;
	}
	if(rename(argv[1], argv[2])){
		perror("rename");
		return 1;
	}
	return 0;
}

--- a/sys/src/ape/lib/ap/plan9/rename.c
+++ b/sys/src/ape/lib/ap/plan9/rename.c
@@ -10,66 +10,92 @@
 int
 rename(const char *from, const char *to)
 {
-	int n, i;
-	char *f, *t;
-	Dir *d, nd;
+	char buf[8192], *f, *t;
+	Dir *s, *d, nd;
+	int n, ffd, tfd;
 
-	if(access(to, 0) >= 0){
-		if(_REMOVE(to) < 0){
-			_syserrno();
-			return -1;
-		}
-	}
-	if((d = _dirstat(to)) != nil){
-		free(d);
-		errno = EEXIST;
+	f = strrchr(from, '/');
+	t = strrchr(to, '/');
+	f = f != nil ? f+1 : (char*)from;
+	t = t != nil ? t+1 : (char*)to;
+
+	if(*f == '\0' || strcmp(f, ".") == 0 || strcmp(f, "..") == 0
+	|| *t == '\0' || strcmp(t, ".") == 0 || strcmp(t, "..") == 0){
+		errno = EINVAL;
 		return -1;
 	}
-	if((d = _dirstat(from)) == nil){
+
+	if((s = _dirstat(from)) == nil){
 		_syserrno();
 		return -1;
 	}
-	f = strrchr(from, '/');
-	t = strrchr(to, '/');
-	f = f? f+1 : from;
-	t = t? t+1 : to;
-	n = 0;
+	if((d = _dirstat(to)) != nil){
+		if(d->qid.type == s->qid.type
+		&& d->qid.path == s->qid.path
+		&& d->qid.vers == s->qid.vers
+		&& d->type == s->type
+		&& d->dev == s->dev)
+			goto out;	/* same file */
+
+		if((d->mode ^ s->mode) & DMDIR){
+			errno = (d->mode & DMDIR) ? EISDIR : ENOTDIR;
+			goto err;
+		}
+	}
+
+	/* from and to are in same directory (we miss some cases) */
 	if(f-from==t-to && strncmp(from, to, f-from)==0){
-		/* from and to are in same directory (we miss some cases) */
-		i = strlen(t);
+		if(d != nil && _REMOVE(to) < 0){
+			_syserrno();
+			goto err;
+		}
 		_nulldir(&nd);
 		nd.name = t;
 		if(_dirwstat(from, &nd) < 0){
 			_syserrno();
-			n = -1;
+			goto err;
 		}
-	}else{
-		/* different directories: have to copy */
-		int ffd, tfd;
-		char buf[8192];
+		goto out;
+	}
 
-		if((ffd = _OPEN(from, OREAD)) < 0 ||
-		   (tfd = _CREATE(to, OWRITE, d->mode)) < 0){
-			_CLOSE(ffd);
-			_syserrno();
-			n = -1;
-		}
-		while(n>=0 && (n = _READ(ffd, buf, sizeof(buf))) > 0)
-			if(_WRITE(tfd, buf, n) != n){
-				_syserrno();
-				n = -1;
-			}
+	/* different directories: have to copy */
+	if((ffd = _OPEN(from, OREAD)) < 0){
+		_syserrno();
+		goto err;
+	}
+	if((s->mode & DMDIR) != 0 && _READ(ffd, buf, sizeof(buf)) > 0){
+		/* cannot copy non-empty directories */
+		errno = EXDEV;
 		_CLOSE(ffd);
-		_CLOSE(tfd);
-		if(n>0)
-			n = 0;
-		if(n == 0) {
-			if(_REMOVE(from) < 0){
-				_syserrno();
-				return -1;
-			}
-		}
+		goto err;
 	}
+	if(d != nil && _REMOVE(to) < 0){
+		_syserrno();
+		_CLOSE(ffd);
+		goto err;
+	}
+	if((tfd = _CREATE(to, OWRITE, s->mode)) < 0){
+		_syserrno();
+		_CLOSE(ffd);
+		goto err;
+	}
+	while((n = _READ(ffd, buf, sizeof(buf))) > 0){
+		if(_WRITE(tfd, buf, n) != n)
+			break;
+	}
+	_CLOSE(ffd);
+	_CLOSE(tfd);
+	if(n != 0 || _REMOVE(from) < 0){
+		_syserrno();
+		_REMOVE(to);	/* cleanup */
+		goto err;
+	}
+out:
+	free(s);
 	free(d);
-	return n;
+	return 0;
+err:
+	free(s);
+	free(d);
+	return -1;
 }