don't redefine _PATH_TMP; don't assume it begins "/tmp/";
[unix-history] / usr / src / usr.bin / rdist / server.c
index e02cb91..534bf09 100644 (file)
@@ -1,6 +1,13 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * %sccs.include.redist.c%
+ */
+
 #ifndef lint
 #ifndef lint
-static char *sccsid = "@(#)server.c    4.20 (Berkeley) 84/09/21";
-#endif
+static char sccsid[] = "@(#)server.c   5.13 (Berkeley) %G%";
+#endif /* not lint */
 
 #include "defs.h"
 
 
 #include "defs.h"
 
@@ -11,6 +18,7 @@ struct        linkbuf *ihead;         /* list of files with more than one link */
 char   buf[BUFSIZ];            /* general purpose buffer */
 char   target[BUFSIZ];         /* target/source directory name */
 char   *tp;                    /* pointer to end of target name */
 char   buf[BUFSIZ];            /* general purpose buffer */
 char   target[BUFSIZ];         /* target/source directory name */
 char   *tp;                    /* pointer to end of target name */
+char   *Tdest;                 /* pointer to last T dest*/
 int    catname;                /* cat name to target name */
 char   *stp[32];               /* stack of saved tp's for directories */
 int    oumask;                 /* old umask for creating files */
 int    catname;                /* cat name to target name */
 char   *stp[32];               /* stack of saved tp's for directories */
 int    oumask;                 /* old umask for creating files */
@@ -180,6 +188,7 @@ install(src, dest, destdir, opts)
        int destdir, opts;
 {
        char *rname;
        int destdir, opts;
 {
        char *rname;
+       char destcopy[BUFSIZ];
 
        if (dest == NULL) {
                opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
 
        if (dest == NULL) {
                opts &= ~WHOLE; /* WHOLE mode only useful if renaming */
@@ -230,9 +239,16 @@ install(src, dest, destdir, opts)
        if (response() < 0)
                return;
 
        if (response() < 0)
                return;
 
+       if (destdir) {
+               strcpy(destcopy, dest);
+               Tdest = destcopy;
+       }
        sendf(rname, opts);
        sendf(rname, opts);
+       Tdest = 0;
 }
 
 }
 
+#define protoname() (pw ? pw->pw_name : user)
+#define protogroup() (gr ? gr->gr_name : group)
 /*
  * Transfer the file or directory in target[].
  * rname is the name of the file on the remote host.
 /*
  * Transfer the file or directory in target[].
  * rname is the name of the file on the remote host.
@@ -249,15 +265,15 @@ sendf(rname, opts)
        struct direct *dp;
        char *otp, *cp;
        extern struct subcmd *subcmds;
        struct direct *dp;
        char *otp, *cp;
        extern struct subcmd *subcmds;
+       static char user[15], group[15];
 
        if (debug)
                printf("sendf(%s, %x)\n", rname, opts);
 
        if (except(target))
                return;
 
        if (debug)
                printf("sendf(%s, %x)\n", rname, opts);
 
        if (except(target))
                return;
-       if (access(target, 4) < 0 ||
-           (opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
-               error("%s: %s\n", target, sys_errlist[errno]);
+       if ((opts & FOLLOW ? stat(target, &stb) : lstat(target, &stb)) < 0) {
+               error("%s: %s\n", target, strerror(errno));
                return;
        }
        if ((u = update(rname, opts, &stb)) == 0) {
                return;
        }
        if ((u = update(rname, opts, &stb)) == 0) {
@@ -268,14 +284,17 @@ sendf(rname, opts)
 
        if (pw == NULL || pw->pw_uid != stb.st_uid)
                if ((pw = getpwuid(stb.st_uid)) == NULL) {
 
        if (pw == NULL || pw->pw_uid != stb.st_uid)
                if ((pw = getpwuid(stb.st_uid)) == NULL) {
-                       error("%s: no password entry for uid %d\n", target,
-                               stb.st_uid);
-                       return;
+                       log(lfp, "%s: no password entry for uid %d \n",
+                               target, stb.st_uid);
+                       pw = NULL;
+                       sprintf(user, ":%d", stb.st_uid);
                }
        if (gr == NULL || gr->gr_gid != stb.st_gid)
                if ((gr = getgrgid(stb.st_gid)) == NULL) {
                }
        if (gr == NULL || gr->gr_gid != stb.st_gid)
                if ((gr = getgrgid(stb.st_gid)) == NULL) {
-                       error("%s: no name for group %d\n", target, stb.st_gid);
-                       return;
+                       log(lfp, "%s: no name for group %d\n",
+                               target, stb.st_gid);
+                       gr = NULL;
+                       sprintf(group, ":%d", stb.st_gid);
                }
        if (u == 1) {
                if (opts & VERIFY) {
                }
        if (u == 1) {
                if (opts & VERIFY) {
@@ -289,11 +308,11 @@ sendf(rname, opts)
        switch (stb.st_mode & S_IFMT) {
        case S_IFDIR:
                if ((d = opendir(target)) == NULL) {
        switch (stb.st_mode & S_IFMT) {
        case S_IFDIR:
                if ((d = opendir(target)) == NULL) {
-                       error("%s: %s\n", target, sys_errlist[errno]);
+                       error("%s: %s\n", target, strerror(errno));
                        return;
                }
                (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
                        return;
                }
                (void) sprintf(buf, "D%o %04o 0 0 %s %s %s\n", opts,
-                       stb.st_mode & 07777, pw->pw_name, gr->gr_name, rname);
+                       stb.st_mode & 07777, protoname(), protogroup(), rname);
                if (debug)
                        printf("buf = %s", buf);
                (void) write(rem, buf, strlen(buf));
                if (debug)
                        printf("buf = %s", buf);
                (void) write(rem, buf, strlen(buf));
@@ -334,9 +353,27 @@ sendf(rname, opts)
        case S_IFLNK:
                if (u != 1)
                        opts |= COMPARE;
        case S_IFLNK:
                if (u != 1)
                        opts |= COMPARE;
-               (void) sprintf(buf, "K%o %o %D %D %s %s %s\n", opts,
+               if (stb.st_nlink > 1) {
+                       struct linkbuf *lp;
+
+                       if ((lp = savelink(&stb)) != NULL) {
+                               /* install link */
+                               if (*lp->target == 0)
+                               (void) sprintf(buf, "k%o %s %s\n", opts,
+                                       lp->pathname, rname);
+                               else
+                               (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+                                       lp->target, lp->pathname, rname);
+                               if (debug)
+                                       printf("buf = %s", buf);
+                               (void) write(rem, buf, strlen(buf));
+                               (void) response();
+                               return;
+                       }
+               }
+               (void) sprintf(buf, "K%o %o %ld %ld %s %s %s\n", opts,
                        stb.st_mode & 07777, stb.st_size, stb.st_mtime,
                        stb.st_mode & 07777, stb.st_size, stb.st_mtime,
-                       pw->pw_name, gr->gr_name, rname);
+                       protoname(), protogroup(), rname);
                if (debug)
                        printf("buf = %s", buf);
                (void) write(rem, buf, strlen(buf));
                if (debug)
                        printf("buf = %s", buf);
                (void) write(rem, buf, strlen(buf));
