GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rdist/client.c Lines: 0 514 0.0 %
Date: 2017-11-07 Branches: 0 389 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: client.c,v 1.36 2017/07/09 14:04:50 espie 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 *
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
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
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
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
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
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
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
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 *
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
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
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)) < 0) {
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
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) < 0) {
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
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
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
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
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 < 0)
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
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
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
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
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
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
}