#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; }