@@ -345,7 +382,7 @@ sendf(rname, opts)
                sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
                (void) write(rem, buf, stb.st_size);
                if (debug)
                sizerr = (readlink(target, buf, BUFSIZ) != stb.st_size);
                (void) write(rem, buf, stb.st_size);
                if (debug)
-                       printf("readlink = %.*s\n", stb.st_size, buf);
+                       printf("readlink = %.*s\n", (int)stb.st_size, buf);
                goto done;
 
        case S_IFREG:
                goto done;
 
        case S_IFREG:
@@ -369,8 +406,12 @@ sendf(rname, opts)
 
                if ((lp = savelink(&stb)) != NULL) {
                        /* install link */
 
                if ((lp = savelink(&stb)) != NULL) {
                        /* install link */
+                       if (*lp->target == 0)
                        (void) sprintf(buf, "k%o %s %s\n", opts,
                                lp->pathname, rname);
                        (void) sprintf(buf, "k%o %s %s\n", opts,
                                lp->pathname, rname);
+                       else
+                       (void) sprintf(buf, "k%o %s/%s %s\n", opts,
+                               lp->target, lp->pathname, rname);
                        if (debug)
                                printf("buf = %s", buf);
                        (void) write(rem, buf, strlen(buf));
                        if (debug)
                                printf("buf = %s", buf);
                        (void) write(rem, buf, strlen(buf));
@@ -380,12 +421,12 @@ sendf(rname, opts)
        }
 
        if ((f = open(target, 0)) < 0) {
        }
 
        if ((f = open(target, 0)) < 0) {
-               error("%s: %s\n", target, sys_errlist[errno]);
+               error("%s: %s\n", target, strerror(errno));
                return;
        }
                return;
        }
-       (void) sprintf(buf, "R%o %o %D %D %s %s %s\n", opts,
+       (void) sprintf(buf, "R%o %o %ld %ld %s %s %s\n", opts,
                stb.st_mode & 07777, stb.st_size, stb.st_mtime,
                stb.st_mode & 07777, stb.st_size, stb.st_mtime,
-               pw->pw_name, gr->gr_name, rname);
+               protoname(), protogroup(), rname);
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
@@ -416,12 +457,12 @@ dospecial:
        for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
                if (sc->sc_type != SPECIAL)
                        continue;
        for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
                if (sc->sc_type != SPECIAL)
                        continue;
