=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/rdistd/server.c,v retrieving revision 1.13 retrieving revision 1.14 diff -c -r1.13 -r1.14 *** src/usr.bin/rdistd/server.c 2003/04/19 17:22:30 1.13 --- src/usr.bin/rdistd/server.c 2003/05/14 01:34:35 1.14 *************** *** 1,4 **** ! /* $OpenBSD: server.c,v 1.13 2003/04/19 17:22:30 millert Exp $ */ /* * Copyright (c) 1983 Regents of the University of California. --- 1,4 ---- ! /* $OpenBSD: server.c,v 1.14 2003/05/14 01:34:35 millert Exp $ */ /* * Copyright (c) 1983 Regents of the University of California. *************** *** 32,49 **** * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 ! static char RCSid[] = ! "$From: server.c,v 6.85 1996/03/12 22:55:38 mcooper Exp $"; #else ! static char RCSid[] = ! "$OpenBSD: server.c,v 1.13 2003/04/19 17:22:30 millert Exp $"; #endif ! static char sccsid[] = "@(#)server.c 5.3 (Berkeley) 6/7/86"; ! static char copyright[] = "@(#) Copyright (c) 1983 Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ --- 32,52 ---- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #include "defs.h" + #ifndef lint #if 0 ! static char RCSid[] __attribute__((__unused__)) = ! "$From: server.c,v 1.10 1999/08/04 15:57:33 christos Exp $"; #else ! static char RCSid[] __attribute__((__unused__)) = ! "$OpenBSD: server.c,v 1.14 2003/05/14 01:34:35 millert Exp $"; #endif ! static char sccsid[] __attribute__((__unused__)) = ! "@(#)server.c 5.3 (Berkeley) 6/7/86"; ! static char copyright[] __attribute__((__unused__)) = "@(#) Copyright (c) 1983 Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ *************** *** 52,59 **** * Server routines */ - #include "defs.h" - char tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */ char buf[BUFSIZ]; /* general purpose buffer */ char target[MAXPATHLEN]; /* target/source directory name */ --- 55,60 ---- *************** *** 65,75 **** static long min_freefiles = 0; /* Minimium free # files on a filesystem */ int oumask; /* Old umask */ /* * Cat "string" onto the target buffer with error checking. */ ! static int cattarget(string) ! char *string; { if (strlen(string) + strlen(target) + 2 > sizeof(target)) { message(MT_INFO, "target buffer is not large enough."); --- 66,97 ---- static long min_freefiles = 0; /* Minimium free # files on a filesystem */ int oumask; /* Old umask */ + static int cattarget(char *); + static int setownership(char *, int, UID_T, GID_T, int); + static int setfilemode(char *, int, int, int); + static int fchog(int, char *, char *, char *, int); + static int removefile(struct stat *, int); + static void doclean(char *); + static void clean(char *); + static void dospecial(char *); + static void docmdspecial(void); + static void query(char *); + static int chkparent(char *, opt_t); + static char *savetarget(char *, opt_t); + static void recvfile(char *, opt_t, int, char *, char *, time_t, time_t, off_t); + static void recvdir(opt_t, int, char *, char *); + static void recvlink(char *, opt_t, int, off_t); + static void hardlink(char *); + static void setconfig(char *); + static void recvit(char *, int); + static void dochmog(char *); + static void settarget(char *, int); + /* * Cat "string" onto the target buffer with error checking. */ ! static int ! cattarget(char *string) { if (strlen(string) + strlen(target) + 2 > sizeof(target)) { message(MT_INFO, "target buffer is not large enough."); *************** *** 80,87 **** return(-10); } ! (void) snprintf(ptarget, target + sizeof(target) - ptarget, ! "/%s", string); return(0); } --- 102,109 ---- return(-10); } ! (void) snprintf(ptarget, sizeof(target) - (ptarget - target), ! "/%s", string); return(0); } *************** *** 89,135 **** /* * Set uid and gid ownership of a file. */ ! static int setownership(file, fd, uid, gid) ! char *file; ! int fd; ! UID_T uid; ! GID_T gid; { int status = -1; /* * We assume only the Superuser can change uid ownership. */ ! if (getuid() == 0) { ! #if defined(HAVE_FCHOWN) ! if (fd != -1) ! status = fchown(fd, (CHOWN_UID_T) uid, ! (CHOWN_GID_T) gid); #endif - if (status < 0) - status = chown(file, (CHOWN_UID_T) uid, - (CHOWN_GID_T) gid); - if (status < 0) { - message(MT_NOTICE, "%s: chown %d:%d failed: %s", - target, (UID_T) uid, (GID_T) gid, SYSERR); - return(-1); - } - } else { #if defined(HAVE_FCHOWN) ! if (fd != -1) ! status = fchown(fd, (CHOWN_UID_T) -1, ! (CHOWN_GID_T) gid); #endif ! if (status < 0) ! status = chown(file, (CHOWN_UID_T) -1, ! (CHOWN_GID_T) gid); ! if (status < 0) { message(MT_NOTICE, "%s: chgrp %d failed: %s", ! target, (GID_T) gid, SYSERR); ! return(-1); ! } } return(0); --- 111,157 ---- /* * Set uid and gid ownership of a file. */ ! static int ! setownership(char *file, int fd, UID_T uid, GID_T gid, int link) { int status = -1; /* * We assume only the Superuser can change uid ownership. */ ! if (getuid() != 0) ! uid = -1; ! ! /* ! * If we are dealing with a symlink, only try to change it if ! * we have lchown, if we don't leave it alone. ! */ ! #if defined(HAVE_LCHOWN) ! if (link) ! status = lchown(file, (CHOWN_UID_T) uid, ! (CHOWN_GID_T) gid); ! #else ! if (link) ! return 0; #endif #if defined(HAVE_FCHOWN) ! if (fd != -1 && !link) ! status = fchown(fd, (CHOWN_UID_T) uid, ! (CHOWN_GID_T) gid); #endif ! if (status < 0 && !link) ! status = chown(file, (CHOWN_UID_T) uid, ! (CHOWN_GID_T) gid); ! if (status < 0) { ! if (uid == (UID_T) -1) message(MT_NOTICE, "%s: chgrp %d failed: %s", ! target, gid, SYSERR); ! else ! message(MT_NOTICE, "%s: chown %d.%d failed: %s", ! target, uid, gid, SYSERR); ! return(-1); } return(0); *************** *** 138,159 **** /* * Set mode of a file */ ! static int setfilemode(file, fd, mode) ! char *file; ! int fd; ! int mode; { int status = -1; if (mode == -1) return(0); #if defined(HAVE_FCHMOD) ! if (fd != -1) status = fchmod(fd, mode); #endif ! if (status < 0) status = chmod(file, mode); if (status < 0) { --- 160,191 ---- /* * Set mode of a file */ ! static int ! setfilemode(char *file, int fd, int mode, int link) { int status = -1; if (mode == -1) return(0); + /* + * If we are dealing with a symlink, only try to change it if + * we have lchown, if we don't leave it alone. + */ + #if defined(HAVE_LCHMOD) + if (link) + status = lchmod(file, mode); + #else + if (link) + return 0; + #endif + #if defined(HAVE_FCHMOD) ! if (fd != -1 && !link) status = fchmod(fd, mode); #endif ! if (status < 0 && !link) status = chmod(file, mode); if (status < 0) { *************** *** 163,203 **** return(0); } - /* - * Get group entry. This routine takes a string argument (name). - * If name is of form ":N" a lookup for gid N is done. - * Otherwise a lookup by name is done. - */ - static struct group *mygetgroup(name) - char *name; - { - struct group *gr; - - if (*name == ':') - gr = getgrgid(atoi(name + 1)); - else - gr = getgrnam(name); - - return(gr); - } - - /* * Change owner, group and mode of file. */ ! static int fchog(fd, file, owner, group, mode) ! int fd; ! char *file, *owner, *group; ! int mode; { ! struct group *gr = NULL; ! static char last_group[128]; ! static char last_owner[128]; ! static GID_T last_gid = (GID_T)-2; ! static UID_T last_uid = (UID_T)-2; ! static GID_T last_primegid; extern char *locuser; int i; UID_T uid; GID_T gid; GID_T primegid = (GID_T)-2; --- 195,210 ---- return(0); } /* * Change owner, group and mode of file. */ ! static int ! fchog(int fd, char *file, char *owner, char *group, int mode) { ! static struct group *gr = NULL; extern char *locuser; int i; + struct stat st; UID_T uid; GID_T gid; GID_T primegid = (GID_T)-2; *************** *** 206,213 **** if (userid == 0) { /* running as root; take anything */ if (*owner == ':') { uid = (UID_T) atoi(owner + 1); ! } else if (last_uid == (UID_T)-2 || ! strcmp(owner, last_owner) != 0) { if ((pw = getpwnam(owner)) == NULL) { if (mode != -1 && IS_ON(mode, S_ISUID)) { message(MT_NOTICE, --- 213,219 ---- if (userid == 0) { /* running as root; take anything */ if (*owner == ':') { uid = (UID_T) atoi(owner + 1); ! } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) { if ((pw = getpwnam(owner)) == NULL) { if (mode != -1 && IS_ON(mode, S_ISUID)) { message(MT_NOTICE, *************** *** 219,232 **** message(MT_NOTICE, "%s: unknown login name \"%s\"", target, owner); ! } else { ! uid = last_uid = pw->pw_uid; ! primegid = last_primegid = pw->pw_gid; ! strlcpy(last_owner, owner, sizeof last_owner); ! } } else { ! uid = last_uid; ! primegid = last_primegid; } if (*group == ':') { gid = (GID_T) atoi(group + 1); --- 225,235 ---- message(MT_NOTICE, "%s: unknown login name \"%s\"", target, owner); ! } else ! uid = pw->pw_uid; } else { ! uid = pw->pw_uid; ! primegid = pw->pw_gid; } if (*group == ':') { gid = (GID_T) atoi(group + 1); *************** *** 248,261 **** } gid = (GID_T) -1; ! if (last_gid < (GID_T)0 || strcmp(group, last_group) != 0) { ! /* ! * Invalid cached values so we need to do a new lookup. ! */ ! if ((gr = mygetgroup(group))) { ! last_gid = gid = gr->gr_gid; ! strlcpy(last_group, gr->gr_name, sizeof last_group); ! } else { if (mode != -1 && IS_ON(mode, S_ISGID)) { message(MT_NOTICE, "%s: unknown group \"%s\", clearing setgid", --- 251,260 ---- } gid = (GID_T) -1; ! if (gr == NULL || strcmp(group, gr->gr_name) != 0) { ! if ((*group == ':' && ! (getgrgid(gid = atoi(group + 1)) == NULL)) ! || ((gr = (struct group *)getgrnam(group)) == NULL)) { if (mode != -1 && IS_ON(mode, S_ISGID)) { message(MT_NOTICE, "%s: unknown group \"%s\", clearing setgid", *************** *** 265,288 **** message(MT_NOTICE, "%s: unknown group \"%s\"", target, group); ! } ! } else { ! /* ! * Use the cached values. ! */ ! gid = last_gid; ! } - /* - * We need to check non-root users to make sure they're a member - * of the group. If they are not, we don't set that gid ownership. - */ if (userid && gid >= 0 && gid != primegid) { - if (!gr) - gr = mygetgroup(group); if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++) ! if (strcmp(locuser, gr->gr_mem[i]) == 0) goto ok; if (mode != -1 && IS_ON(mode, S_ISGID)) { message(MT_NOTICE, --- 264,278 ---- message(MT_NOTICE, "%s: unknown group \"%s\"", target, group); ! } else ! gid = gr->gr_gid; ! } else ! gid = gr->gr_gid; if (userid && gid >= 0 && gid != primegid) { if (gr) for (i = 0; gr->gr_mem[i] != NULL; i++) ! if (strcmp(locuser, gr->gr_mem[i]) == 0) goto ok; if (mode != -1 && IS_ON(mode, S_ISGID)) { message(MT_NOTICE, *************** *** 293,304 **** gid = (GID_T) -1; } ok: /* * Set uid and gid ownership. If that fails, strip setuid and * setgid bits from mode. Once ownership is set, successful * or otherwise, set the new file mode. */ ! if (setownership(file, fd, uid, gid) < 0) { if (mode != -1 && IS_ON(mode, S_ISUID)) { message(MT_NOTICE, "%s: chown failed, clearing setuid", target); --- 283,298 ---- gid = (GID_T) -1; } ok: + if (stat(file, &st) == -1) { + error("%s: Stat failed %s", file, SYSERR); + return -1; + } /* * Set uid and gid ownership. If that fails, strip setuid and * setgid bits from mode. Once ownership is set, successful * or otherwise, set the new file mode. */ ! if (setownership(file, fd, uid, gid, S_ISLNK(st.st_mode)) < 0) { if (mode != -1 && IS_ON(mode, S_ISUID)) { message(MT_NOTICE, "%s: chown failed, clearing setuid", target); *************** *** 310,316 **** mode &= ~S_ISGID; } } ! (void) setfilemode(file, fd, mode); return(0); --- 304,310 ---- mode &= ~S_ISGID; } } ! (void) setfilemode(file, fd, mode, S_ISLNK(st.st_mode)); return(0); *************** *** 320,327 **** * Remove a file or directory (recursively) and send back an acknowledge * or an error message. */ ! static int removefile(statb) ! struct stat *statb; { DIR *d; static DIRENTRY *dp; --- 314,321 ---- * Remove a file or directory (recursively) and send back an acknowledge * or an error message. */ ! static int ! removefile(struct stat *statb, int silent) { DIR *d; static DIRENTRY *dp; *************** *** 333,343 **** switch (statb->st_mode & S_IFMT) { case S_IFREG: case S_IFLNK: if (unlink(target) < 0) { if (errno == ETXTBSY) { ! message(MT_REMOTE|MT_NOTICE, ! "%s: unlink failed: %s", ! target, SYSERR); return(0); } else { error("%s: unlink failed: %s", target, SYSERR); --- 327,346 ---- switch (statb->st_mode & S_IFMT) { case S_IFREG: case S_IFLNK: + case S_IFCHR: + case S_IFBLK: + #ifdef S_IFSOCK + case S_IFSOCK: + #endif + #ifdef S_IFIFO + case S_IFIFO: + #endif if (unlink(target) < 0) { if (errno == ETXTBSY) { ! if (!silent) ! message(MT_REMOTE|MT_NOTICE, ! "%s: unlink failed: %s", ! target, SYSERR); return(0); } else { error("%s: unlink failed: %s", target, SYSERR); *************** *** 362,390 **** optarget = ptarget; len = ptarget - target; ! while ((dp = readdir(d))) { if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') || (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')) continue; if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) { ! message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long", ! target, dp->d_name); continue; } ptarget = optarget; *ptarget++ = '/'; cp = dp->d_name;; ! while ((*ptarget++ = *cp++)) ! ; ptarget--; if (lstat(target, &stb) < 0) { ! message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s", ! target, SYSERR); continue; } ! if (removefile(&stb) < 0) ++failures; } (void) closedir(d); --- 365,397 ---- optarget = ptarget; len = ptarget - target; ! while ((dp = readdir(d)) != NULL) { if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') || (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')) continue; if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) { ! if (!silent) ! message(MT_REMOTE|MT_WARNING, ! "%s/%s: Name too long", ! target, dp->d_name); continue; } ptarget = optarget; *ptarget++ = '/'; cp = dp->d_name;; ! while ((*ptarget++ = *cp++) != '\0') ! continue; ptarget--; if (lstat(target, &stb) < 0) { ! if (!silent) ! message(MT_REMOTE|MT_WARNING, ! "%s: lstat failed: %s", ! target, SYSERR); continue; } ! if (removefile(&stb, 0) < 0) ++failures; } (void) closedir(d); *************** *** 399,404 **** --- 406,415 ---- return(-1); } removed: + #if NEWWAY + if (!silent) + message(MT_CHANGE|MT_REMOTE, "%s: removed", target); + #else /* * We use MT_NOTICE instead of MT_CHANGE because this function is * sometimes called by other functions that are suppose to return a *************** *** 406,411 **** --- 417,423 ---- * the Rdist protocol is re-done. Sigh. */ message(MT_NOTICE|MT_REMOTE, "%s: removed", target); + #endif return(0); } *************** *** 413,420 **** * Check the current directory (initialized by the 'T' command to server()) * for extraneous files and remove them. */ ! static void doclean(cp) ! char *cp; { DIR *d; DIRENTRY *dp; --- 425,432 ---- * Check the current directory (initialized by the 'T' command to server()) * for extraneous files and remove them. */ ! static void ! doclean(char *cp) { DIR *d; DIRENTRY *dp; *************** *** 422,427 **** --- 434,440 ---- char *optarget, *ep; int len; opt_t opts; + char targ[MAXPATHLEN*4]; opts = strtol(cp, &ep, 8); if (*ep != CNULL) { *************** *** 436,442 **** optarget = ptarget; len = ptarget - target; ! while ((dp = readdir(d))) { if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') || (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')) --- 449,455 ---- optarget = ptarget; len = ptarget - target; ! while ((dp = readdir(d)) != NULL) { if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') || (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.')) *************** *** 450,457 **** ptarget = optarget; *ptarget++ = '/'; cp = dp->d_name;; ! while ((*ptarget++ = *cp++)) ! ; ptarget--; if (lstat(target, &stb) < 0) { message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s", --- 463,470 ---- ptarget = optarget; *ptarget++ = '/'; cp = dp->d_name;; ! while ((*ptarget++ = *cp++) != '\0') ! continue; ptarget--; if (lstat(target, &stb) < 0) { message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s", *************** *** 459,465 **** continue; } ! (void) sendcmd(CC_QUERY, "%s", dp->d_name); (void) remline(cp = buf, sizeof(buf), TRUE); if (*cp != CC_YES) --- 472,479 ---- continue; } ! ENCODE(targ, dp->d_name); ! (void) sendcmd(CC_QUERY, "%s", targ); (void) remline(cp = buf, sizeof(buf), TRUE); if (*cp != CC_YES) *************** *** 469,475 **** message(MT_REMOTE|MT_INFO, "%s: need to remove", target); else ! (void) removefile(&stb); } (void) closedir(d); --- 483,489 ---- message(MT_REMOTE|MT_INFO, "%s: need to remove", target); else ! (void) removefile(&stb, 0); } (void) closedir(d); *************** *** 480,487 **** /* * Frontend to doclean(). */ ! static void clean(cp) ! char *cp; { doclean(cp); (void) sendcmd(CC_END, NULL); --- 494,501 ---- /* * Frontend to doclean(). */ ! static void ! clean(char *cp) { doclean(cp); (void) sendcmd(CC_END, NULL); *************** *** 493,501 **** * We can't really set an alarm timeout here since we * have no idea how long the command should take. */ ! static void dospecial(cmd) ! char *cmd; { runcommand(cmd); } --- 507,520 ---- * We can't really set an alarm timeout here since we * have no idea how long the command should take. */ ! static void ! dospecial(char *xcmd) { + char cmd[BUFSIZ]; + if (DECODE(cmd, xcmd) == -1) { + error("dospecial: Cannot decode command."); + return; + } runcommand(cmd); } *************** *** 507,513 **** * E_FILES. When an RC_COMMAND is finally received, the E_FILES variable * is stuffed into our environment and a normal dospecial() command is run. */ ! static void docmdspecial() { char *cp; char *cmd, *env = NULL; --- 526,533 ---- * E_FILES. When an RC_COMMAND is finally received, the E_FILES variable * is stuffed into our environment and a normal dospecial() command is run. */ ! static void ! docmdspecial(void) { char *cp; char *cmd, *env = NULL; *************** *** 576,587 **** * QC_YES - exists and its a directory or symbolic link * QC_ERRMSGmessage - error message */ ! static void query(name) ! char *name; { static struct stat stb; int s = -1, stbvalid = 0; if (catname && cattarget(name) < 0) return; --- 596,613 ---- * QC_YES - exists and its a directory or symbolic link * QC_ERRMSGmessage - error message */ ! static void ! query(char *xname) { static struct stat stb; int s = -1, stbvalid = 0; + char name[MAXPATHLEN]; + if (DECODE(name, xname) == -1) { + error("query: Cannot decode filename"); + return; + } + if (catname && cattarget(name) < 0) return; *************** *** 641,649 **** case S_IFLNK: case S_IFDIR: case S_IFREG: (void) sendcmd(QC_YES, "%ld %ld %o %s %s", ! (long) stb.st_size, ! stb.st_mtime, stb.st_mode & 07777, getusername(stb.st_uid, target, options), getgroupname(stb.st_gid, target, options)); --- 667,684 ---- case S_IFLNK: case S_IFDIR: case S_IFREG: + #ifdef notyet + case S_IFCHR: + case S_IFBLK: + #ifdef S_IFSOCK + case S_IFSOCK: + #endif + #ifdef S_IFIFO + case S_IFIFO: + #endif + #endif (void) sendcmd(QC_YES, "%ld %ld %o %s %s", ! (long) stb.st_size, stb.st_mtime, stb.st_mode & 07777, getusername(stb.st_uid, target, options), getgroupname(stb.st_gid, target, options)); *************** *** 659,667 **** /* * Check to see if parent directory exists and create one if not. */ ! static int chkparent(name, opts) ! char *name; ! opt_t opts; { char *cp; struct stat stb; --- 694,701 ---- /* * Check to see if parent directory exists and create one if not. */ ! static int ! chkparent(char *name, opt_t opts) { char *cp; struct stat stb; *************** *** 697,704 **** /* * Save a copy of 'file' by renaming it. */ ! static char *savetarget(file) ! char *file; { static char savefile[MAXPATHLEN]; --- 731,738 ---- /* * Save a copy of 'file' by renaming it. */ ! static char * ! savetarget(char *file, opt_t opts) { static char savefile[MAXPATHLEN]; *************** *** 707,719 **** return(NULL); } ! (void) snprintf(savefile, sizeof savefile, "%s%s", file, SAVE_SUFFIX); ! if (unlink(savefile) != 0 && errno != ENOENT) { ! message(MT_NOTICE, "%s: remove failed: %s", savefile, SYSERR); ! return(NULL); } if (rename(file, savefile) != 0 && errno != ENOENT) { error("%s -> %s: rename failed: %s", file, savefile, SYSERR); --- 741,780 ---- return(NULL); } ! if (IS_ON(opts, DO_HISTORY)) { ! int i; ! struct stat st; ! /* ! * There is a race here, but the worst that can happen ! * is to lose a version of the file ! */ ! for (i = 1; i < 1000; i++) { ! (void) snprintf(savefile, sizeof(savefile), ! "%s;%.3d", file, i); ! if (stat(savefile, &st) == -1 && errno == ENOENT) ! break; ! } ! if (i == 1000) { ! message(MT_NOTICE, ! "%s: More than 1000 versions for %s; reusing 1\n", ! savefile, SYSERR); ! i = 1; ! (void) snprintf(savefile, sizeof(savefile), ! "%s;%.3d", file, i); ! } } + else { + (void) snprintf(savefile, sizeof(savefile), "%s%s", + file, SAVE_SUFFIX); + if (unlink(savefile) != 0 && errno != ENOENT) { + message(MT_NOTICE, "%s: remove failed: %s", + savefile, SYSERR); + return(NULL); + } + } + if (rename(file, savefile) != 0 && errno != ENOENT) { error("%s -> %s: rename failed: %s", file, savefile, SYSERR); *************** *** 724,760 **** } /* - * See if buf is all zeros (sparse check) - */ - static int iszeros (buf, size) - char *buf; - off_t size; - { - while (size > 0) { - if (*buf != CNULL) - return(0); - buf++; - size--; - } - - return(1); - } - - - /* * Receive a file */ ! static void recvfile(new, opts, mode, owner, group, mtime, atime, size) ! /*ARGSUSED*/ ! char *new; ! opt_t opts; ! int mode; ! char *owner, *group; ! time_t mtime; ! time_t atime; ! off_t size; { ! int f, wrerr, olderrno, lastwashole = 0, wassparse = 0; off_t i; char *cp; char *savefile = NULL; --- 785,797 ---- } /* * Receive a file */ ! static void ! recvfile(char *new, opt_t opts, int mode, char *owner, char *group, ! time_t mtime, time_t atime, off_t size) { ! int f, wrerr, olderrno; off_t i; char *cp; char *savefile = NULL; *************** *** 801,831 **** amt = BUFSIZ; if (i + amt > size) amt = size - i; ! if (IS_ON(opts, DO_SPARSE) && iszeros(buf, amt)) { ! if (lseek (f, amt, SEEK_CUR) < 0L) { ! olderrno = errno; ! wrerr++; ! } ! lastwashole = 1; ! wassparse++; ! } else { ! if (wrerr == 0 && xwrite(f, buf, amt) != amt) { ! olderrno = errno; ! wrerr++; ! } ! lastwashole = 0; ! } ! } ! ! if (lastwashole) { ! #if defined(HAVE_FTRUNCATE) ! if (write (f, "", 1) != 1 || ftruncate (f, size) < 0) ! #else ! /* Seek backwards one character and write a null. */ ! if (lseek (f, (off_t) -1, SEEK_CUR) < 0L ! || write (f, "", 1) != 1) ! #endif ! { olderrno = errno; wrerr++; } --- 838,844 ---- amt = BUFSIZ; if (i + amt > size) amt = size - i; ! if (wrerr == 0 && xwrite(f, buf, amt) != amt) { olderrno = errno; wrerr++; } *************** *** 861,866 **** --- 874,880 ---- errno = 0; if ((f2 = fopen(new, "r")) == NULL) { error("%s: open for read failed: %s", new, SYSERR); + (void) fclose(f1); (void) close(f); (void) unlink(new); return; *************** *** 916,922 **** * Try to save target file from being over-written */ if (IS_ON(opts, DO_SAVETARGETS)) ! if ((savefile = savetarget(target)) == NULL) { (void) unlink(new); return; } --- 930,936 ---- * Try to save target file from being over-written */ if (IS_ON(opts, DO_SAVETARGETS)) ! if ((savefile = savetarget(target, opts)) == NULL) { (void) unlink(new); return; } *************** *** 929,935 **** char *saveptr = ptarget; ptarget = &target[strlen(target)]; ! removefile(&statbuff); ptarget = saveptr; } --- 943,949 ---- char *saveptr = ptarget; ptarget = &target[strlen(target)]; ! removefile(&statbuff, 0); ptarget = saveptr; } *************** *** 937,973 **** * Install new (temporary) file as the actual target */ if (rename(new, target) < 0) { /* * If the rename failed due to "Text file busy", then * try to rename the target file and retry the rename. */ ! if (errno == ETXTBSY) { /* Save the target */ ! if ((savefile = savetarget(target)) != NULL) { /* Retry installing new file as target */ if (rename(new, target) < 0) { ! error("%s -> %s: rename failed: %s", ! new, target, SYSERR); /* Try to put back save file */ if (rename(savefile, target) < 0) ! error( ! "%s -> %s: rename failed: %s", ! savefile, target, ! SYSERR); } else message(MT_NOTICE, "%s: renamed to %s", target, savefile); } ! } else { ! error("%s -> %s: rename failed: %s", ! new, target, SYSERR); (void) unlink(new); } } - if (wassparse) - message (MT_NOTICE, "%s: was sparse", target); - if (IS_ON(opts, DO_COMPARE)) message(MT_REMOTE|MT_CHANGE, "%s: updated", target); else --- 951,1010 ---- * Install new (temporary) file as the actual target */ if (rename(new, target) < 0) { + static char fmt[] = "%s -> %s: rename failed: %s"; + struct stat stb; /* * If the rename failed due to "Text file busy", then * try to rename the target file and retry the rename. */ ! switch (errno) { ! case ETXTBSY: /* Save the target */ ! if ((savefile = savetarget(target, opts)) != NULL) { /* Retry installing new file as target */ if (rename(new, target) < 0) { ! error(fmt, new, target, SYSERR); /* Try to put back save file */ if (rename(savefile, target) < 0) ! error(fmt, ! savefile, target, SYSERR); ! (void) unlink(new); } else message(MT_NOTICE, "%s: renamed to %s", target, savefile); + /* + * XXX: We should remove the savefile here. + * But we are nice to nfs clients and + * we keep it. + */ } ! break; ! case EISDIR: ! /* ! * See if target is a directory and remove it if it is ! */ ! if (lstat(target, &stb) == 0) { ! if (S_ISDIR(stb.st_mode)) { ! char *optarget = ptarget; ! for (ptarget = target; *ptarget; ! ptarget++); ! /* If we failed to remove, we'll catch ! it later */ ! (void) removefile(&stb, 1); ! ptarget = optarget; ! } ! } ! if (rename(new, target) >= 0) ! break; ! /*FALLTHROUGH*/ ! ! default: ! error(fmt, new, target, SYSERR); (void) unlink(new); + break; } } if (IS_ON(opts, DO_COMPARE)) message(MT_REMOTE|MT_CHANGE, "%s: updated", target); else *************** *** 977,986 **** /* * Receive a directory */ ! static void recvdir(opts, mode, owner, group) ! opt_t opts; ! int mode; ! char *owner, *group; { static char lowner[100], lgroup[100]; char *cp; --- 1014,1021 ---- /* * Receive a directory */ ! static void ! recvdir(opt_t opts, int mode, char *owner, char *group) { static char lowner[100], lgroup[100]; char *cp; *************** *** 1040,1059 **** o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER : opts; ! if ((cp = getusername(stb.st_uid, target, o))) if (strcmp(owner, cp)) (void) strlcpy(lowner, cp, ! sizeof lowner); } if (!IS_ON(opts, DO_NOCHKGROUP) && group) { int o; o = (group[0] == ':') ? opts & DO_NUMCHKGROUP : opts; ! if ((cp = getgroupname(stb.st_gid, target, o))) if (strcmp(group, cp)) (void) strlcpy(lgroup, cp, ! sizeof lgroup); } /* --- 1075,1096 ---- o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER : opts; ! if ((cp = getusername(stb.st_uid, target, o)) ! != NULL) if (strcmp(owner, cp)) (void) strlcpy(lowner, cp, ! sizeof(lowner)); } if (!IS_ON(opts, DO_NOCHKGROUP) && group) { int o; o = (group[0] == ':') ? opts & DO_NUMCHKGROUP : opts; ! if ((cp = getgroupname(stb.st_gid, target, o)) ! != NULL) if (strcmp(group, cp)) (void) strlcpy(lgroup, cp, ! sizeof(lgroup)); } /* *************** *** 1065,1076 **** (cp = getusername(stb.st_uid, target, opts))) (void) strlcpy(lowner, cp, ! sizeof lowner); if (lgroup[0] == CNULL && (cp = getgroupname(stb.st_gid, target, opts))) (void) strlcpy(lgroup, cp, ! sizeof lgroup); if (IS_ON(opts, DO_VERIFY)) message(MT_NOTICE, --- 1102,1113 ---- (cp = getusername(stb.st_uid, target, opts))) (void) strlcpy(lowner, cp, ! sizeof(lowner)); if (lgroup[0] == CNULL && (cp = getgroupname(stb.st_gid, target, opts))) (void) strlcpy(lgroup, cp, ! sizeof(lgroup)); if (IS_ON(opts, DO_VERIFY)) message(MT_NOTICE, *************** *** 1128,1141 **** /* * Receive a link */ ! static void recvlink(new, opts, mode, size) ! char *new; ! opt_t opts; ! int mode; ! off_t size; { struct stat stb; char *optarget; off_t i; /* --- 1165,1177 ---- /* * Receive a link */ ! static void ! recvlink(char *new, opt_t opts, int mode, off_t size) { + char tbuf[MAXPATHLEN]; struct stat stb; char *optarget; + int uptodate; off_t i; /* *************** *** 1149,1154 **** --- 1185,1211 ---- return; } + uptodate = 0; + if ((i = readlink(target, tbuf, sizeof(tbuf))) != -1) { + tbuf[i] = '\0'; + if (i == size && strncmp(buf, tbuf, (int) size) == 0) + uptodate = 1; + } + mode &= 0777; + + if (IS_ON(opts, DO_VERIFY) || uptodate) { + if (uptodate) + message(MT_REMOTE|MT_INFO, ""); + else + message(MT_REMOTE|MT_INFO, "%s: need to update", + target); + if (IS_ON(opts, DO_COMPARE)) + return; + (void) sendcmd(C_END, NULL); + (void) response(); + return; + } + /* * Make new symlink using a temporary name */ *************** *** 1162,1198 **** } /* - * Do comparison of what link is pointing to if enabled - */ - mode &= 0777; - if (IS_ON(opts, DO_COMPARE)) { - char tbuf[MAXPATHLEN]; - - if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1) - tbuf[i] = '\0'; - if (i != -1 && i == size && strncmp(buf, tbuf, (size_t) size) == 0) { - (void) unlink(new); - ack(); - return; - } - if (IS_ON(opts, DO_VERIFY)) { - (void) unlink(new); - message(MT_REMOTE|MT_INFO, "%s: need to update", - target); - (void) sendcmd(C_END, NULL); - (void) response(); - return; - } - } - - /* * See if target is a directory and remove it if it is */ if (lstat(target, &stb) == 0) { if (S_ISDIR(stb.st_mode)) { optarget = ptarget; for (ptarget = target; *ptarget; ptarget++); ! if (removefile(&stb) < 0) { ptarget = optarget; (void) unlink(new); (void) sendcmd(C_END, NULL); --- 1219,1231 ---- } /* * See if target is a directory and remove it if it is */ if (lstat(target, &stb) == 0) { if (S_ISDIR(stb.st_mode)) { optarget = ptarget; for (ptarget = target; *ptarget; ptarget++); ! if (removefile(&stb, 0) < 0) { ptarget = optarget; (void) unlink(new); (void) sendcmd(C_END, NULL); *************** *** 1215,1224 **** return; } ! if (IS_ON(opts, DO_COMPARE)) ! message(MT_REMOTE|MT_CHANGE, "%s: updated", target); ! else ! ack(); /* * Indicate end of receive operation --- 1248,1254 ---- return; } ! message(MT_REMOTE|MT_CHANGE, "%s: updated", target); /* * Indicate end of receive operation *************** *** 1230,1243 **** /* * Creat a hard link to existing file. */ ! static void hardlink(cmd) ! char *cmd; { struct stat stb; int exists = 0; ! char *oldname, *newname; char *cp = cmd; static char expbuf[BUFSIZ]; /* Skip over opts */ (void) strtol(cp, &cp, 8); --- 1260,1274 ---- /* * Creat a hard link to existing file. */ ! static void ! hardlink(char *cmd) { struct stat stb; int exists = 0; ! char *xoldname, *xnewname; char *cp = cmd; static char expbuf[BUFSIZ]; + char oldname[BUFSIZ], newname[BUFSIZ]; /* Skip over opts */ (void) strtol(cp, &cp, 8); *************** *** 1246,1268 **** return; } ! oldname = strtok(cp, " "); ! if (oldname == NULL) { error("hardlink: oldname name not delimited"); return; } ! newname = strtok(NULL, " "); ! if (newname == NULL) { error("hardlink: new name not specified"); return; } if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) { error("hardlink: tilde expansion failed"); return; } - oldname = expbuf; if (catname && cattarget(newname) < 0) { error("Cannot set newname target."); --- 1277,1308 ---- return; } ! xoldname = strtok(cp, " "); ! if (xoldname == NULL) { error("hardlink: oldname name not delimited"); return; } ! if (DECODE(oldname, xoldname) == -1) { ! error("hardlink: Cannot decode oldname"); ! return; ! } ! ! xnewname = strtok(NULL, " "); ! if (xnewname == NULL) { error("hardlink: new name not specified"); return; } + if (DECODE(newname, xnewname) == -1) { + error("hardlink: Cannot decode newname"); + return; + } + if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) { error("hardlink: tilde expansion failed"); return; } if (catname && cattarget(newname) < 0) { error("Cannot set newname target."); *************** *** 1287,1293 **** error("%s: unlink failed: %s", target, SYSERR); return; } ! if (link(oldname, target) < 0) { error("%s: cannot link to %s: %s", target, oldname, SYSERR); return; } --- 1327,1333 ---- error("%s: unlink failed: %s", target, SYSERR); return; } ! if (link(expbuf, target) < 0) { error("%s: cannot link to %s: %s", target, oldname, SYSERR); return; } *************** *** 1302,1309 **** * SC_FREESPACE - Set minimium free space of filesystem * SC_FREEFILES - Set minimium free number of files of filesystem */ ! static void setconfig(cmd) ! char *cmd; { char *cp = cmd; char *estr; --- 1342,1349 ---- * SC_FREESPACE - Set minimium free space of filesystem * SC_FREEFILES - Set minimium free number of files of filesystem */ ! static void ! setconfig(char *cmd) { char *cp = cmd; char *estr; *************** *** 1315,1321 **** */ if (!fromhost) { fromhost = xstrdup(cp); ! message(MT_SYSLOG, "startup for %s", fromhost); #if defined(SETARGS) || defined(HAVE_SETPROCTITLE) setproctitle("serving %s", cp); #endif /* SETARGS || HAVE_SETPROCTITLE */ --- 1355,1361 ---- */ if (!fromhost) { fromhost = xstrdup(cp); ! message(MT_SYSLOG, "startup for %s", fromhost); #if defined(SETARGS) || defined(HAVE_SETPROCTITLE) setproctitle("serving %s", cp); #endif /* SETARGS || HAVE_SETPROCTITLE */ *************** *** 1323,1329 **** break; case SC_FREESPACE: /* Minimium free space */ ! if (!isdigit(*cp)) { fatalerr("Expected digit, got '%s'.", cp); return; } --- 1363,1369 ---- break; case SC_FREESPACE: /* Minimium free space */ ! if (!isdigit((unsigned char)*cp)) { fatalerr("Expected digit, got '%s'.", cp); return; } *************** *** 1331,1337 **** break; case SC_FREEFILES: /* Minimium free files */ ! if (!isdigit(*cp)) { fatalerr("Expected digit, got '%s'.", cp); return; } --- 1371,1377 ---- break; case SC_FREEFILES: /* Minimium free files */ ! if (!isdigit((unsigned char)*cp)) { fatalerr("Expected digit, got '%s'.", cp); return; } *************** *** 1339,1351 **** break; case SC_LOGGING: /* Logging options */ ! if ((estr = msgparseopts(cp, TRUE))) { fatalerr("Bad message option string (%s): %s", cp, estr); return; } break; default: message(MT_NOTICE, "Unknown config command \"%s\".", cp-1); return; --- 1379,1399 ---- break; case SC_LOGGING: /* Logging options */ ! if ((estr = msgparseopts(cp, TRUE)) != NULL) { fatalerr("Bad message option string (%s): %s", cp, estr); return; } break; + case SC_DEFOWNER: + (void) strlcpy(defowner, cp, sizeof(defowner)); + break; + + case SC_DEFGROUP: + (void) strlcpy(defgroup, cp, sizeof(defgroup)); + break; + default: message(MT_NOTICE, "Unknown config command \"%s\".", cp-1); return; *************** *** 1355,1363 **** /* * Receive something */ ! static void recvit(cmd, type) ! char *cmd; ! int type; { int mode; opt_t opts; --- 1403,1410 ---- /* * Receive something */ ! static void ! recvit(char *cmd, int type) { int mode; opt_t opts; *************** *** 1365,1370 **** --- 1412,1418 ---- time_t mtime, atime; char *owner, *group, *file; char new[MAXPATHLEN]; + char fileb[MAXPATHLEN]; long freespace = -1, freefiles = -1; char *cp = cmd; *************** *** 1432,1445 **** } /* ! * Get file name. Can't use strtok() since there could * be white space in the file name. */ ! file = group + strlen(group) + 1; ! if (file == NULL) { error("recvit: no file name"); return; } debugmsg(DM_MISC, "recvit: opts = %04o mode = %04o size = %d mtime = %d", --- 1480,1498 ---- } /* ! * Get file name. Can't use strtok() since there could * be white space in the file name. */ ! if (DECODE(fileb, group + strlen(group) + 1) == -1) { ! error("recvit: Cannot decode file name"); ! return; ! } ! ! if (fileb[0] == '\0') { error("recvit: no file name"); return; } + file = fileb; debugmsg(DM_MISC, "recvit: opts = %04o mode = %04o size = %d mtime = %d", *************** *** 1449,1463 **** owner, group, file, catname, (type == S_IFDIR) ? 1 : 0); if (type == S_IFDIR) { ! if (catname >= sizeof(sptarget)) { error("%s: too many directory levels", target); return; } sptarget[catname] = ptarget; if (catname++) { *ptarget++ = '/'; ! while ((*ptarget++ = *file++)) ! ; ptarget--; } } else { --- 1502,1516 ---- owner, group, file, catname, (type == S_IFDIR) ? 1 : 0); if (type == S_IFDIR) { ! if ((size_t) catname >= sizeof(sptarget)) { error("%s: too many directory levels", target); return; } sptarget[catname] = ptarget; if (catname++) { *ptarget++ = '/'; ! while ((*ptarget++ = *file++) != '\0') ! continue; ptarget--; } } else { *************** *** 1470,1481 **** } file = strrchr(target, '/'); if (file == NULL) ! (void) strlcpy(new, tempname, sizeof new); else if (file == target) ! (void) snprintf(new, sizeof new, "/%s", tempname); else { *file = CNULL; ! (void) snprintf(new, sizeof new, "%s/%s", target, tempname); *file = '/'; } (void) mktemp(new); --- 1523,1535 ---- } file = strrchr(target, '/'); if (file == NULL) ! (void) strlcpy(new, tempname, sizeof(new)); else if (file == target) ! (void) snprintf(new, sizeof(new), "/%s", tempname); else { *file = CNULL; ! (void) snprintf(new, sizeof(new), "%s/%s", target, ! tempname); *file = '/'; } (void) mktemp(new); *************** *** 1535,1548 **** } /* * Set target information */ ! static void settarget(cmd, isdir) ! char *cmd; ! int isdir; { char *cp = cmd; opt_t opts; catname = isdir; --- 1589,1681 ---- } /* + * Chmog something + */ + static void + dochmog(char *cmd) + { + int mode; + opt_t opts; + char *owner, *group, *file; + char *cp = cmd; + char fileb[MAXPATHLEN]; + + /* + * Get rdist option flags + */ + opts = strtol(cp, &cp, 8); + if (*cp++ != ' ') { + error("dochmog: options not delimited"); + return; + } + + /* + * Get file mode + */ + mode = strtol(cp, &cp, 8); + if (*cp++ != ' ') { + error("dochmog: mode not delimited"); + return; + } + + /* + * Get file owner name + */ + owner = strtok(cp, " "); + if (owner == NULL) { + error("dochmog: owner name not delimited"); + return; + } + + /* + * Get file group name + */ + group = strtok(NULL, " "); + if (group == NULL) { + error("dochmog: group name not delimited"); + return; + } + + /* + * Get file name. Can't use strtok() since there could + * be white space in the file name. + */ + if (DECODE(fileb, group + strlen(group) + 1) == -1) { + error("dochmog: Cannot decode file name"); + return; + } + + if (fileb[0] == '\0') { + error("dochmog: no file name"); + return; + } + file = fileb; + + debugmsg(DM_MISC, + "dochmog: opts = %04o mode = %04o", opts, mode); + debugmsg(DM_MISC, + "dochmog: owner = '%s' group = '%s' file = '%s' catname = %d", + owner, group, file, catname); + + if (catname && cattarget(file) < 0) { + error("Cannot set newname target."); + return; + } + + (void) fchog(-1, target, owner, group, mode); + + ack(); + } + + /* * Set target information */ ! static void ! settarget(char *cmd, int isdir) { char *cp = cmd; opt_t opts; + char file[BUFSIZ]; catname = isdir; *************** *** 1556,1561 **** --- 1689,1699 ---- } options = opts; + if (DECODE(file, cp) == -1) { + error("settarget: Cannot decode target name"); + return; + } + /* * Handle target */ *************** *** 1571,1577 **** /* * Cleanup in preparation for exiting. */ ! extern void cleanup() { /* We don't need to do anything */ } --- 1709,1716 ---- /* * Cleanup in preparation for exiting. */ ! void ! cleanup(int dummy) { /* We don't need to do anything */ } *************** *** 1579,1603 **** /* * Server routine to read requests and process them. */ ! extern void server() { static char cmdbuf[BUFSIZ]; char *cp; int n; extern jmp_buf finish_jmpbuf; ! if (setjmp(finish_jmpbuf)) { ! setjmp_ok = FALSE; return; - } - setjmp_ok = TRUE; (void) signal(SIGHUP, sighandler); (void) signal(SIGINT, sighandler); (void) signal(SIGQUIT, sighandler); (void) signal(SIGTERM, sighandler); (void) signal(SIGPIPE, sighandler); (void) umask(oumask = umask(0)); ! (void) strlcpy(tempname, _RDIST_TMP, sizeof tempname); if (fromhost) { message(MT_SYSLOG, "Startup for %s", fromhost); #if defined(SETARGS) --- 1718,1740 ---- /* * Server routine to read requests and process them. */ ! void ! server(void) { static char cmdbuf[BUFSIZ]; char *cp; int n; extern jmp_buf finish_jmpbuf; ! if (setjmp(finish_jmpbuf)) return; (void) signal(SIGHUP, sighandler); (void) signal(SIGINT, sighandler); (void) signal(SIGQUIT, sighandler); (void) signal(SIGTERM, sighandler); (void) signal(SIGPIPE, sighandler); (void) umask(oumask = umask(0)); ! (void) strlcpy(tempname, _RDIST_TMP, sizeof(tempname)); if (fromhost) { message(MT_SYSLOG, "Startup for %s", fromhost); #if defined(SETARGS) *************** *** 1611,1630 **** (void) sendcmd(S_VERSION, NULL); if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) { - setjmp_ok = FALSE; error("server: expected control record"); return; } ! if (cmdbuf[0] != S_VERSION || !isdigit(cmdbuf[1])) { ! setjmp_ok = FALSE; error("Expected version command, received: \"%s\".", cmdbuf); return; } proto_version = atoi(&cmdbuf[1]); if (proto_version != VERSION) { - setjmp_ok = FALSE; error("Protocol version %d is not supported.", proto_version); return; } --- 1748,1764 ---- (void) sendcmd(S_VERSION, NULL); if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) { error("server: expected control record"); return; } ! if (cmdbuf[0] != S_VERSION || !isdigit((unsigned char)cmdbuf[1])) { error("Expected version command, received: \"%s\".", cmdbuf); return; } proto_version = atoi(&cmdbuf[1]); if (proto_version != VERSION) { error("Protocol version %d is not supported.", proto_version); return; } *************** *** 1637,1646 **** */ for ( ; ; ) { n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE); ! if (n == -1) { /* EOF */ ! setjmp_ok = FALSE; return; - } if (n == 0) { error("server: expected control record"); continue; --- 1771,1778 ---- */ for ( ; ; ) { n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE); ! if (n == -1) /* EOF */ return; if (n == 0) { error("server: expected control record"); continue; *************** *** 1703,1713 **** docmdspecial(); continue; ! #ifdef DOCHMOD ! case C_CHMOD: /* Set mode */ ! dochmod(cp); continue; - #endif /* DOCHMOD */ case C_ERRMSG: /* Normal error message */ if (cp && *cp) --- 1835,1843 ---- docmdspecial(); continue; ! case C_CHMOG: /* Set owner, group, mode */ ! dochmog(cp); continue; case C_ERRMSG: /* Normal error message */ if (cp && *cp) *************** *** 1717,1723 **** case C_FERRMSG: /* Fatal error message */ if (cp && *cp) message(MT_FERROR|MT_NOREMOTE, "%s", cp); - setjmp_ok = FALSE; return; default: --- 1847,1852 ----