235 lines
3.7 KiB
C
235 lines
3.7 KiB
C
#include "common.h"
|
|
#include "dat.h"
|
|
|
|
#define deprint(...) /* eprint(__VA_ARGS__) */
|
|
|
|
static int
|
|
delivery(char *s)
|
|
{
|
|
if(strncmp(s, "/mail/fs/", 9) == 0)
|
|
if((s = strrchr(s, '/')) && strcmp(s + 1, "mbox") == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
isdir(char *s)
|
|
{
|
|
int isdir;
|
|
Dir *d;
|
|
|
|
d = dirstat(s);
|
|
isdir = d && d->mode & DMDIR;
|
|
free(d);
|
|
return isdir;
|
|
}
|
|
|
|
static int
|
|
docreate(char *file, int perm)
|
|
{
|
|
int fd;
|
|
Dir ndir;
|
|
Dir *d;
|
|
|
|
fd = create(file, OREAD, perm);
|
|
if(fd < 0)
|
|
return -1;
|
|
d = dirfstat(fd);
|
|
if(d == nil)
|
|
return -1;
|
|
nulldir(&ndir);
|
|
ndir.mode = perm;
|
|
ndir.gid = d->uid;
|
|
dirfwstat(fd, &ndir);
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
rollup(char *s)
|
|
{
|
|
char *p;
|
|
int mode;
|
|
|
|
if(access(s, 0) == 0)
|
|
return -1;
|
|
|
|
/*
|
|
* if we can deliver to this mbox, it needs
|
|
* to be read/execable all the way down
|
|
*/
|
|
mode = 0711;
|
|
if(delivery(s))
|
|
mode = 0755;
|
|
|
|
for(p = s; p; p++) {
|
|
if(*p == '/')
|
|
continue;
|
|
p = strchr(p, '/');
|
|
if(p == 0)
|
|
break;
|
|
*p = 0;
|
|
if(access(s, 0) != 0)
|
|
if(docreate(s, DMDIR|mode) < 0)
|
|
return -1;
|
|
*p = '/';
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
copyfile(char *a, char *b, int flags)
|
|
{
|
|
char *s;
|
|
int fd, fd1, mode, i, m, n, r;
|
|
Dir *d;
|
|
|
|
mode = 0600;
|
|
if(delivery(b))
|
|
mode = 0622;
|
|
fd = open(a, OREAD);
|
|
fd1 = create(b, OWRITE|OEXCL, DMEXCL|mode);
|
|
if(fd == -1 || fd1 == -1){
|
|
close(fd);
|
|
close(fd1);
|
|
return -1;
|
|
}
|
|
s = malloc(64*1024);
|
|
i = m = 0;
|
|
while((n = read(fd, s, sizeof s)) > 0)
|
|
for(i = 0; i != n; i += m)
|
|
if((m = write(fd1, s + i, n - i)) == -1)
|
|
goto lose;
|
|
lose:
|
|
free(s);
|
|
close(fd);
|
|
close(fd1);
|
|
if(i != m || n != 0)
|
|
return -1;
|
|
|
|
if((flags & Rtrunc) == 0)
|
|
return vremove(a);
|
|
|
|
fd = open(a, ORDWR);
|
|
if(fd == -1)
|
|
return -1;
|
|
r = -1;
|
|
if(d = dirfstat(fd)){
|
|
d->length = 0;
|
|
r = dirfwstat(fd, d);
|
|
free(d);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
copydir(char *a, char *b, int flags)
|
|
{
|
|
char *p, buf[Pathlen], ns[Pathlen], owd[Pathlen];
|
|
int fd, fd1, len, i, n, r;
|
|
Dir *d;
|
|
|
|
fd = open(a, OREAD);
|
|
fd1 = create(b, OWRITE|OEXCL, DMEXCL|0777);
|
|
close(fd1);
|
|
if(fd == -1 || fd1 == -1){
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
/* fixup mode */
|
|
if(delivery(b))
|
|
if(d = dirfstat(fd)){
|
|
d->mode |= 0777;
|
|
dirfwstat(fd, d);
|
|
free(d);
|
|
}
|
|
|
|
getwd(owd, sizeof owd);
|
|
if(chdir(a) == -1)
|
|
return -1;
|
|
|
|
p = seprint(buf, buf + sizeof buf, "%s/", b);
|
|
len = buf + sizeof buf - p;
|
|
n = dirreadall(fd, &d);
|
|
r = 0;
|
|
for(i = 0; i < n; i++){
|
|
snprint(p, len, "%s", d[i].name);
|
|
if(d->mode & DMDIR){
|
|
snprint(ns, sizeof ns, "%s/%s", a, d[i].name);
|
|
r |= copydir(ns, buf, 0);
|
|
chdir(a);
|
|
}else
|
|
r |= copyfile(d[i].name, buf, 0);
|
|
if(r)
|
|
break;
|
|
}
|
|
free(d);
|
|
|
|
if((flags & Rtrunc) == 0)
|
|
r |= vremove(a);
|
|
|
|
chdir(owd);
|
|
return r;
|
|
}
|
|
|
|
int
|
|
rename(char *a, char *b, int flags)
|
|
{
|
|
char *e0, *e1;
|
|
int fd, r;
|
|
Dir *d;
|
|
|
|
e0 = strrchr(a, '/');
|
|
e1 = strrchr(b, '/');
|
|
if(!e0 || !e1 || !e1[1])
|
|
return -1;
|
|
|
|
if(e0 - a == e1 - b)
|
|
if(strncmp(a, b, e0 - a) == 0)
|
|
if(!delivery(a) || isdir(a)){
|
|
fd = open(a, OREAD);
|
|
if(!(d = dirfstat(fd))){
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
d->name = e1 + 1;
|
|
r = dirfwstat(fd, d);
|
|
deprint("rename %s %s -> %d\n", a, b, r);
|
|
if(r != -1 && flags & Rtrunc)
|
|
close(create(a, OWRITE, d->mode));
|
|
free(d);
|
|
close(fd);
|
|
return r;
|
|
}
|
|
|
|
if(rollup(b) == -1)
|
|
return -1;
|
|
if(isdir(a))
|
|
return copydir(a, b, flags);
|
|
return copyfile(a, b, flags);
|
|
}
|
|
|
|
char*
|
|
localrename(Mailbox *mb, char *p2, int flags)
|
|
{
|
|
char *path, *msg;
|
|
int r;
|
|
static char err[2*Pathlen];
|
|
|
|
path = mb->path;
|
|
msg = "rename";
|
|
if(flags & Rtrunc)
|
|
msg = "move";
|
|
deprint("localrename %s: %s %s\n", msg, path, p2);
|
|
|
|
r = rename(path, p2, flags);
|
|
if(r == -1){
|
|
snprint(err, sizeof err, "%s: can't %s\n", path, msg);
|
|
deprint("localrename %s\n", err);
|
|
return err;
|
|
}
|
|
close(r);
|
|
return 0;
|
|
}
|