-               if (!inlist(sc->sc_args, target))
+               if (sc->sc_args != NULL && !inlist(sc->sc_args, target))
                        continue;
                log(lfp, "special \"%s\"\n", sc->sc_name);
                if (opts & VERIFY)
                        continue;
                        continue;
                log(lfp, "special \"%s\"\n", sc->sc_name);
                if (opts & VERIFY)
                        continue;
-               (void) sprintf(buf, "S%s\n", sc->sc_name);
+               (void) sprintf(buf, "SFILE=%s;%s\n", target, sc->sc_name);
                if (debug)
                        printf("buf = %s", buf);
                (void) write(rem, buf, strlen(buf));
                if (debug)
                        printf("buf = %s", buf);
                (void) write(rem, buf, strlen(buf));
@@ -452,6 +493,10 @@ savelink(stp)
                lp->devnum = stp->st_dev;
                lp->count = stp->st_nlink - 1;
                strcpy(lp->pathname, target);
                lp->devnum = stp->st_dev;
                lp->count = stp->st_nlink - 1;
                strcpy(lp->pathname, target);
+               if (Tdest)
+                       strcpy(lp->target, Tdest);
+               else
+                       *lp->target = 0;
        }
        return(NULL);
 }
        }
        return(NULL);
 }
@@ -480,6 +525,7 @@ update(rname, opts, stp)
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
        if (debug)
                printf("buf = %s", buf);
        (void) write(rem, buf, strlen(buf));
+again:
        cp = s = buf;
        do {
                if (read(rem, cp, 1) != 1)
        cp = s = buf;
        do {
                if (read(rem, cp, 1) != 1)
@@ -505,9 +551,15 @@ update(rname, opts, stp)
                }
                return(0);
 
                }
                return(0);
 
+       case '\3':
+               *--cp = '\0';
+               if (lfp != NULL) 
+                       log(lfp, "update: note: %s\n", s);
+               goto again;
+
        default:
                *--cp = '\0';
        default:
                *--cp = '\0';
-               error("update: unexpected response '%s'\n", buf);
+               error("update: unexpected response '%s'\n", s);
                return(0);
        }
 
                return(0);
        }
 
@@ -562,14 +614,17 @@ query(name)
                (void) sprintf(tp, "/%s", name);
 
        if (lstat(target, &stb) < 0) {
                (void) sprintf(tp, "/%s", name);
 
        if (lstat(target, &stb) < 0) {
-               (void) write(rem, "N\n", 2);
+               if (errno == ENOENT)
+                       (void) write(rem, "N\n", 2);
+               else
+                       error("%s:%s: %s\n", host, target, strerror(errno));
                *tp = '\0';
                return;
        }
 
        switch (stb.st_mode & S_IFMT) {
        case S_IFREG:
                *tp = '\0';
                return;
        }
 
        switch (stb.st_mode & S_IFMT) {
        case S_IFREG:
-               (void) sprintf(buf, "Y%D %D\n", stb.st_size, stb.st_mtime);
+               (void) sprintf(buf, "Y%ld %ld\n", stb.st_size, stb.st_mtime);
                (void) write(rem, buf, strlen(buf));
                break;
 
                (void) write(rem, buf, strlen(buf));
                break;
 
@@ -595,9 +650,9 @@ recvf(cmd, type)
        time_t mtime;
        struct stat stb;
        struct timeval tvp[2];
        time_t mtime;
        struct stat stb;
        struct timeval tvp[2];
-       char *owner, *group, *dir;
+       char *owner, *group;
        char new[BUFSIZ];
        char new[BUFSIZ];
-       extern char *tmpname;
+       extern char *tempname;
 
        cp = cmd;
        opts = 0;
 
        cp = cmd;
        opts = 0;
@@ -664,52 +719,42 @@ recvf(cmd, type)
                }
                if (lstat(target, &stb) == 0) {
                        if (ISDIR(stb.st_mode)) {
                }
                if (lstat(target, &stb) == 0) {
                        if (ISDIR(stb.st_mode)) {
-                               if ((stb.st_mode & 0777) == mode) {
+                               if ((stb.st_mode & 07777) == mode) {
                                        ack();
                                        return;
                                }
                                buf[0] = '\0';
                                (void) sprintf(buf + 1,
                                        ack();
                                        return;
                                }
                                buf[0] = '\0';
                                (void) sprintf(buf + 1,
-                                       "%s:%s: Warning: mode %o != %o\n",
-                                       host, target, stb.st_mode & 0777, mode);
+                                       "%s: Warning: remote mode %o != local mode %o\n",
+                                       target, stb.st_mode & 07777, mode);
                                (void) write(rem, buf, strlen(buf + 1) + 1);
                                return;
                        }
                                (void) write(rem, buf, strlen(buf + 1) + 1);
                                return;
                        }
-                       error("%s:%s: %s\n", host,target,sys_errlist[ENOTDIR]);
-               } else if (chkparent(target) < 0 || mkdir(target, mode) < 0)
-                       error("%s:%s: %s\n", host, target, sys_errlist[errno]);
-               else if (chog(target, owner, group, mode) == 0) {
-                       ack();
+                       errno = ENOTDIR;
+               } else if (errno == ENOENT && (mkdir(target, mode) == 0 ||
+                   chkparent(target) == 0 && mkdir(target, mode) == 0)) {
+                       if (chog(target, owner, group, mode) == 0)
+                               ack();
                        return;
                }
                        return;
                }
