diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-06-24 20:09:04 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-06-24 20:09:04 +0200 |
commit | 6118d778581b44e5c41a04a60d4aa8e6cad4a37c (patch) | |
tree | 8e1ddd9d522edcc86a3a3b9759af7fde8ca33567 /sys/src/ape | |
parent | 4cffc0436451cb192fb041a4b2be6fa8db5eda8e (diff) |
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;
}
Diffstat (limited to 'sys/src/ape')
-rw-r--r-- | sys/src/ape/lib/ap/plan9/rename.c | 118 |
1 files changed, 72 insertions, 46 deletions
diff --git a/sys/src/ape/lib/ap/plan9/rename.c b/sys/src/ape/lib/ap/plan9/rename.c index 182f49ce2..ae2736d71 100644 --- 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 0; +err: + free(s); free(d); - return n; + return -1; } |