xref: /openbsd/usr.bin/rdist/client.c (revision 3aaa63eb)
1 /*	$OpenBSD: client.c,v 1.37 2019/06/28 13:35:03 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1983 Regents of the University of California.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <ctype.h>
33 #include <dirent.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 
41 #include "client.h"
42 #include "gram.h"
43 
44 /*
45  * Routines used in client mode to communicate with remove server.
46  */
47 
48 
49 /*
50  * Update status
51  */
52 #define US_NOTHING 	0	/* No update needed */
53 #define US_NOENT	1	/* Entry does not exist */
54 #define US_OUTDATE	2	/* Entry is out of date */
55 #define US_DOCOMP	3	/* Do a binary comparison */
56 #define US_CHMOG	4	/* Modes or ownership of file differ */
57 
58 struct	linkbuf *ihead = NULL;	/* list of files with more than one link */
59 char	buf[BUFSIZ];		/* general purpose buffer */
60 u_char	respbuff[BUFSIZ];	/* Response buffer */
61 char	target[BUFSIZ];		/* target/source directory name */
62 char	source[BUFSIZ];		/* source directory name */
63 char	*ptarget;		/* pointer to end of target name */
64 char	*Tdest;			/* pointer to last T dest*/
65 struct namelist	*updfilelist = NULL; /* List of updated files */
66 
67 static void runspecial(char *, opt_t, char *, int);
68 static void addcmdspecialfile(char *, char *, int);
69 static void freecmdspecialfiles(void);
70 static struct linkbuf *linkinfo(struct stat *);
71 static int sendhardlink(opt_t, struct linkbuf *, char *, int);
72 static int sendfile(char *, opt_t, struct stat *, char *, char *, int);
73 static int rmchk(opt_t);
74 static int senddir(char *, opt_t, struct stat *, char *, char *, int);
75 static int sendlink(char *, opt_t, struct stat *, char *, char *, int);
76 static int update(char *, opt_t, struct stat *);
77 static int dostat(char *, struct stat *, opt_t);
78 static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *);
79 static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *);
80 static int sendit(char *, opt_t, int);
81 
82 /*
83  * return remote file pathname (relative from target)
84  */
85 char *
remfilename(char * src,char * dest,char * path,char * rname,int destdir)86 remfilename(char *src, char *dest, char *path, char *rname, int destdir)
87 {
88 	char *lname, *cp;
89 	static char buff[BUFSIZ];
90 	int srclen, pathlen;
91 	char *p;
92 
93 
94 	debugmsg(DM_MISC,
95 		 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
96 		A(src), A(dest), A(path), A(rname), destdir);
97 
98 	if (!dest) {
99 		debugmsg(DM_MISC, "remfilename: remote filename=%s\n", path);
100 		return(path);
101 	}
102 
103 	if (!destdir) {
104 		debugmsg(DM_MISC, "remfilename: remote filename=%s\n", dest);
105 		return(dest);
106 	}
107 
108 	buff[0] = CNULL;
109 	lname = buff;
110 	if (path && *path) {
111 		cp = strrchr(path, '/');
112  		if (cp == NULL)
113 			(void) snprintf(buff, sizeof(buff), "%s/%s", dest, path);
114 		else {
115 			srclen = strlen(src);
116 			pathlen = strlen(path);
117 			if (srclen >= pathlen)
118 				cp++; /* xbasename(path) */
119 			else {
120 				if (filelist && filelist->n_next == NULL)
121 					/* path relative to src */
122 					cp = path + srclen;
123 				else {
124 					if ((p = strrchr(src, '/')))
125 						cp = path + srclen - strlen(p);
126 					else
127 						cp = path;
128 				}
129 			}
130 			if ((*cp != '/') && *cp)
131 				(void) snprintf(buff, sizeof(buff), "%s/%s",
132 						dest, cp);
133 			else
134 				(void) snprintf(buff, sizeof(buff), "%s%s",
135 						dest, cp);
136 		}
137 	} else
138 		(void) strlcpy(lname, dest, buf + sizeof buff - lname);
139 
140 	debugmsg(DM_MISC, "remfilename: remote filename=%s\n", lname);
141 
142 	return(lname);
143 }
144 
145 /*
146  * Return true if name is in the list.
147  */
148 int
inlist(struct namelist * list,char * file)149 inlist(struct namelist *list, char *file)
150 {
151 	struct namelist *nl;
152 
153 	for (nl = list; nl != NULL; nl = nl->n_next)
154 		if (strcmp(file, nl->n_name) == 0)
155 			return(1);
156 	return(0);
157 }
158 
159 /*
160  * Run any special commands for this file
161  */
162 static void
runspecial(char * starget,opt_t opts,char * rname,int destdir)163 runspecial(char *starget, opt_t opts, char *rname, int destdir)
164 {
165 	struct subcmd *sc;
166 	char *rfile;
167 
168  	rfile = remfilename(source, Tdest, target, rname, destdir);
169 
170 	for (sc = subcmds; sc != NULL; sc = sc->sc_next) {
171 		if (sc->sc_type != SPECIAL)
172 			continue;
173 		if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
174 			continue;
175 		message(MT_CHANGE, "special \"%s\"", sc->sc_name);
176 		if (IS_ON(opts, DO_VERIFY))
177 			continue;
178 		(void) sendcmd(C_SPECIAL,
179 			"%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
180 			E_LOCFILE, starget,
181 			E_REMFILE, rfile,
182 			E_BASEFILE, xbasename(rfile),
183 			E_LOCFILE, E_REMFILE, E_BASEFILE,
184 			sc->sc_name);
185 		while (response() > 0)
186 			;
187 	}
188 }
189 
190 /*
191  * If we're doing a target with a "cmdspecial" in it, then
192  * save the name of the file being updated for use with "cmdspecial".
193  */
194 static void
addcmdspecialfile(char * starget,char * rname,int destdir)195 addcmdspecialfile(char *starget, char *rname, int destdir)
196 {
197 	char *rfile;
198 	struct namelist *new;
199 	struct subcmd *sc;
200 	int isokay = 0;
201 
202  	rfile = remfilename(source, Tdest, target, rname, destdir);
203 
204 	for (sc = subcmds; sc != NULL && !isokay; sc = sc->sc_next) {
205 		if (sc->sc_type != CMDSPECIAL)
206 			continue;
207 		if (sc->sc_args != NULL && !inlist(sc->sc_args, starget))
208 			continue;
209 		isokay = TRUE;
210 	}
211 
212 	if (isokay) {
213 		new = xmalloc(sizeof *new);
214 		new->n_name = xstrdup(rfile);
215 		new->n_regex = NULL;
216 		new->n_next = updfilelist;
217 		updfilelist = new;
218 	}
219 }
220 
221 /*
222  * Free the file list
223  */
224 static void
freecmdspecialfiles(void)225 freecmdspecialfiles(void)
226 {
227 	struct namelist *ptr, *save;
228 
229 	for (ptr = updfilelist; ptr; ) {
230 		if (ptr->n_name) (void) free(ptr->n_name);
231 		save = ptr->n_next;
232 		(void) free(ptr);
233 		if (save)
234 			ptr = save->n_next;
235 		else
236 			ptr = NULL;
237 	}
238 	updfilelist = NULL;
239 }
240 
241 /*
242  * Run commands for an entire cmd
243  */
244 void
runcmdspecial(struct cmd * cmd,opt_t opts)245 runcmdspecial(struct cmd *cmd, opt_t opts)
246 {
247 	struct subcmd *sc;
248 	struct namelist *f;
249 	int first = TRUE;
250 
251 	for (sc = cmd->c_cmds; sc != NULL; sc = sc->sc_next) {
252 		if (sc->sc_type != CMDSPECIAL)
253 			continue;
254 		message(MT_CHANGE, "cmdspecial \"%s\"", sc->sc_name);
255 		if (IS_ON(opts, DO_VERIFY))
256 			continue;
257 		/* Send all the file names */
258 		for (f = updfilelist; f != NULL; f = f->n_next) {
259 			if (first) {
260 				(void) sendcmd(C_CMDSPECIAL, NULL);
261 				if (response() < 0)
262 					return;
263 				first = FALSE;
264 			}
265 			(void) sendcmd(RC_FILE, "%s", f->n_name);
266 			if (response() < 0)
267 				return;
268 		}
269 		if (first) {
270 			(void) sendcmd(C_CMDSPECIAL, NULL);
271 			if (response() < 0)
272 				return;
273 			first = FALSE;
274 		}
275 		/* Send command to run and wait for it to complete */
276 		(void) sendcmd(RC_COMMAND, "%s", sc->sc_name);
277 		while (response() > 0)
278 			;
279 		first = TRUE;	/* Reset in case there are more CMDSPECIAL's */
280 	}
281 	freecmdspecialfiles();
282 }
283 
284 /*
285  * For security, reject filenames that contains a newline
286  */
287 int
checkfilename(char * name)288 checkfilename(char *name)
289 {
290 	char *cp;
291 
292 	if (strchr(name, '\n')) {
293 		for (cp = name; *cp; cp++)
294 			if (*cp == '\n')
295 				*cp = '?';
296 		message(MT_NERROR,
297 			"Refuse to handle filename containing newline: %s",
298 			name);
299 		return(-1);
300 	}
301 
302 	return(0);
303 }
304 
305 void
freelinkinfo(struct linkbuf * lp)306 freelinkinfo(struct linkbuf *lp)
307 {
308 	free(lp->pathname);
309 	free(lp->src);
310 	free(lp->target);
311 	free(lp);
312 }
313 
314 /*
315  * Save and retrieve hard link info
316  */
317 static struct linkbuf *
linkinfo(struct stat * statp)318 linkinfo(struct stat *statp)
319 {
320 	struct linkbuf *lp;
321 
322 	/* XXX - linear search doesn't scale with many links */
323 	for (lp = ihead; lp != NULL; lp = lp->nextp)
324 		if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) {
325 			lp->count--;
326 			return(lp);
327 		}
328 
329 	lp = xmalloc(sizeof(*lp));
330 	lp->nextp = ihead;
331 	ihead = lp;
332 	lp->inum = statp->st_ino;
333 	lp->devnum = statp->st_dev;
334 	lp->count = statp->st_nlink - 1;
335 	lp->pathname = xstrdup(target);
336 	lp->src = xstrdup(source);
337 	if (Tdest)
338 		lp->target = xstrdup(Tdest);
339 	else
340 		lp->target = NULL;
341 
342 	return(NULL);
343 }
344 
345 /*
346  * Send a hardlink
347  */
348 static int
sendhardlink(opt_t opts,struct linkbuf * lp,char * rname,int destdir)349 sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir)
350 {
351 	static char buff[PATH_MAX];
352 	char *lname;	/* name of file to link to */
353 	char ername[PATH_MAX*4], elname[PATH_MAX*4];
354 
355 	debugmsg(DM_MISC,
356 	       "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
357 		rname, lp->pathname ? lp->pathname : "",
358 		lp->src ? lp->src : "", lp->target ? lp->target : "");
359 
360 	if (lp->target == NULL)
361 		lname = lp->pathname;
362 	else {
363 		lname = buff;
364 		strlcpy(lname, remfilename(lp->src, lp->target,
365 					  lp->pathname, rname,
366 					  destdir), sizeof(buff));
367 		debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
368 	}
369 	ENCODE(elname, lname);
370 	ENCODE(ername, rname);
371 	(void) sendcmd(C_RECVHARDLINK, "%o %s %s", opts, elname, ername);
372 
373 	return(response());
374 }
375 
376 /*
377  * Send a file
378  */
379 static int
sendfile(char * rname,opt_t opts,struct stat * stb,char * user,char * group,int destdir)380 sendfile(char *rname, opt_t opts, struct stat *stb, char *user,
381 	 char *group, int destdir)
382 {
383 	int goterr, f;
384 	off_t i;
385 	char ername[PATH_MAX*4];
386 
387 	if (stb->st_nlink > 1) {
388 		struct linkbuf *lp;
389 
390 		if ((lp = linkinfo(stb)) != NULL)
391 			return(sendhardlink(opts, lp, rname, destdir));
392 	}
393 
394 	if ((f = open(target, O_RDONLY)) == -1) {
395 		error("%s: open for read failed: %s", target, SYSERR);
396 		return(-1);
397 	}
398 
399 	/*
400 	 * Send file info
401 	 */
402 	ENCODE(ername, rname);
403 
404 	(void) sendcmd(C_RECVREG, "%o %04o %lld %lld %lld %s %s %s",
405 		       opts, stb->st_mode & 07777, (long long) stb->st_size,
406 		       (long long)stb->st_mtime, (long long)stb->st_atime,
407 		       user, group, ername);
408 	if (response() < 0) {
409 		(void) close(f);
410 		return(-1);
411 	}
412 
413 
414 	debugmsg(DM_MISC, "Send file '%s' %lld bytes\n", rname,
415 		 (long long) stb->st_size);
416 
417 	/*
418 	 * Set remote time out alarm handler.
419 	 */
420 	(void) signal(SIGALRM, sighandler);
421 
422 	/*
423 	 * Actually transfer the file
424 	 */
425 	goterr = 0;
426 	for (i = 0; i < stb->st_size; i += BUFSIZ) {
427 		off_t amt = BUFSIZ;
428 
429 		(void) alarm(rtimeout);
430 		if (i + amt > stb->st_size)
431 			amt = stb->st_size - i;
432 		if (read(f, buf, (size_t) amt) != (ssize_t) amt) {
433 			error("%s: File changed size", target);
434 			err();
435 			++goterr;
436 			/*
437 			 * XXX - We have to keep going because the
438 			 * server expects to receive a fixed number
439 			 * of bytes that we specified as the file size.
440 			 * We need Out Of Band communication to handle
441 			 * this situation gracefully.
442 			 */
443 		}
444 		if (xwrite(rem_w, buf, (size_t) amt) < 0) {
445 			error("%s: Error writing to client: %s",
446 			      target, SYSERR);
447 			err();
448 			++goterr;
449 			break;
450 		}
451 		(void) alarm(0);
452 	}
453 
454 	(void) alarm(0);	/* Insure alarm is off */
455 	(void) close(f);
456 
457 	debugmsg(DM_MISC, "Send file '%s' %s.\n",
458 		 (goterr) ? "failed" : "complete", rname);
459 
460 	/*
461 	 * Check for errors and end send
462 	 */
463 	if (goterr)
464 		return(-1);
465 	else {
466 		ack();
467 		f = response();
468 		if (f < 0)
469 			return(-1);
470 		else if (f == 0 && IS_ON(opts, DO_COMPARE))
471 			return(0);
472 
473 		runspecial(target, opts, rname, destdir);
474 		addcmdspecialfile(target, rname, destdir);
475 
476 		return(0);
477 	}
478 }
479 
480 /*
481  * Check for files on the machine being updated that are not on the master
482  * machine and remove them.
483  *
484  * Return < 0 on error.
485  * Return 0 if nothing happened.
486  * Return > 0 if anything is updated.
487  */
488 static int
rmchk(opt_t opts)489 rmchk(opt_t opts)
490 {
491 	u_char *s;
492 	struct stat stb;
493 	int didupdate = 0;
494 	int n;
495 	char targ[PATH_MAX*4];
496 
497 	debugmsg(DM_CALL, "rmchk()\n");
498 
499 	/*
500 	 * Tell the remote to clean the files from the last directory sent.
501 	 */
502 	(void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
503 	if (response() < 0)
504 		return(-1);
505 
506 	for ( ; ; ) {
507 		n = remline(s = respbuff, sizeof(respbuff), TRUE);
508 		if (n <= 0) {
509 			error("rmchk: unexpected control record");
510 			return(didupdate);
511 		}
512 
513 		switch (*s++) {
514 		case CC_QUERY: /* Query if file should be removed */
515 			/*
516 			 * Return the following codes to remove query.
517 			 * CC_NO -- file exists - DON'T remove.
518 			 * CC_YES -- file doesn't exist - REMOVE.
519 			 */
520 			if (DECODE(targ, (char *) s) == -1) {
521 				error("rmchk: cannot decode file");
522 				return(-1);
523 			}
524 			(void) snprintf(ptarget,
525 					sizeof(target) - (ptarget - target),
526 					"%s%s",
527 				        (ptarget[-1] == '/' ? "" : "/"),
528 				        targ);
529 			debugmsg(DM_MISC, "check %s\n", target);
530 			if (except(target))
531 				(void) sendcmd(CC_NO, NULL);
532 			else if (lstat(target, &stb) == -1) {
533 				if (sendcmd(CC_YES, NULL) == 0)
534 					didupdate = 1;
535 			} else
536 				(void) sendcmd(CC_NO, NULL);
537 			break;
538 
539 		case CC_END:
540 			*ptarget = CNULL;
541 			ack();
542 			return(didupdate);
543 
544 		case C_LOGMSG:
545 			if (n > 0)
546 				message(MT_INFO, "%s", s);
547 			break;
548 
549 		case C_NOTEMSG:
550 			if (n > 0)
551 				message(MT_NOTICE, "%s", s);
552 			break;
553 			/* Goto top of loop */
554 
555 		case C_ERRMSG:
556 			message(MT_NERROR, "%s", s);
557 			return(didupdate);
558 
559 		case C_FERRMSG:
560 			message(MT_FERROR, "%s", s);
561 			finish();
562 
563 		default:
564 			error("rmchk: unexpected response '%s'", respbuff);
565 			err();
566 		}
567 	}
568 	/*NOTREACHED*/
569 }
570 
571 /*
572  * Send a directory
573  *
574  * Return < 0 on error.
575  * Return 0 if nothing happened.
576  * Return > 0 if anything is updated.
577  */
578 static int
senddir(char * rname,opt_t opts,struct stat * stb,char * user,char * group,int destdir)579 senddir(char *rname, opt_t opts, struct stat *stb, char *user,
580 	char *group, int destdir)
581 {
582 	struct dirent *dp;
583 	DIR *d;
584 	char *optarget, *cp;
585 	int len;
586 	int didupdate = 0;
587 	char ername[PATH_MAX*4];
588 
589 	/*
590 	 * Send recvdir command in recvit() format.
591 	 */
592 	ENCODE(ername, rname);
593 	(void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s",
594 		       opts, stb->st_mode & 07777, user, group, ername);
595 	if (response() < 0)
596 		return(-1);
597 
598 	optarget = ptarget;
599 
600 	/*
601 	 * Don't descend into directory
602 	 */
603 	if (IS_ON(opts, DO_NODESCEND)) {
604 		didupdate = 0;
605 		goto out;
606 	}
607 
608 	if (IS_ON(opts, DO_REMOVE))
609 		if (rmchk(opts) > 0)
610 			++didupdate;
611 
612 	if ((d = opendir(target)) == NULL) {
613 		error("%s: opendir failed: %s", target, SYSERR);
614 		didupdate = -1;
615 		goto out;
616 	}
617 
618 	len = ptarget - target;
619 	while ((dp = readdir(d)) != NULL) {
620 		if (!strcmp(dp->d_name, ".") ||
621 		    !strcmp(dp->d_name, ".."))
622 			continue;
623 		if (len + 1 + (int) strlen(dp->d_name) >= PATH_MAX - 1) {
624 			error("%s/%s: Name too long", target,
625 			      dp->d_name);
626 			continue;
627 		}
628 		ptarget = optarget;
629 		if (ptarget[-1] != '/')
630 			*ptarget++ = '/';
631 		cp = dp->d_name;
632 		while ((*ptarget++ = *cp++) != '\0')
633 			continue;
634 		ptarget--;
635 		if (sendit(dp->d_name, opts, destdir) > 0)
636 			didupdate = 1;
637 	}
638 	(void) closedir(d);
639 
640 out:
641 	(void) sendcmd(C_END, NULL);
642 	(void) response();
643 
644 	ptarget = optarget;
645 	*ptarget = CNULL;
646 
647 	return(didupdate);
648 }
649 
650 /*
651  * Send a link
652  */
653 static int
sendlink(char * rname,opt_t opts,struct stat * stb,char * user,char * group,int destdir)654 sendlink(char *rname, opt_t opts, struct stat *stb, char *user,
655 	 char *group, int destdir)
656 {
657 	int f, n;
658 	static char tbuf[BUFSIZ];
659 	char lbuf[PATH_MAX];
660 	u_char *s;
661 	char ername[PATH_MAX*4];
662 
663 	debugmsg(DM_CALL, "sendlink(%s, %#x, stb, %d)\n", rname, opts, destdir);
664 
665 	if (stb->st_nlink > 1) {
666 		struct linkbuf *lp;
667 
668 		if ((lp = linkinfo(stb)) != NULL)
669 			return(sendhardlink(opts, lp, rname, destdir));
670 	}
671 
672 	/*
673 	 * Gather and send basic link info
674 	 */
675 	ENCODE(ername, rname);
676 	(void) sendcmd(C_RECVSYMLINK, "%o %04o %lld %lld %lld %s %s %s",
677 		       opts, stb->st_mode & 07777, (long long) stb->st_size,
678 		       (long long)stb->st_mtime, (long long)stb->st_atime,
679 		       user, group, ername);
680 	if (response() < 0)
681 		return(-1);
682 
683 	/*
684 	 * Gather and send additional link info
685 	 */
686 	if ((n = readlink(target, lbuf, sizeof(lbuf)-1)) != -1)
687 		lbuf[n] = '\0';
688 	else {
689 		error("%s: readlink failed", target);
690 		err();
691 	}
692 	(void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf);
693 	ENCODE(ername, tbuf);
694 	(void) sendcmd(C_NONE, "%s\n", ername);
695 
696 	if (n != stb->st_size) {
697 		error("%s: file changed size", target);
698 		err();
699 	} else
700 		ack();
701 
702 	/*
703 	 * Check response
704 	 */
705 	f = response();
706 	if (f < 0)
707 		return(-1);
708 	else if (f == 0 && IS_ON(opts, DO_COMPARE))
709 		return(0);
710 
711 	/*
712 	 * Read and process responses from server.
713 	 * The server may send multiple messages regarding
714 	 * file deletes if the remote target is a directory.
715 	 */
716 	for (;;) {
717 		n = remline(s = respbuff, sizeof(respbuff), TRUE);
718 		if (n == -1)	/* normal EOF */
719 			return(0);
720 		if (n == 0) {
721 			error("expected control record");
722 			continue;
723 		}
724 
725 		switch (*s++) {
726 		case C_END:	/* End of send operation */
727 			*ptarget = CNULL;
728 			ack();
729 			runspecial(target, opts, rname, destdir);
730 			addcmdspecialfile(target, rname, destdir);
731 			return(0);
732 
733 		case C_LOGMSG:
734 			if (n > 0)
735 				message(MT_INFO, "%s", s);
736 			break;
737 
738 		case C_NOTEMSG:
739 			if (n > 0)
740 				message(MT_NOTICE, "%s", s);
741 			break;
742 			/* Goto top of loop */
743 
744 		case C_ERRMSG:
745 			message(MT_NERROR, "%s", s);
746 			return(-1);
747 
748 		case C_FERRMSG:
749 			message(MT_FERROR, "%s", s);
750 			finish();
751 
752 		default:
753 			error("install link: unexpected response '%s'",
754 			      respbuff);
755 			err();
756 		}
757 	}
758 	/*NOTREACHED*/
759 }
760 
761 /*
762  * Check to see if file needs to be updated on the remote machine.
763  * Returns:
764  * 	US_NOTHING	- no update
765  *	US_NOENT	- remote doesn't exist
766  *	US_OUTDATE	- out of date
767  *	US_DOCOMP	- comparing binaries to determine if out of date
768  *	US_CHMOG	- File modes or ownership do not match
769  */
770 static int
update(char * rname,opt_t opts,struct stat * statp)771 update(char *rname, opt_t opts, struct stat *statp)
772 {
773 	off_t size;
774 	time_t mtime;
775 	unsigned short lmode;
776 	unsigned short rmode;
777 	char *owner = NULL, *group = NULL;
778 	int done, n;
779 	u_char *cp;
780 	char ername[PATH_MAX*4];
781 
782 	debugmsg(DM_CALL, "update(%s, %#x, %p)\n", rname, opts, statp);
783 
784 	switch (statp->st_mode & S_IFMT) {
785 	case S_IFBLK:
786 		debugmsg(DM_MISC, "%s is a block special; skipping\n", target);
787 		return(US_NOTHING);
788 	case S_IFCHR:
789 		debugmsg(DM_MISC, "%s is a character special; skipping\n",
790 		    target);
791 		return(US_NOTHING);
792 	case S_IFIFO:
793 		debugmsg(DM_MISC, "%s is a fifo; skipping\n", target);
794 		return(US_NOTHING);
795 	case S_IFSOCK:
796 		debugmsg(DM_MISC, "%s is a socket; skipping\n", target);
797 		return(US_NOTHING);
798 	}
799 
800 	if (IS_ON(opts, DO_NOEXEC))
801 		if (isexec(target, statp)) {
802 			debugmsg(DM_MISC, "%s is an executable\n", target);
803 			return(US_NOTHING);
804 		}
805 
806 	/*
807 	 * Check to see if the file exists on the remote machine.
808 	 */
809 	ENCODE(ername, rname);
810 	(void) sendcmd(C_QUERY, "%s", ername);
811 
812 	for (done = 0; !done;) {
813 		n = remline(cp = respbuff, sizeof(respbuff), TRUE);
814 		if (n <= 0) {
815 			error("update: unexpected control record in response to query");
816 			return(US_NOTHING);
817 		}
818 
819 		switch (*cp++) {
820 		case QC_ONNFS:  /* Resides on a NFS */
821 			debugmsg(DM_MISC,
822 				 "update: %s is on a NFS.  Skipping...\n",
823 				 rname);
824 			return(US_NOTHING);
825 
826 		case QC_SYM:  /* Is a symbolic link */
827 			debugmsg(DM_MISC,
828 				 "update: %s is a symlink.  Skipping...\n",
829 				 rname);
830 			return(US_NOTHING);
831 
832 		case QC_ONRO:  /* Resides on a Read-Only fs */
833 			debugmsg(DM_MISC,
834 				 "update: %s is on a RO fs.  Skipping...\n",
835 				 rname);
836 			return(US_NOTHING);
837 
838 		case QC_YES:
839 			done = 1;
840 			break;
841 
842 		case QC_NO:  /* file doesn't exist so install it */
843 			return(US_NOENT);
844 
845 		case C_ERRMSG:
846 			if (cp)
847 				message(MT_NERROR, "%s", cp);
848 			return(US_NOTHING);
849 
850 		case C_FERRMSG:
851 			if (cp)
852 				message(MT_FERROR, "%s", cp);
853 			finish();
854 
855 		case C_NOTEMSG:
856 			if (cp)
857 				message(MT_NOTICE, "%s", cp);
858 			break;
859 			/* Goto top of loop */
860 
861 		default:
862 			error("update: unexpected response to query '%s'", respbuff);
863 			return(US_NOTHING);
864 		}
865 	}
866 
867 	/*
868 	 * Target exists, but no other info passed
869 	 */
870 	if (n <= 1 || !S_ISREG(statp->st_mode))
871 		return(US_OUTDATE);
872 
873 	if (IS_ON(opts, DO_COMPARE))
874 		return(US_DOCOMP);
875 
876 	/*
877 	 * Parse size
878 	 */
879 	size = (off_t) strtoll(cp, (char **)&cp, 10);
880 	if (*cp++ != ' ') {
881 		error("update: size not delimited");
882 		return(US_NOTHING);
883 	}
884 
885 	/*
886 	 * Parse mtime
887 	 */
888 	mtime = strtol(cp, (char **)&cp, 10);
889 	if (*cp++ != ' ') {
890 		error("update: mtime not delimited");
891 		return(US_NOTHING);
892 	}
893 
894 	/*
895 	 * Parse remote file mode
896 	 */
897 	rmode = strtol(cp, (char **)&cp, 8);
898 	if (cp && *cp)
899 		++cp;
900 
901 	/*
902 	 * Be backwards compatible
903 	 */
904 	if (cp && *cp != CNULL) {
905 		/*
906 		 * Parse remote file owner
907 		 */
908 		owner = strtok((char *)cp, " ");
909 		if (owner == NULL) {
910 			error("update: owner not delimited");
911 			return(US_NOTHING);
912 		}
913 
914 		/*
915 		 * Parse remote file group
916 		 */
917 		group = strtok(NULL, " ");
918 		if (group == NULL) {
919 			error("update: group not delimited");
920 			return(US_NOTHING);
921 		}
922 	}
923 
924 	/*
925 	 * File needs to be updated?
926 	 */
927 	lmode = statp->st_mode & 07777;
928 
929 	debugmsg(DM_MISC, "update(%s,) local mode %#04o remote mode %#04o\n",
930 		 rname, lmode, rmode);
931 	debugmsg(DM_MISC, "update(%s,) size %lld mtime %lld owner '%s' grp '%s'"
932 		 "\n", rname, (long long) size, (long long)mtime, owner, group);
933 
934 	if (statp->st_mtime != mtime) {
935 		if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
936 			message(MT_WARNING,
937 				"%s: Warning: remote copy is newer",
938 				target);
939 			return(US_NOTHING);
940 		}
941 		return(US_OUTDATE);
942 	}
943 
944 	if (statp->st_size != size) {
945 		debugmsg(DM_MISC, "size does not match (%lld != %lld).\n",
946 			 (long long) statp->st_size, (long long) size);
947 		return(US_OUTDATE);
948 	}
949 
950 	if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
951 		debugmsg(DM_MISC, "modes do not match (%#04o != %#04o).\n",
952 			 lmode, rmode);
953 		return(US_CHMOG);
954 	}
955 
956 
957 	/*
958 	 * Check ownership
959 	 */
960 	if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
961 		if (!IS_ON(opts, DO_NUMCHKOWNER)) {
962 			/* Check by string compare */
963 			if (strcmp(owner, getusername(statp->st_uid,
964 						      target, opts)) != 0) {
965 				debugmsg(DM_MISC,
966 					 "owner does not match (%s != %s).\n",
967 					 getusername(statp->st_uid,
968 						     target, opts), owner);
969 				return(US_CHMOG);
970 			}
971 		} else {
972 			/*
973 			 * Check numerically.
974 			 * Allow negative numbers.
975 			 */
976 			while (*owner && !isdigit((unsigned char)*owner) &&
977 			    (*owner != '-'))
978 				++owner;
979 			if (owner && (uid_t)atoi(owner) != statp->st_uid) {
980 				debugmsg(DM_MISC,
981 					 "owner does not match (%d != %s).\n",
982 					 statp->st_uid, owner);
983 				return(US_CHMOG);
984 			}
985 		}
986 	}
987 
988 	if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
989 		if (!IS_ON(opts, DO_NUMCHKGROUP)) {
990 			/* Check by string compare */
991 			if (strcmp(group, getgroupname(statp->st_gid,
992 						       target, opts)) != 0) {
993 				debugmsg(DM_MISC,
994 					 "group does not match (%s != %s).\n",
995 					 getgroupname(statp->st_gid,
996 						      target, opts), group);
997 				return(US_CHMOG);
998 			}
999 		} else {
1000 			/* Check numerically */
1001 			/* Allow negative gid */
1002 			while (*group && !isdigit((unsigned char) *group) &&
1003 			    (*group != '-'))
1004 				++group;
1005 			if (group && (gid_t)atoi(group) != statp->st_gid) {
1006 				debugmsg(DM_MISC,
1007 					 "group does not match (%d != %s).\n",
1008 					 statp->st_gid, group);
1009 				return(US_CHMOG);
1010 			}
1011 		}
1012 	}
1013 
1014 	return(US_NOTHING);
1015 }
1016 
1017 /*
1018  * Stat a file
1019  */
1020 static int
dostat(char * file,struct stat * statbuf,opt_t opts)1021 dostat(char *file, struct stat *statbuf, opt_t opts)
1022 {
1023 	int s;
1024 
1025 	if (IS_ON(opts, DO_FOLLOW))
1026 		s = stat(file, statbuf);
1027 	else
1028 		s = lstat(file, statbuf);
1029 
1030 	if (s == -1)
1031 		error("%s: %s failed: %s", file,
1032 		      IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
1033 	return(s);
1034 }
1035 
1036 /*
1037  * We need to just change file info.
1038  */
1039 static int
statupdate(int u,char * starget,opt_t opts,char * rname,int destdir,struct stat * st,char * user,char * group)1040 statupdate(int u, char *starget, opt_t opts, char *rname, int destdir,
1041 	   struct stat *st, char *user, char *group)
1042 {
1043 	int rv = 0;
1044 	char ername[PATH_MAX*4];
1045 	int lmode = st->st_mode & 07777;
1046 
1047 	if (u == US_CHMOG) {
1048 		if (IS_ON(opts, DO_VERIFY)) {
1049 			message(MT_INFO,
1050 				"%s: need to change to perm %#04o, owner %s, group %s",
1051 				starget, lmode, user, group);
1052 			runspecial(starget, opts, rname, destdir);
1053 		}
1054 		else {
1055 			message(MT_CHANGE, "%s: change to perm %#04o, owner %s, group %s",
1056 				starget, lmode, user, group);
1057 			ENCODE(ername, rname);
1058 			(void) sendcmd(C_CHMOG, "%o %04o %s %s %s",
1059 				       opts, lmode, user, group, ername);
1060 			(void) response();
1061 		}
1062 		rv = 1;
1063 	}
1064 	return(rv);
1065 }
1066 
1067 
1068 /*
1069  * We need to install/update:
1070  */
1071 static int
fullupdate(int u,char * starget,opt_t opts,char * rname,int destdir,struct stat * st,char * user,char * group)1072 fullupdate(int u, char *starget, opt_t opts, char *rname, int destdir,
1073 	   struct stat *st, char *user, char *group)
1074 {
1075 	/*
1076 	 * No entry - need to install
1077 	 */
1078 	if (u == US_NOENT) {
1079 		if (IS_ON(opts, DO_VERIFY)) {
1080 			message(MT_INFO, "%s: need to install", starget);
1081 			runspecial(starget, opts, rname, destdir);
1082 			return(1);
1083 		}
1084 		if (!IS_ON(opts, DO_QUIET))
1085 			message(MT_CHANGE, "%s: installing", starget);
1086 		FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
1087 	}
1088 
1089 	/*
1090 	 * Handle special file types, including directories and symlinks
1091 	 */
1092 	if (S_ISDIR(st->st_mode)) {
1093 		if (senddir(rname, opts, st, user, group, destdir) > 0)
1094 			return(1);
1095 		return(0);
1096 	} else if (S_ISLNK(st->st_mode)) {
1097 		if (u == US_NOENT)
1098 			FLAG_ON(opts, DO_COMPARE);
1099 		/*
1100 		 * Since we always send link info to the server
1101 		 * so the server can determine if the remote link
1102 		 * is correct, we never get any acknowledgement
1103 		 * from the server whether the link was really
1104 		 * updated or not.
1105 		 */
1106 		(void) sendlink(rname, opts, st, user, group, destdir);
1107 		return(0);
1108 	} else if (S_ISREG(st->st_mode)) {
1109 		if (u == US_OUTDATE) {
1110 			if (IS_ON(opts, DO_VERIFY)) {
1111 				message(MT_INFO, "%s: need to update", starget);
1112 				runspecial(starget, opts, rname, destdir);
1113 				return(1);
1114 			}
1115 			if (!IS_ON(opts, DO_QUIET))
1116 				message(MT_CHANGE, "%s: updating", starget);
1117 		}
1118 		return (sendfile(rname, opts, st, user, group, destdir) == 0);
1119 	} else {
1120 		message(MT_INFO, "%s: unknown file type %#o", starget,
1121 			st->st_mode);
1122 		return(0);
1123 	}
1124 }
1125 
1126 /*
1127  * Transfer the file or directory in target[].
1128  * rname is the name of the file on the remote host.
1129  *
1130  * Return < 0 on error.
1131  * Return 0 if nothing happened.
1132  * Return > 0 if anything is updated.
1133  */
1134 static int
sendit(char * rname,opt_t opts,int destdir)1135 sendit(char *rname, opt_t opts, int destdir)
1136 {
1137 	static struct stat stb;
1138 	char *user, *group;
1139 	int u, len;
1140 
1141 	/*
1142 	 * Remove possible accidental newline
1143 	 */
1144 	len = strlen(rname);
1145 	if (len > 0 && rname[len-1] == '\n')
1146 		rname[len-1] = CNULL;
1147 
1148 	if (checkfilename(rname) != 0)
1149 		return(-1);
1150 
1151 	debugmsg(DM_CALL, "sendit(%s, %#x) called\n", rname, opts);
1152 
1153 	if (except(target))
1154 		return(0);
1155 
1156 	if (dostat(target, &stb, opts) < 0)
1157 		return(-1);
1158 
1159 	/*
1160 	 * Does rname need updating?
1161 	 */
1162 	u = update(rname, opts, &stb);
1163 	debugmsg(DM_MISC, "sendit(%s, %#x): update status of %s is %d\n",
1164 		 rname, opts, target, u);
1165 
1166 	/*
1167 	 * Don't need to update the file, but we may need to save hardlink
1168 	 * info.
1169 	 */
1170 	if (u == US_NOTHING) {
1171 		if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
1172 			(void) linkinfo(&stb);
1173 		return(0);
1174 	}
1175 
1176 	user = getusername(stb.st_uid, target, opts);
1177 	group = getgroupname(stb.st_gid, target, opts);
1178 
1179 	if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM))
1180 		u = US_OUTDATE;
1181 
1182 	if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP)
1183 		return(fullupdate(u, target, opts, rname, destdir, &stb,
1184 				  user, group));
1185 
1186 	if (u == US_CHMOG)
1187 		return(statupdate(u, target, opts, rname, destdir, &stb,
1188 				  user, group));
1189 
1190 	return(0);
1191 }
1192 
1193 /*
1194  * Remove temporary files and do any cleanup operations before exiting.
1195  */
1196 void
cleanup(int dummy)1197 cleanup(int dummy)
1198 {
1199 	char *file;
1200 
1201 	if ((file = getnotifyfile()) != NULL)
1202 		(void) unlink(file);
1203 }
1204 
1205 /*
1206  * Update the file(s) if they are different.
1207  * destdir = 1 if destination should be a directory
1208  * (i.e., more than one source is being copied to the same destination).
1209  *
1210  * Return < 0 on error.
1211  * Return 0 if nothing updated.
1212  * Return > 0 if something was updated.
1213  */
1214 int
install(char * src,char * dest,int ddir,int destdir,opt_t opts)1215 install(char *src, char *dest, int ddir, int destdir, opt_t opts)
1216 {
1217 	static char destcopy[PATH_MAX];
1218 	char *rname;
1219 	int didupdate = 0;
1220 	char ername[PATH_MAX*4];
1221 
1222 	debugmsg(DM_CALL,
1223 		"install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%#x) start\n",
1224 		(src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
1225 	/*
1226 	 * Save source name
1227 	 */
1228 	if (IS_ON(opts, DO_WHOLE))
1229 		source[0] = CNULL;
1230 	else
1231 		(void) strlcpy(source, src, sizeof(source));
1232 
1233 	if (dest == NULL) {
1234 		FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
1235 		dest = src;
1236 	}
1237 
1238 	if (checkfilename(dest) != 0)
1239 		return(-1);
1240 
1241 	if (nflag || debug) {
1242 		static char buff[BUFSIZ];
1243 		char *cp;
1244 
1245 		cp = getondistoptlist(opts);
1246 		(void) snprintf(buff, sizeof(buff), "%s%s%s %s %s",
1247 			       IS_ON(opts, DO_VERIFY) ? "verify" : "install",
1248 			       (cp) ? " -o" : "", (cp) ? cp : "",
1249 			       src, dest);
1250 		if (nflag) {
1251 			printf("%s\n", buff);
1252 			return(0);
1253 		} else
1254 			debugmsg(DM_MISC, "%s\n", buff);
1255 	}
1256 
1257 	rname = exptilde(target, src, sizeof(target));
1258 	if (rname == NULL)
1259 		return(-1);
1260 	ptarget = target;
1261 	while (*ptarget)
1262 		ptarget++;
1263 	/*
1264 	 * If we are renaming a directory and we want to preserve
1265 	 * the directory hierarchy (-w), we must strip off the leading
1266 	 * directory name and preserve the rest.
1267 	 */
1268 	if (IS_ON(opts, DO_WHOLE)) {
1269 		while (*rname == '/')
1270 			rname++;
1271 		ddir = 1;
1272 		destdir = 1;
1273 	} else {
1274 		rname = strrchr(target, '/');
1275 		/* Check if no '/' or target ends in '/' */
1276 		if (rname == NULL ||
1277 		    rname+1 == NULL ||
1278 		    *(rname+1) == CNULL)
1279 			rname = target;
1280 		else
1281 			rname++;
1282 	}
1283 
1284 	debugmsg(DM_MISC,
1285  	"install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
1286  		 target, source, rname, dest, destdir, ddir);
1287 
1288 	/*
1289 	 * Pass the destination file/directory name to remote.
1290 	 */
1291 	ENCODE(ername, dest);
1292  	if (ddir)
1293 		(void) sendcmd(C_DIRTARGET, "%o %s", opts, ername);
1294 	else
1295 		(void) sendcmd(C_TARGET, "%o %s", opts, ername);
1296 	if (response() < 0)
1297 		return(-1);
1298 
1299 	/*
1300 	 * Save the name of the remote target destination if we are
1301 	 * in WHOLE mode (destdir > 0) or if the source and destination
1302 	 * are not the same.  This info will be used later for maintaining
1303 	 * hardlink info.
1304 	 */
1305 	if (destdir || (src && dest && strcmp(src, dest))) {
1306 		(void) strlcpy(destcopy, dest, sizeof(destcopy));
1307 		Tdest = destcopy;
1308 	}
1309 
1310 	didupdate = sendit(rname, opts, destdir);
1311 	Tdest = 0;
1312 
1313 	return(didupdate);
1314 }
1315