+               error("%s:%s: %s\n", host, target, strerror(errno));
                tp = stp[--catname];
                *tp = '\0';
                return;
        }
 
                tp = stp[--catname];
                *tp = '\0';
                return;
        }
 
-       new[0] = '\0';
        if (catname)
                (void) sprintf(tp, "/%s", cp);
        if (catname)
                (void) sprintf(tp, "/%s", cp);
-       if (lstat(target, &stb) == 0 && (f = stb.st_mode & S_IFMT) != S_IFREG &&
-           f != S_IFLNK) {
-               error("%s:%s: not a regular file\n", host, target);
-               return;
-       }
-       if (chkparent(target) < 0)
-               goto bad;
        cp = rindex(target, '/');
        if (cp == NULL)
        cp = rindex(target, '/');
        if (cp == NULL)
-               dir = ".";
-       else if (cp == target) {
-               dir = "";
-               cp = NULL;
-       } else {
-               dir = target;
+               strcpy(new, tempname);
+       else if (cp == target)
+               (void) sprintf(new, "/%s", tempname);
+       else {
                *cp = '\0';
                *cp = '\0';
-       }
-       (void) sprintf(new, "%s/%s", dir, tmpname);
-       if (cp != NULL)
+               (void) sprintf(new, "%s/%s", target, tempname);
                *cp = '/';
                *cp = '/';
+       }
 
        if (type == S_IFLNK) {
                int j;
 
        if (type == S_IFLNK) {
                int j;
@@ -722,19 +767,21 @@ recvf(cmd, type)
                        cp += j;
                }
                *cp = '\0';
                        cp += j;
                }
                *cp = '\0';
-               umask(~mode & 0777);
-               j = symlink(buf, new);
-               umask(0);
-               (void) response();
-               if (j < 0)
-                       goto bad1;
+               if (response() < 0) {
+                       err();
+                       return;
+               }
+               if (symlink(buf, new) < 0) {
+                       if (errno != ENOENT || chkparent(new) < 0 ||
+                           symlink(buf, new) < 0)
+                               goto badn;
+               }
                mode &= 0777;
                if (opts & COMPARE) {
                        char tbuf[BUFSIZ];
 
                mode &= 0777;
                if (opts & COMPARE) {
                        char tbuf[BUFSIZ];
 
-                       if ((i = readlink(target, tbuf, BUFSIZ)) < 0)
-                               goto bad;
-                       if (i == size && strncmp(buf, tbuf, size) == 0) {
+                       if ((i = readlink(target, tbuf, BUFSIZ)) >= 0 &&
+                           i == size && strncmp(buf, tbuf, size) == 0) {
                                (void) unlink(new);
                                ack();
                                return;
                                (void) unlink(new);
                                ack();
                                return;
@@ -745,8 +792,12 @@ recvf(cmd, type)
                goto fixup;
        }
 
                goto fixup;
        }
 
-       if ((f = creat(new, mode)) < 0)
-               goto bad1;
+       if ((f = creat(new, mode)) < 0) {
+               if (errno != ENOENT || chkparent(new) < 0 ||
+                   (f = creat(new, mode)) < 0)
+                       goto badn;
+       }
+
        ack();
        wrerr = 0;
        for (i = 0; i < size; i += BUFSIZ) {
        ack();
        wrerr = 0;
        for (i = 0; i < size; i += BUFSIZ) {
@@ -775,9 +826,13 @@ recvf(cmd, type)
                }
        }
        (void) close(f);
                }
        }
        (void) close(f);
