Bug Summary

File:src/usr.bin/rdist/client.c
Warning:line 273, column 4
Value stored to 'first' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name client.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rdist/obj -resource-dir /usr/local/lib/clang/13.0.0 -I . -I /usr/src/usr.bin/rdist -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rdist/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/rdist/client.c
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(*__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_NOTHING0 0 /* No update needed */
53#define US_NOENT1 1 /* Entry does not exist */
54#define US_OUTDATE2 2 /* Entry is out of date */
55#define US_DOCOMP3 3 /* Do a binary comparison */
56#define US_CHMOG4 4 /* Modes or ownership of file differ */
57
58struct linkbuf *ihead = NULL((void *)0); /* list of files with more than one link */
59char buf[BUFSIZ1024]; /* general purpose buffer */
60u_char respbuff[BUFSIZ1024]; /* Response buffer */
61char target[BUFSIZ1024]; /* target/source directory name */
62char source[BUFSIZ1024]; /* source directory name */
63char *ptarget; /* pointer to end of target name */
64char *Tdest; /* pointer to last T dest*/
65struct namelist *updfilelist = NULL((void *)0); /* 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[BUFSIZ1024];
90 int srclen, pathlen;
91 char *p;
92
93
94 debugmsg(DM_MISC0x10,
95 "remfilename: src=%s dest=%s path=%s rname=%s destdir=%d\n",
96 A(src)((src) ? src : "<null>"), A(dest)((dest) ? dest : "<null>"), A(path)((path) ? path : "<null>"), A(rname)((rname) ? rname : "<null>"), destdir);
97
98 if (!dest) {
99 debugmsg(DM_MISC0x10, "remfilename: remote filename=%s\n", path);
100 return(path);
101 }
102
103 if (!destdir) {
104 debugmsg(DM_MISC0x10, "remfilename: remote filename=%s\n", dest);
105 return(dest);
106 }
107
108 buff[0] = CNULL'\0';
109 lname = buff;
110 if (path && *path) {
111 cp = strrchr(path, '/');
112 if (cp == NULL((void *)0))
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((void *)0))
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_MISC0x10, "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((void *)0); 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((void *)0); sc = sc->sc_next) {
171 if (sc->sc_type != SPECIAL10)
172 continue;
173 if (sc->sc_args != NULL((void *)0) && !inlist(sc->sc_args, starget))
174 continue;
175 message(MT_CHANGE0x0020, "special \"%s\"", sc->sc_name);
176 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001))
177 continue;
178 (void) sendcmd(C_SPECIAL'S',
179 "%s=%s;%s=%s;%s=%s;export %s %s %s;%s",
180 E_LOCFILE"FILE", starget,
181 E_REMFILE"REMFILE", rfile,
182 E_BASEFILE"BASEFILE", xbasename(rfile),
183 E_LOCFILE"FILE", E_REMFILE"REMFILE", E_BASEFILE"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((void *)0) && !isokay; sc = sc->sc_next) {
205 if (sc->sc_type != CMDSPECIAL11)
206 continue;
207 if (sc->sc_args != NULL((void *)0) && !inlist(sc->sc_args, starget))
208 continue;
209 isokay = TRUE1;
210 }
211
212 if (isokay) {
213 new = xmalloc(sizeof *new);
214 new->n_name = xstrdup(rfile);
215 new->n_regex = NULL((void *)0);
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((void *)0);
237 }
238 updfilelist = NULL((void *)0);
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 = TRUE1;
250
251 for (sc = cmd->c_cmds; sc != NULL((void *)0); sc = sc->sc_next) {
252 if (sc->sc_type != CMDSPECIAL11)
253 continue;
254 message(MT_CHANGE0x0020, "cmdspecial \"%s\"", sc->sc_name);
255 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001))
256 continue;
257 /* Send all the file names */
258 for (f = updfilelist; f != NULL((void *)0); f = f->n_next) {
259 if (first) {
260 (void) sendcmd(C_CMDSPECIAL's', NULL((void *)0));
261 if (response() < 0)
262 return;
263 first = FALSE0;
264 }
265 (void) sendcmd(RC_FILE'F', "%s", f->n_name);
266 if (response() < 0)
267 return;
268 }
269 if (first) {
270 (void) sendcmd(C_CMDSPECIAL's', NULL((void *)0));
271 if (response() < 0)
272 return;
273 first = FALSE0;
Value stored to 'first' is never read
274 }
275 /* Send command to run and wait for it to complete */
276 (void) sendcmd(RC_COMMAND'C', "%s", sc->sc_name);
277 while (response() > 0)
278 ;
279 first = TRUE1; /* 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_NERROR0x0002,
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((void *)0); 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((void *)0);
341
342 return(NULL((void *)0));
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_MAX1024];
352 char *lname; /* name of file to link to */
353 char ername[PATH_MAX1024*4], elname[PATH_MAX1024*4];
354
355 debugmsg(DM_MISC0x10,
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((void *)0))
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_MISC0x10, "sendhardlink: lname=%s\n", lname);
368 }
369 ENCODE(elname, lname)strvis(elname, lname, (0x04 | 0x08 | 0x10));
370 ENCODE(ername, rname)strvis(ername, rname, (0x04 | 0x08 | 0x10));
371 (void) sendcmd(C_RECVHARDLINK'k', "%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_MAX1024*4];
386
387 if (stb->st_nlink > 1) {
388 struct linkbuf *lp;
389
390 if ((lp = linkinfo(stb)) != NULL((void *)0))
391 return(sendhardlink(opts, lp, rname, destdir));
392 }
393
394 if ((f = open(target, O_RDONLY0x0000)) == -1) {
395 error("%s: open for read failed: %s", target, SYSERRstrerror((*__errno())));
396 return(-1);
397 }
398
399 /*
400 * Send file info
401 */
402 ENCODE(ername, rname)strvis(ername, rname, (0x04 | 0x08 | 0x10));
403
404 (void) sendcmd(C_RECVREG'R', "%o %04o %lld %lld %lld %s %s %s",
405 opts, stb->st_mode & 07777, (long long) stb->st_size,
406 (long long)stb->st_mtimest_mtim.tv_sec, (long long)stb->st_atimest_atim.tv_sec,
407 user, group, ername);
408 if (response() < 0) {
409 (void) close(f);
410 return(-1);
411 }
412
413
414 debugmsg(DM_MISC0x10, "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(SIGALRM14, sighandler);
421
422 /*
423 * Actually transfer the file
424 */
425 goterr = 0;
426 for (i = 0; i < stb->st_size; i += BUFSIZ1024) {
427 off_t amt = BUFSIZ1024;
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()(void) sendcmd('\1', ((void *)0));
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, SYSERRstrerror((*__errno())));
447 err()(void) sendcmd('\1', ((void *)0));
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_MISC0x10, "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()(void) sendcmd('\5', ((void *)0));
467 f = response();
468 if (f < 0)
469 return(-1);
470 else if (f == 0 && IS_ON(opts, DO_COMPARE)(opts & 0x0000008))
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_MAX1024*4];
496
497 debugmsg(DM_CALL0x01, "rmchk()\n");
498
499 /*
500 * Tell the remote to clean the files from the last directory sent.
501 */
502 (void) sendcmd(C_CLEAN'C', "%o", IS_ON(opts, DO_VERIFY)(opts & 0x0000001));
503 if (response() < 0)
504 return(-1);
505
506 for ( ; ; ) {
507 n = remline(s = respbuff, sizeof(respbuff), TRUE1);
508 if (n <= 0) {
509 error("rmchk: unexpected control record");
510 return(didupdate);
511 }
512
513 switch (*s++) {
514 case CC_QUERY'Q': /* 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)strunvis(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_MISC0x10, "check %s\n", target);
530 if (except(target))
531 (void) sendcmd(CC_NO'N', NULL((void *)0));
532 else if (lstat(target, &stb) == -1) {
533 if (sendcmd(CC_YES'Y', NULL((void *)0)) == 0)
534 didupdate = 1;
535 } else
536 (void) sendcmd(CC_NO'N', NULL((void *)0));
537 break;
538
539 case CC_END'E':
540 *ptarget = CNULL'\0';
541 ack()(void) sendcmd('\5', ((void *)0));
542 return(didupdate);
543
544 case C_LOGMSG'\4':
545 if (n > 0)
546 message(MT_INFO0x0040, "%s", s);
547 break;
548
549 case C_NOTEMSG'\3':
550 if (n > 0)
551 message(MT_NOTICE0x0100, "%s", s);
552 break;
553 /* Goto top of loop */
554
555 case C_ERRMSG'\1':
556 message(MT_NERROR0x0002, "%s", s);
557 return(didupdate);
558
559 case C_FERRMSG'\2':
560 message(MT_FERROR0x0004, "%s", s);
561 finish();
562
563 default:
564 error("rmchk: unexpected response '%s'", respbuff);
565 err()(void) sendcmd('\1', ((void *)0));
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_MAX1024*4];
588
589 /*
590 * Send recvdir command in recvit() format.
591 */
592 ENCODE(ername, rname)strvis(ername, rname, (0x04 | 0x08 | 0x10));
593 (void) sendcmd(C_RECVDIR'D', "%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)(opts & 0x0002000)) {
604 didupdate = 0;
605 goto out;
606 }
607
608 if (IS_ON(opts, DO_REMOVE)(opts & 0x0000010))
609 if (rmchk(opts) > 0)
610 ++didupdate;
611
612 if ((d = opendir(target)) == NULL((void *)0)) {
613 error("%s: opendir failed: %s", target, SYSERRstrerror((*__errno())));
614 didupdate = -1;
615 goto out;
616 }
617
618 len = ptarget - target;
619 while ((dp = readdir(d)) != NULL((void *)0)) {
620 if (!strcmp(dp->d_name, ".") ||
621 !strcmp(dp->d_name, ".."))
622 continue;
623 if (len + 1 + (int) strlen(dp->d_name) >= PATH_MAX1024 - 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'E', NULL((void *)0));
642 (void) response();
643
644 ptarget = optarget;
645 *ptarget = CNULL'\0';
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[BUFSIZ1024];
659 char lbuf[PATH_MAX1024];
660 u_char *s;
661 char ername[PATH_MAX1024*4];
662
663 debugmsg(DM_CALL0x01, "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((void *)0))
669 return(sendhardlink(opts, lp, rname, destdir));
670 }
671
672 /*
673 * Gather and send basic link info
674 */
675 ENCODE(ername, rname)strvis(ername, rname, (0x04 | 0x08 | 0x10));
676 (void) sendcmd(C_RECVSYMLINK'K', "%o %04o %lld %lld %lld %s %s %s",
677 opts, stb->st_mode & 07777, (long long) stb->st_size,
678 (long long)stb->st_mtimest_mtim.tv_sec, (long long)stb->st_atimest_atim.tv_sec,
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()(void) sendcmd('\1', ((void *)0));
691 }
692 (void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf);
693 ENCODE(ername, tbuf)strvis(ername, tbuf, (0x04 | 0x08 | 0x10));
694 (void) sendcmd(C_NONE'=', "%s\n", ername);
695
696 if (n != stb->st_size) {
697 error("%s: file changed size", target);
698 err()(void) sendcmd('\1', ((void *)0));
699 } else
700 ack()(void) sendcmd('\5', ((void *)0));
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)(opts & 0x0000008))
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), TRUE1);
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'E': /* End of send operation */
727 *ptarget = CNULL'\0';
728 ack()(void) sendcmd('\5', ((void *)0));
729 runspecial(target, opts, rname, destdir);
730 addcmdspecialfile(target, rname, destdir);
731 return(0);
732
733 case C_LOGMSG'\4':
734 if (n > 0)
735 message(MT_INFO0x0040, "%s", s);
736 break;
737
738 case C_NOTEMSG'\3':
739 if (n > 0)
740 message(MT_NOTICE0x0100, "%s", s);
741 break;
742 /* Goto top of loop */
743
744 case C_ERRMSG'\1':
745 message(MT_NERROR0x0002, "%s", s);
746 return(-1);
747
748 case C_FERRMSG'\2':
749 message(MT_FERROR0x0004, "%s", s);
750 finish();
751
752 default:
753 error("install link: unexpected response '%s'",
754 respbuff);
755 err()(void) sendcmd('\1', ((void *)0));
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((void *)0), *group = NULL((void *)0);
778 int done, n;
779 u_char *cp;
780 char ername[PATH_MAX1024*4];
781
782 debugmsg(DM_CALL0x01, "update(%s, %#x, %p)\n", rname, opts, statp);
783
784 switch (statp->st_mode & S_IFMT0170000) {
785 case S_IFBLK0060000:
786 debugmsg(DM_MISC0x10, "%s is a block special; skipping\n", target);
787 return(US_NOTHING0);
788 case S_IFCHR0020000:
789 debugmsg(DM_MISC0x10, "%s is a character special; skipping\n",
790 target);
791 return(US_NOTHING0);
792 case S_IFIFO0010000:
793 debugmsg(DM_MISC0x10, "%s is a fifo; skipping\n", target);
794 return(US_NOTHING0);
795 case S_IFSOCK0140000:
796 debugmsg(DM_MISC0x10, "%s is a socket; skipping\n", target);
797 return(US_NOTHING0);
798 }
799
800 if (IS_ON(opts, DO_NOEXEC)(opts & 0x0000800))
801 if (isexec(target, statp)) {
802 debugmsg(DM_MISC0x10, "%s is an executable\n", target);
803 return(US_NOTHING0);
804 }
805
806 /*
807 * Check to see if the file exists on the remote machine.
808 */
809 ENCODE(ername, rname)strvis(ername, rname, (0x04 | 0x08 | 0x10));
810 (void) sendcmd(C_QUERY'Q', "%s", ername);
811
812 for (done = 0; !done;) {
813 n = remline(cp = respbuff, sizeof(respbuff), TRUE1);
814 if (n <= 0) {
815 error("update: unexpected control record in response to query");
816 return(US_NOTHING0);
817 }
818
819 switch (*cp++) {
820 case QC_ONNFS'F': /* Resides on a NFS */
821 debugmsg(DM_MISC0x10,
822 "update: %s is on a NFS. Skipping...\n",
823 rname);
824 return(US_NOTHING0);
825
826 case QC_SYM'l': /* Is a symbolic link */
827 debugmsg(DM_MISC0x10,
828 "update: %s is a symlink. Skipping...\n",
829 rname);
830 return(US_NOTHING0);
831
832 case QC_ONRO'O': /* Resides on a Read-Only fs */
833 debugmsg(DM_MISC0x10,
834 "update: %s is on a RO fs. Skipping...\n",
835 rname);
836 return(US_NOTHING0);
837
838 case QC_YES'Y':
839 done = 1;
840 break;
841
842 case QC_NO'N': /* file doesn't exist so install it */
843 return(US_NOENT1);
844
845 case C_ERRMSG'\1':
846 if (cp)
847 message(MT_NERROR0x0002, "%s", cp);
848 return(US_NOTHING0);
849
850 case C_FERRMSG'\2':
851 if (cp)
852 message(MT_FERROR0x0004, "%s", cp);
853 finish();
854
855 case C_NOTEMSG'\3':
856 if (cp)
857 message(MT_NOTICE0x0100, "%s", cp);
858 break;
859 /* Goto top of loop */
860
861 default:
862 error("update: unexpected response to query '%s'", respbuff);
863 return(US_NOTHING0);
864 }
865 }
866
867 /*
868 * Target exists, but no other info passed
869 */
870 if (n <= 1 || !S_ISREG(statp->st_mode)((statp->st_mode & 0170000) == 0100000))
871 return(US_OUTDATE2);
872
873 if (IS_ON(opts, DO_COMPARE)(opts & 0x0000008))
874 return(US_DOCOMP3);
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_NOTHING0);
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_NOTHING0);
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'\0') {
905 /*
906 * Parse remote file owner
907 */
908 owner = strtok((char *)cp, " ");
909 if (owner == NULL((void *)0)) {
910 error("update: owner not delimited");
911 return(US_NOTHING0);
912 }
913
914 /*
915 * Parse remote file group
916 */
917 group = strtok(NULL((void *)0), " ");
918 if (group == NULL((void *)0)) {
919 error("update: group not delimited");
920 return(US_NOTHING0);
921 }
922 }
923
924 /*
925 * File needs to be updated?
926 */
927 lmode = statp->st_mode & 07777;
928
929 debugmsg(DM_MISC0x10, "update(%s,) local mode %#04o remote mode %#04o\n",
930 rname, lmode, rmode);
931 debugmsg(DM_MISC0x10, "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_mtimest_mtim.tv_sec != mtime) {
935 if (statp->st_mtimest_mtim.tv_sec < mtime && IS_ON(opts, DO_YOUNGER)(opts & 0x0000004)) {
936 message(MT_WARNING0x0010,
937 "%s: Warning: remote copy is newer",
938 target);
939 return(US_NOTHING0);
940 }
941 return(US_OUTDATE2);
942 }
943
944 if (statp->st_size != size) {
945 debugmsg(DM_MISC0x10, "size does not match (%lld != %lld).\n",
946 (long long) statp->st_size, (long long) size);
947 return(US_OUTDATE2);
948 }
949
950 if (!IS_ON(opts, DO_NOCHKMODE)(opts & 0x0008000) && lmode != rmode) {
951 debugmsg(DM_MISC0x10, "modes do not match (%#04o != %#04o).\n",
952 lmode, rmode);
953 return(US_CHMOG4);
954 }
955
956
957 /*
958 * Check ownership
959 */
960 if (!IS_ON(opts, DO_NOCHKOWNER)(opts & 0x0004000) && owner) {
961 if (!IS_ON(opts, DO_NUMCHKOWNER)(opts & 0x0080000)) {
962 /* Check by string compare */
963 if (strcmp(owner, getusername(statp->st_uid,
964 target, opts)) != 0) {
965 debugmsg(DM_MISC0x10,
966 "owner does not match (%s != %s).\n",
967 getusername(statp->st_uid,
968 target, opts), owner);
969 return(US_CHMOG4);
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_MISC0x10,
981 "owner does not match (%d != %s).\n",
982 statp->st_uid, owner);
983 return(US_CHMOG4);
984 }
985 }
986 }
987
988 if (!IS_ON(opts, DO_NOCHKGROUP)(opts & 0x0010000) && group) {
989 if (!IS_ON(opts, DO_NUMCHKGROUP)(opts & 0x0040000)) {
990 /* Check by string compare */
991 if (strcmp(group, getgroupname(statp->st_gid,
992 target, opts)) != 0) {
993 debugmsg(DM_MISC0x10,
994 "group does not match (%s != %s).\n",
995 getgroupname(statp->st_gid,
996 target, opts), group);
997 return(US_CHMOG4);
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_MISC0x10,
1007 "group does not match (%d != %s).\n",
1008 statp->st_gid, group);
1009 return(US_CHMOG4);
1010 }
1011 }
1012 }
1013
1014 return(US_NOTHING0);
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)(opts & 0x0000020))
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)(opts & 0x0000020) ? "stat" : "lstat", SYSERRstrerror((*__errno())));
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_MAX1024*4];
1045 int lmode = st->st_mode & 07777;
1046
1047 if (u == US_CHMOG4) {
1048 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001)) {
1049 message(MT_INFO0x0040,
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_CHANGE0x0020, "%s: change to perm %#04o, owner %s, group %s",
1056 starget, lmode, user, group);
1057 ENCODE(ername, rname)strvis(ername, rname, (0x04 | 0x08 | 0x10));
1058 (void) sendcmd(C_CHMOG'M', "%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_NOENT1) {
1079 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001)) {
1080 message(MT_INFO0x0040, "%s: need to install", starget);
1081 runspecial(starget, opts, rname, destdir);
1082 return(1);
1083 }
1084 if (!IS_ON(opts, DO_QUIET)(opts & 0x0000100))
1085 message(MT_CHANGE0x0020, "%s: installing", starget);
1086 FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE))opts &= ~((0x0000008|0x0000010));
1087 }
1088
1089 /*
1090 * Handle special file types, including directories and symlinks
1091 */
1092 if (S_ISDIR(st->st_mode)((st->st_mode & 0170000) == 0040000)) {
1093 if (senddir(rname, opts, st, user, group, destdir) > 0)
1094 return(1);
1095 return(0);
1096 } else if (S_ISLNK(st->st_mode)((st->st_mode & 0170000) == 0120000)) {
1097 if (u == US_NOENT1)
1098 FLAG_ON(opts, DO_COMPARE)opts |= 0x0000008;
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)((st->st_mode & 0170000) == 0100000)) {
1109 if (u == US_OUTDATE2) {
1110 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001)) {
1111 message(MT_INFO0x0040, "%s: need to update", starget);
1112 runspecial(starget, opts, rname, destdir);
1113 return(1);
1114 }
1115 if (!IS_ON(opts, DO_QUIET)(opts & 0x0000100))
1116 message(MT_CHANGE0x0020, "%s: updating", starget);
1117 }
1118 return (sendfile(rname, opts, st, user, group, destdir) == 0);
1119 } else {
1120 message(MT_INFO0x0040, "%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'\0';
1147
1148 if (checkfilename(rname) != 0)
1149 return(-1);
1150
1151 debugmsg(DM_CALL0x01, "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_MISC0x10, "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_NOTHING0) {
1171 if (S_ISREG(stb.st_mode)((stb.st_mode & 0170000) == 0100000) && 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_CHMOG4 && IS_OFF(opts, DO_UPDATEPERM)!((opts & 0x0200000)))
1180 u = US_OUTDATE2;
1181
1182 if (u == US_NOENT1 || u == US_OUTDATE2 || u == US_DOCOMP3)
1183 return(fullupdate(u, target, opts, rname, destdir, &stb,
1184 user, group));
1185
1186 if (u == US_CHMOG4)
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((void *)0))
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_MAX1024];
1218 char *rname;
1219 int didupdate = 0;
1220 char ername[PATH_MAX1024*4];
1221
1222 debugmsg(DM_CALL0x01,
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)(opts & 0x0000002))
1229 source[0] = CNULL'\0';
1230 else
1231 (void) strlcpy(source, src, sizeof(source));
1232
1233 if (dest == NULL((void *)0)) {
1234 FLAG_OFF(opts, DO_WHOLE)opts &= ~(0x0000002); /* 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[BUFSIZ1024];
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)(opts & 0x0000001) ? "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_MISC0x10, "%s\n", buff);
1255 }
1256
1257 rname = exptilde(target, src, sizeof(target));
1258 if (rname == NULL((void *)0))
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)(opts & 0x0000002)) {
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((void *)0) ||
1277 rname+1 == NULL((void *)0) ||
1278 *(rname+1) == CNULL'\0')
1279 rname = target;
1280 else
1281 rname++;
1282 }
1283
1284 debugmsg(DM_MISC0x10,
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)strvis(ername, dest, (0x04 | 0x08 | 0x10));
1292 if (ddir)
1293 (void) sendcmd(C_DIRTARGET'T', "%o %s", opts, ername);
1294 else
1295 (void) sendcmd(C_TARGET't', "%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}