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
58struct	linkbuf *ihead = NULL;	/* list of files with more than one link */
59char	buf[BUFSIZ];		/* general purpose buffer */
60u_char	respbuff[BUFSIZ];	/* Response buffer */
61char	target[BUFSIZ];		/* target/source directory name */
62char	source[BUFSIZ];		/* source directory name */
63char	*ptarget;		/* pointer to end of target name */
64char	*Tdest;			/* pointer to last T dest*/
65struct namelist	*updfilelist = NULL; /* List of updated files */
66
67static void runspecial(char *, opt_t, char *, int);
68static void addcmdspecialfile(char *, char *, int);
69static void freecmdspecialfiles(void);
70static struct linkbuf *linkinfo(struct stat *);
71static int sendhardlink(opt_t, struct linkbuf *, char *, int);
72static int sendfile(char *, opt_t, struct stat *, char *, char *, int);
73static int rmchk(opt_t);
74static int senddir(char *, opt_t, struct stat *, char *, char *, int);
75static int sendlink(char *, opt_t, struct stat *, char *, char *, int);
76static int update(char *, opt_t, struct stat *);
77static int dostat(char *, struct stat *, opt_t);
78static int statupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *);
79static int fullupdate(int, char *, opt_t, char *, int, struct stat *, char *, char *);
80static int sendit(char *, opt_t, int);
81
82/*
83 * return remote file pathname (relative from target)
84 */
85char *
86remfilename(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 */
148int
149inlist(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 */
162static void
163runspecial(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 */
194static void
195addcmdspecialfile(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 */
224static void
225freecmdspecialfiles(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 */
244void
245runcmdspecial(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 */
287int
288checkfilename(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
305void
306freelinkinfo(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 */
317static struct linkbuf *
318linkinfo(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 */
348static int
349sendhardlink(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 */
379static int
380sendfile(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 */
488static int
489rmchk(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 */
578static int
579senddir(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
640out:
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 */
653static int
654sendlink(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 */
770static int
771update(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 */
1020static int
1021dostat(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 */
1039static int
1040statupdate(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 */
1071static int
1072fullupdate(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 */
1134static int
1135sendit(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 */
1196void
1197cleanup(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 */
1214int
1215install(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