-       (void) response();
+       if (response() < 0) {
+               err();
+               (void) unlink(new);
+               return;
+       }
        if (wrerr) {
        if (wrerr) {
-               error("%s:%s: %s\n", host, cp, sys_errlist[olderrno]);
+               error("%s:%s: %s\n", host, new, strerror(errno));
                (void) unlink(new);
                return;
        }
                (void) unlink(new);
                return;
        }
@@ -786,9 +841,13 @@ recvf(cmd, type)
                int c;
 
                if ((f1 = fopen(target, "r")) == NULL)
                int c;
 
                if ((f1 = fopen(target, "r")) == NULL)
-                       goto bad;
-               if ((f2 = fopen(new, "r")) == NULL)
-                       goto bad1;
+                       goto badt;
+               if ((f2 = fopen(new, "r")) == NULL) {
+               badn:
+                       error("%s:%s: %s\n", host, new, strerror(errno));
+                       (void) unlink(new);
+                       return;
+               }
                while ((c = getc(f1)) == getc(f2))
                        if (c == EOF) {
                                (void) fclose(f1);
                while ((c = getc(f1)) == getc(f2))
                        if (c == EOF) {
                                (void) fclose(f1);
@@ -803,8 +862,7 @@ recvf(cmd, type)
                differ:
                        (void) unlink(new);
                        buf[0] = '\0';
                differ:
                        (void) unlink(new);
                        buf[0] = '\0';
-                       (void) sprintf(buf + 1, "need to update %s:%s\n",
-                               host, target);
+                       (void) sprintf(buf + 1, "need to update: %s\n",target);
                        (void) write(rem, buf, strlen(buf + 1) + 1);
                        return;
                }
                        (void) write(rem, buf, strlen(buf + 1) + 1);
                        return;
                }
@@ -818,27 +876,22 @@ recvf(cmd, type)
        tvp[1].tv_sec = mtime;
        tvp[1].tv_usec = 0;
        if (utimes(new, tvp) < 0) {
        tvp[1].tv_sec = mtime;
        tvp[1].tv_usec = 0;
        if (utimes(new, tvp) < 0) {
-bad1:
-               error("%s:%s: %s\n", host, new, sys_errlist[errno]);
-               (void) unlink(new);
-               return;
+               note("%s:utimes failed %s: %s\n", host, new, strerror(errno));
        }
        }
-fixup:
        if (chog(new, owner, group, mode) < 0) {
                (void) unlink(new);
                return;
        }
        if (chog(new, owner, group, mode) < 0) {
                (void) unlink(new);
                return;
        }
-       
+fixup:
        if (rename(new, target) < 0) {
        if (rename(new, target) < 0) {
-bad:
-               error("%s:%s: %s\n", host, target, sys_errlist[errno]);
-               if (new[0])
-                       (void) unlink(new);
+badt:
+               error("%s:%s: %s\n", host, target, strerror(errno));
+               (void) unlink(new);
                return;
        }
        if (opts & COMPARE) {
                buf[0] = '\0';
                return;
        }
        if (opts & COMPARE) {
                buf[0] = '\0';
-               (void) sprintf(buf + 1, "%s: updated %s\n", host, target);
+               (void) sprintf(buf + 1, "updated %s\n", target);
                (void) write(rem, buf, strlen(buf + 1) + 1);
        } else
                ack();
                (void) write(rem, buf, strlen(buf + 1) + 1);
        } else
                ack();
@@ -872,68 +925,59 @@ hardlink(cmd)
        }
        *cp++ = '\0';
 
        }
        *cp++ = '\0';
 
-       if (catname)
+       if (catname) {
                (void) sprintf(tp, "/%s", cp);
                (void) sprintf(tp, "/%s", cp);
+       }
        if (lstat(target, &stb) == 0) {
        if (lstat(target, &stb) == 0) {
-               if ((stb.st_mode & S_IFMT) != S_IFREG) {
+               int mode = stb.st_mode & S_IFMT;
+               if (mode != S_IFREG && mode != S_IFLNK) {
                        error("%s:%s: not a regular file\n", host, target);
                        return;
                }
                exists = 1;
        }
                        error("%s:%s: not a regular file\n", host, target);
                        return;
                }
                exists = 1;
        }
