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