-       if (chkparent(target) < 0 ||
-           exists && unlink(target) < 0 ||
-           link(oldname, target) < 0) {
-               error("%s:%s: %s\n", host, target, sys_errlist[errno]);
+       if (chkparent(target) < 0 ) {
+               error("%s:%s: %s (no parent)\n",
+                       host, target, strerror(errno));
+               return;
+       }
+       if (exists && (unlink(target) < 0)) {
+               error("%s:%s: %s (unlink)\n",
+                       host, target, strerror(errno));
+               return;
+       }
+       if (link(oldname, target) < 0) {
+               error("%s:can't link %s to %s\n",
+                       host, target, oldname);
                return;
        }
        ack();
 }
 
 /*
                return;
        }
        ack();
 }
 
 /*
- * Check parent directory for write permission and create if it doesn't
- * exist.
+ * Check to see if parent directory exists and create one if not.
  */
 chkparent(name)
        char *name;
 {
  */
 chkparent(name)
        char *name;
 {
-       register char *cp, *dir;
-       extern int userid, groupid;
+       register char *cp;
+       struct stat stb;
 
        cp = rindex(name, '/');
 
        cp = rindex(name, '/');
-       if (cp == NULL)
-               dir = ".";
-       else if (cp == name) {
-               dir = "/";
-               cp = NULL;
-       } else {
-               dir = name;
-               *cp = '\0';
-       }
-       if (access(dir, 2) == 0) {
-               if (cp != NULL)
-                       *cp = '/';
+       if (cp == NULL || cp == name)
                return(0);
                return(0);
-       }
-       if (errno == ENOENT) {
-               if (rindex(dir, '/') != NULL && chkparent(dir) < 0)
-                       goto bad;
-               if (!strcmp(dir, ".") || !strcmp(dir, "/"))
-                       goto bad;
-               if (mkdir(dir, 0777 & ~oumask) < 0)
-                       goto bad;
-               if (chown(dir, userid, groupid) < 0) {
-                       (void) unlink(dir);
-                       goto bad;
-               }
-               if (cp != NULL)
+       *cp = '\0';
+       if (lstat(name, &stb) < 0) {
+               if (errno == ENOENT && chkparent(name) >= 0 &&
+                   mkdir(name, 0777 & ~oumask) >= 0) {
                        *cp = '/';
                        *cp = '/';
+                       return(0);
+               }
+       } else if (ISDIR(stb.st_mode)) {
+               *cp = '/';
                return(0);
        }
                return(0);
        }
-
-bad:
-       if (cp != NULL)
-               *cp = '/';
+       *cp = '/';
        return(-1);
 }
 
        return(-1);
 }
 
@@ -944,56 +988,62 @@ chog(file, owner, group, mode)
        char *file, *owner, *group;
        int mode;
 {
        char *file, *owner, *group;
        int mode;
 {
-       extern int userid, groupid;
-       extern char user[];
        register int i;
        int uid, gid;
        register int i;
        int uid, gid;
+       extern char user[];
+       extern int userid;
 
        uid = userid;
        if (userid == 0) {
 
        uid = userid;
        if (userid == 0) {
-               if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
+               if (*owner == ':') {
+                       uid = atoi(owner + 1);
+               } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
                        if ((pw = getpwnam(owner)) == NULL) {
                                if (mode & 04000) {
                        if ((pw = getpwnam(owner)) == NULL) {
                                if (mode & 04000) {
-                                       error("%s:%s: unknown login name\n",
+                                       note("%s:%s: unknown login name, clearing setuid",
                                                host, owner);
                                                host, owner);
-                                       return(-1);
+                                       mode &= ~04000;
+                                       uid = 0;
                                }
                        } else
                                uid = pw->pw_uid;
                } else
                        uid = pw->pw_uid;
                                }
                        } else
                                uid = pw->pw_uid;
                } else
                        uid = pw->pw_uid;
+               if (*group == ':') {
+                       gid = atoi(group + 1);
+                       goto ok;
+               }
        } else if ((mode & 04000) && strcmp(user, owner) != 0)
                mode &= ~04000;
        } else if ((mode & 04000) && strcmp(user, owner) != 0)
                mode &= ~04000;
-       gid = groupid;
+       gid = -1;
        if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
        if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
-               if ((gr = getgrnam(group)) == NULL) {
+               if ((*group == ':' && (getgrgid(gid = atoi(group + 1)) == NULL))
+                  || ((gr = getgrnam(group)) == NULL)) {
                        if (mode & 02000) {
                        if (mode & 02000) {
-                               error("%s:%s: unknown group\n", host, group);
-                               return(-1);
+                               note("%s:%s: unknown group", host, group);
+                               mode &= ~02000;
                        }
                } else
                        gid = gr->gr_gid;
        } else
                gid = gr->gr_gid;
                        }
                } else
                        gid = gr->gr_gid;
        } else
                gid = gr->gr_gid;
-       if (userid && groupid != gid) {
-               for (i = 0; gr->gr_mem[i] != NULL; i++)
+       if (userid && gid >= 0) {
+               if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++)
                        if (!(strcmp(user, gr->gr_mem[i])))
                                goto ok;
                mode &= ~02000;
                        if (!(strcmp(user, gr->gr_mem[i])))
                                goto ok;
                mode &= ~02000;
-               gid = groupid;
+               gid = -1;
        }
 ok:
        }
 ok:
-       if (chown(file, uid, gid) < 0) {
-               error("%s:%s: %s\n", host, file, sys_errlist[errno]);
-               return(-1);
-       }
-       /*
-        * Restore set-user-id or set-group-id bit if appropriate.
-        */
-       if ((mode & 06000) && chmod(file, mode) < 0) {
-               error("%s:%s: %s\n", host, file, sys_errlist[errno]);
-               return(-1);
+       if (userid)
+               setreuid(userid, 0);
+       if (chown(file, uid, gid) < 0 ||
+           (mode & 07000) && chmod(file, mode) < 0) {
+               note("%s: chown or chmod failed: file %s:  %s",
+                            host, file, strerror(errno));
        }
        }
+       if (userid)
+               setreuid(0, userid);
        return(0);
 }
 
        return(0);
 }
 
@@ -1098,8 +1148,8 @@ clean(cp)
                error("clean: options not delimited\n");
                return;
        }
                error("clean: options not delimited\n");
                return;
        }
-       if (access(target, 6) < 0 || (d = opendir(target)) == NULL) {
-               error("%s:%s: %s\n", host, target, sys_errlist[errno]);
+       if ((d = opendir(target)) == NULL) {
+               error("%s:%s: %s\n", host, target, strerror(errno));
                return;
        }
        ack();
                return;
        }
        ack();
@@ -1121,7 +1171,7 @@ clean(cp)
                        ;
                tp--;
                if (lstat(target, &stb) < 0) {
                        ;
                tp--;
                if (lstat(target, &stb) < 0) {
-                       error("%s:%s: %s\n", host, target, sys_errlist[errno]);
+                       error("%s:%s: %s\n", host, target, strerror(errno));
                        continue;
                }
                (void) sprintf(buf, "Q%s\n", dp->d_name);
                        continue;
                }
                (void) sprintf(buf, "Q%s\n", dp->d_name);
@@ -1138,7 +1188,7 @@ clean(cp)
                if (opts & VERIFY) {
                        cp = buf;
                        *cp++ = '\0';
                if (opts & VERIFY) {
                        cp = buf;
                        *cp++ = '\0';
-                       (void) sprintf(cp, "need to remove %s\n", target);
+                       (void) sprintf(cp, "need to remove: %s\n", target);
                        (void) write(rem, buf, strlen(cp) + 1);
                } else
                        remove(&stb);
                        (void) write(rem, buf, strlen(cp) + 1);
                } else
                        remove(&stb);
@@ -1179,7 +1229,7 @@ remove(stp)
                return;
        }
 
                return;
        }
 
-       if (access(target, 6) < 0 || (d = opendir(target)) == NULL)
+       if ((d = opendir(target)) == NULL)
                goto bad;
 
        otp = tp;
                goto bad;
 
        otp = tp;
@@ -1199,7 +1249,7 @@ remove(stp)
                        ;
                tp--;
                if (lstat(target, &stb) < 0) {
                        ;
                tp--;
                if (lstat(target, &stb) < 0) {
-                       error("%s:%s: %s\n", host, target, sys_errlist[errno]);
+                       error("%s:%s: %s\n", host, target, strerror(errno));
                        continue;
                }
                remove(&stb);
                        continue;
                }
                remove(&stb);
@@ -1209,7 +1259,7 @@ remove(stp)
        *tp = '\0';
        if (rmdir(target) < 0) {
 bad:
        *tp = '\0';
        if (rmdir(target) < 0) {
 bad:
-               error("%s:%s: %s\n", host, target, sys_errlist[errno]);
+               error("%s:%s: %s\n", host, target, strerror(errno));
                return;
        }
 removed:
                return;
        }
 removed:
@@ -1228,9 +1278,10 @@ dospecial(cmd)
        int fd[2], status, pid, i;
        register char *cp, *s;
        char sbuf[BUFSIZ];
        int fd[2], status, pid, i;
        register char *cp, *s;
        char sbuf[BUFSIZ];
+       extern int userid, groupid;
 
        if (pipe(fd) < 0) {
 
        if (pipe(fd) < 0) {
-               error("%s\n", sys_errlist[errno]);
+               error("%s\n", strerror(errno));
                return;
        }
        if ((pid = fork()) == 0) {
                return;
        }
        if ((pid = fork()) == 0) {
@@ -1240,13 +1291,14 @@ dospecial(cmd)
                (void) close(0);
                (void) close(1);
                (void) close(2);
                (void) close(0);
                (void) close(1);
                (void) close(2);
-               (void) open("/dev/null", 0);
+               (void) open(_PATH_DEVNULL, O_RDONLY);
                (void) dup(fd[1]);
                (void) dup(fd[1]);
                (void) close(fd[0]);
                (void) close(fd[1]);
                (void) dup(fd[1]);
                (void) dup(fd[1]);
                (void) close(fd[0]);
                (void) close(fd[1]);
+               setgid(groupid);
                setuid(userid);
                setuid(userid);
-               execl("/bin/sh", "sh", "-c", cmd, 0);
+               execl(_PATH_BSHELL, "sh", "-c", cmd, 0);
                _exit(127);
        }
        (void) close(fd[1]);
                _exit(127);
        }
        (void) close(fd[1]);
@@ -1280,6 +1332,7 @@ dospecial(cmd)
                ;
        if (i == -1)
                status = -1;
                ;
        if (i == -1)
                status = -1;
+       (void) close(fd[0]);
        if (status)
                error("shell returned %d\n", status);
        else
        if (status)
                error("shell returned %d\n", status);
        else
@@ -1306,16 +1359,27 @@ error(fmt, a1, a2, a3)
        char *fmt;
        int a1, a2, a3;
 {
        char *fmt;
        int a1, a2, a3;
 {
-       nerrs++;
-       strcpy(buf, "\1rdist: ");
-       (void) sprintf(buf+8, fmt, a1, a2, a3);
-       if (!iamremote) {
+       static FILE *fp;
+
+       ++nerrs;
+       if (!fp && !(fp = fdopen(rem, "w")))
+               return;
+       if (iamremote) {
+               (void)fprintf(fp, "%crdist: ", 0x01);
+               (void)fprintf(fp, fmt, a1, a2, a3);
+               fflush(fp);
+       }
+       else {
                fflush(stdout);
                fflush(stdout);
-               (void) write(2, buf+1, strlen(buf+1));
-       } else
-               (void) write(rem, buf, strlen(buf));
-       if (lfp != NULL)
-               (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
+               (void)fprintf(stderr, "rdist: ");
+               (void)fprintf(stderr, fmt, a1, a2, a3);
+               fflush(stderr);
+       }
+       if (lfp != NULL) {
+               (void)fprintf(lfp, "rdist: ");
+               (void)fprintf(lfp, fmt, a1, a2, a3);
+               fflush(lfp);
+       }
 }
 
 /*VARARGS1*/
 }
 
 /*VARARGS1*/
@@ -1323,16 +1387,27 @@ fatal(fmt, a1, a2,a3)
        char *fmt;
        int a1, a2, a3;
 {
        char *fmt;
        int a1, a2, a3;
 {
-       nerrs++;
-       strcpy(buf, "\2rdist: ");
-       (void) sprintf(buf+8, fmt, a1, a2, a3);
-       if (!iamremote) {
+       static FILE *fp;
+
+       ++nerrs;
+       if (!fp && !(fp = fdopen(rem, "w")))
+               return;
+       if (iamremote) {
+               (void)fprintf(fp, "%crdist: ", 0x02);
+               (void)fprintf(fp, fmt, a1, a2, a3);
+               fflush(fp);
+       }
+       else {
                fflush(stdout);
                fflush(stdout);
-               (void) write(2, buf+1, strlen(buf+1));
-       } else
-               (void) write(rem, buf, strlen(buf));
-       if (lfp != NULL)
-               (void) fwrite(buf+1, 1, strlen(buf+1), lfp);
+               (void)fprintf(stderr, "rdist: ");
+               (void)fprintf(stderr, fmt, a1, a2, a3);
+               fflush(stderr);
+       }
+       if (lfp != NULL) {
+               (void)fprintf(lfp, "rdist: ");
+               (void)fprintf(lfp, fmt, a1, a2, a3);
+               fflush(lfp);
+       }
        cleanup();
 }
 
        cleanup();
 }
 
@@ -1358,6 +1433,10 @@ response()
                        return(1);
                }
                return(0);
                        return(1);
                }
                return(0);
+       case '\3':
+               *--cp = '\0';
+               log(lfp, "Note: %s\n",s);
+               return(response());
 
        default:
                s--;
 
        default:
                s--;
@@ -1384,6 +1463,23 @@ response()
  */
 cleanup()
 {
  */
 cleanup()
 {
-       (void) unlink(tmpfile);
+       (void) unlink(tempfile);
        exit(1);
 }
        exit(1);
 }
+
+note(fmt, a1, a2, a3)
+{
+       static char buf[BUFSIZ];
+       sprintf(buf, fmt, a1, a2, a3);
+       comment(buf);
+}
+
+comment(s)
+char *s;
+{
+       char c = '\3';
+       write(rem, &c, 1);
+       write(rem, s, strlen(s));
+       c = '\n';
+       write(rem, &c, 1);
+}