Annotation of src/usr.bin/rdist/client.c, Revision 1.33
1.33 ! guenther 1: /* $OpenBSD: client.c,v 1.32 2015/01/16 06:40:11 deraadt Exp $ */
1.3 deraadt 2:
1.1 dm 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.
1.19 millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 dm 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:
1.33 ! guenther 32: #include <ctype.h>
1.28 guenther 33: #include <dirent.h>
1.33 ! guenther 34: #include <errno.h>
! 35: #include <fcntl.h>
! 36: #include <limits.h>
! 37: #include <stdlib.h>
! 38: #include <string.h>
! 39: #include <unistd.h>
1.28 guenther 40:
1.33 ! guenther 41: #include "client.h"
1.18 millert 42: #include "y.tab.h"
1.1 dm 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 */
1.18 millert 56: #define US_CHMOG 4 /* Modes or ownership of file differ */
1.1 dm 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:
1.18 millert 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);
1.1 dm 81:
82: /*
83: * return remote file pathname (relative from target)
84: */
1.18 millert 85: char *
86: remfilename(char *src, char *dest, char *path, char *rname, int destdir)
1.1 dm 87: {
1.13 mpech 88: char *lname, *cp;
1.1 dm 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)
1.18 millert 113: (void) snprintf(buff, sizeof(buff), "%s/%s", dest, path);
1.1 dm 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)
1.18 millert 131: (void) snprintf(buff, sizeof(buff), "%s/%s",
132: dest, cp);
1.1 dm 133: else
1.18 millert 134: (void) snprintf(buff, sizeof(buff), "%s%s",
135: dest, cp);
1.1 dm 136: }
137: } else
1.18 millert 138: (void) strlcpy(lname, dest, buf + sizeof buff - lname);
1.1 dm 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: */
1.18 millert 148: int
149: inlist(struct namelist *list, char *file)
1.1 dm 150: {
1.13 mpech 151: struct namelist *nl;
1.1 dm 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: */
1.18 millert 162: static void
163: runspecial(char *starget, opt_t opts, char *rname, int destdir)
1.1 dm 164: {
1.13 mpech 165: struct subcmd *sc;
1.1 dm 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: */
1.18 millert 194: static void
195: addcmdspecialfile(char *starget, char *rname, int destdir)
1.1 dm 196: {
197: char *rfile;
198: struct namelist *new;
1.13 mpech 199: struct subcmd *sc;
1.1 dm 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) {
1.27 guenther 213: new = xmalloc(sizeof *new);
1.8 millert 214: new->n_name = xstrdup(rfile);
1.17 millert 215: new->n_regex = NULL;
1.1 dm 216: new->n_next = updfilelist;
217: updfilelist = new;
218: }
219: }
220:
221: /*
222: * Free the file list
223: */
1.18 millert 224: static void
225: freecmdspecialfiles(void)
1.1 dm 226: {
1.13 mpech 227: struct namelist *ptr, *save;
1.1 dm 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: */
1.18 millert 244: void
245: runcmdspecial(struct cmd *cmd, opt_t opts)
1.1 dm 246: {
1.13 mpech 247: struct subcmd *sc;
248: struct namelist *f;
1.1 dm 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: }
1.26 guenther 265: (void) sendcmd(RC_FILE, "%s", f->n_name);
1.1 dm 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 */
1.26 guenther 276: (void) sendcmd(RC_COMMAND, "%s", sc->sc_name);
1.1 dm 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: */
1.18 millert 287: int
288: checkfilename(char *name)
1.1 dm 289: {
1.13 mpech 290: char *cp;
1.1 dm 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:
1.18 millert 305: void
306: freelinkinfo(struct linkbuf *lp)
1.8 millert 307: {
308: if (lp->pathname)
309: free(lp->pathname);
310: if (lp->src)
311: free(lp->src);
312: if (lp->target)
313: free(lp->target);
314: free(lp);
315: }
316:
1.1 dm 317: /*
318: * Save and retrieve hard link info
319: */
1.18 millert 320: static struct linkbuf *
321: linkinfo(struct stat *statp)
1.1 dm 322: {
323: struct linkbuf *lp;
324:
1.8 millert 325: /* XXX - linear search doesn't scale with many links */
1.1 dm 326: for (lp = ihead; lp != NULL; lp = lp->nextp)
327: if (lp->inum == statp->st_ino && lp->devnum == statp->st_dev) {
328: lp->count--;
329: return(lp);
330: }
331:
1.27 guenther 332: lp = xmalloc(sizeof(*lp));
1.1 dm 333: lp->nextp = ihead;
334: ihead = lp;
335: lp->inum = statp->st_ino;
336: lp->devnum = statp->st_dev;
337: lp->count = statp->st_nlink - 1;
1.8 millert 338: lp->pathname = xstrdup(target);
339: lp->src = xstrdup(source);
1.1 dm 340: if (Tdest)
1.8 millert 341: lp->target = xstrdup(Tdest);
1.1 dm 342: else
1.8 millert 343: lp->target = NULL;
1.1 dm 344:
1.7 millert 345: return(NULL);
1.1 dm 346: }
347:
348: /*
349: * Send a hardlink
350: */
1.18 millert 351: static int
352: sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir)
1.1 dm 353: {
1.32 deraadt 354: static char buff[PATH_MAX];
1.18 millert 355: char *lname; /* name of file to link to */
1.32 deraadt 356: char ername[PATH_MAX*4], elname[PATH_MAX*4];
1.1 dm 357:
358: debugmsg(DM_MISC,
359: "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
1.18 millert 360: rname, lp->pathname ? lp->pathname : "",
361: lp->src ? lp->src : "", lp->target ? lp->target : "");
1.1 dm 362:
1.10 millert 363: if (lp->target == NULL)
1.18 millert 364: lname = lp->pathname;
1.1 dm 365: else {
1.18 millert 366: lname = buff;
367: strlcpy(lname, remfilename(lp->src, lp->target,
368: lp->pathname, rname,
369: destdir), sizeof(buff));
370: debugmsg(DM_MISC, "sendhardlink: lname=%s\n", lname);
371: }
372: ENCODE(elname, lname);
373: ENCODE(ername, rname);
1.24 guenther 374: (void) sendcmd(C_RECVHARDLINK, "%lo %s %s",
1.18 millert 375: opts, elname, ername);
1.1 dm 376:
377: return(response());
378: }
379:
380: /*
381: * Send a file
382: */
1.18 millert 383: static int
384: sendfile(char *rname, opt_t opts, struct stat *stb, char *user,
385: char *group, int destdir)
1.1 dm 386: {
387: int goterr, f;
388: off_t i;
1.32 deraadt 389: char ername[PATH_MAX*4];
1.1 dm 390:
391: if (stb->st_nlink > 1) {
392: struct linkbuf *lp;
393:
394: if ((lp = linkinfo(stb)) != NULL)
395: return(sendhardlink(opts, lp, rname, destdir));
396: }
397:
398: if ((f = open(target, O_RDONLY)) < 0) {
399: error("%s: open for read failed: %s", target, SYSERR);
400: return(-1);
401: }
402:
403: /*
404: * Send file info
405: */
1.18 millert 406: ENCODE(ername, rname);
407:
1.24 guenther 408: (void) sendcmd(C_RECVREG, "%lo %04o %lld %lld %lld %s %s %s",
1.23 krw 409: opts, stb->st_mode & 07777, (long long) stb->st_size,
1.24 guenther 410: (long long)stb->st_mtime, (long long)stb->st_atime,
1.18 millert 411: user, group, ername);
1.1 dm 412: if (response() < 0) {
413: (void) close(f);
414: return(-1);
415: }
416:
1.18 millert 417:
1.23 krw 418: debugmsg(DM_MISC, "Send file '%s' %lld bytes\n", rname,
419: (long long) stb->st_size);
1.1 dm 420:
421: /*
422: * Set remote time out alarm handler.
423: */
424: (void) signal(SIGALRM, sighandler);
425:
426: /*
427: * Actually transfer the file
428: */
429: goterr = 0;
430: for (i = 0; i < stb->st_size; i += BUFSIZ) {
1.18 millert 431: off_t amt = BUFSIZ;
1.1 dm 432:
433: (void) alarm(rtimeout);
434: if (i + amt > stb->st_size)
435: amt = stb->st_size - i;
1.18 millert 436: if (read(f, buf, (size_t) amt) != (ssize_t) amt) {
1.1 dm 437: error("%s: File changed size", target);
438: err();
439: ++goterr;
440: /*
441: * XXX - We have to keep going because the
442: * server expects to receive a fixed number
443: * of bytes that we specified as the file size.
444: * We need Out Of Band communication to handle
445: * this situation gracefully.
446: */
447: }
1.18 millert 448: if (xwrite(rem_w, buf, (size_t) amt) < 0) {
449: error("%s: Error writing to client: %s",
1.1 dm 450: target, SYSERR);
451: err();
452: ++goterr;
453: break;
454: }
455: (void) alarm(0);
456: }
457:
458: (void) alarm(0); /* Insure alarm is off */
459: (void) close(f);
460:
461: debugmsg(DM_MISC, "Send file '%s' %s.\n",
462: (goterr) ? "failed" : "complete", rname);
463:
464: /*
465: * Check for errors and end send
466: */
467: if (goterr)
468: return(-1);
469: else {
470: ack();
471: f = response();
472: if (f < 0)
473: return(-1);
474: else if (f == 0 && IS_ON(opts, DO_COMPARE))
475: return(0);
476:
477: runspecial(target, opts, rname, destdir);
478: addcmdspecialfile(target, rname, destdir);
479:
480: return(0);
481: }
482: }
483:
484: /*
485: * Check for files on the machine being updated that are not on the master
486: * machine and remove them.
487: *
488: * Return < 0 on error.
489: * Return 0 if nothing happened.
490: * Return > 0 if anything is updated.
491: */
1.18 millert 492: static int
493: rmchk(opt_t opts)
1.1 dm 494: {
1.13 mpech 495: u_char *s;
1.1 dm 496: struct stat stb;
497: int didupdate = 0;
498: int n;
1.32 deraadt 499: char targ[PATH_MAX*4];
1.1 dm 500:
501: debugmsg(DM_CALL, "rmchk()\n");
502:
503: /*
504: * Tell the remote to clean the files from the last directory sent.
505: */
506: (void) sendcmd(C_CLEAN, "%o", IS_ON(opts, DO_VERIFY));
507: if (response() < 0)
508: return(-1);
509:
510: for ( ; ; ) {
511: n = remline(s = respbuff, sizeof(respbuff), TRUE);
512: if (n <= 0) {
513: error("rmchk: unexpected control record");
514: return(didupdate);
515: }
516:
517: switch (*s++) {
518: case CC_QUERY: /* Query if file should be removed */
519: /*
520: * Return the following codes to remove query.
521: * CC_NO -- file exists - DON'T remove.
522: * CC_YES -- file doesn't exist - REMOVE.
523: */
1.18 millert 524: if (DECODE(targ, (char *) s) == -1) {
525: error("rmchk: cannot decode file");
526: return(-1);
527: }
528: (void) snprintf(ptarget,
529: sizeof(target) - (ptarget - target),
530: "%s%s",
531: (ptarget[-1] == '/' ? "" : "/"),
532: targ);
1.1 dm 533: debugmsg(DM_MISC, "check %s\n", target);
534: if (except(target))
535: (void) sendcmd(CC_NO, NULL);
536: else if (lstat(target, &stb) < 0) {
537: if (sendcmd(CC_YES, NULL) == 0)
538: didupdate = 1;
539: } else
540: (void) sendcmd(CC_NO, NULL);
541: break;
542:
543: case CC_END:
544: *ptarget = CNULL;
545: ack();
546: return(didupdate);
547:
548: case C_LOGMSG:
549: if (n > 0)
550: message(MT_INFO, "%s", s);
551: break;
552:
1.4 millert 553: case C_NOTEMSG:
554: if (n > 0)
555: message(MT_NOTICE, "%s", s);
556: break;
557: /* Goto top of loop */
558:
1.1 dm 559: case C_ERRMSG:
560: message(MT_NERROR, "%s", s);
561: return(didupdate);
562:
563: case C_FERRMSG:
564: message(MT_FERROR, "%s", s);
565: finish();
566:
567: default:
568: error("rmchk: unexpected response '%s'", respbuff);
569: err();
570: }
571: }
572: /*NOTREACHED*/
573: }
574:
575: /*
576: * Send a directory
577: *
578: * Return < 0 on error.
579: * Return 0 if nothing happened.
580: * Return > 0 if anything is updated.
581: */
1.18 millert 582: static int
583: senddir(char *rname, opt_t opts, struct stat *stb, char *user,
584: char *group, int destdir)
1.1 dm 585: {
1.28 guenther 586: struct dirent *dp;
1.1 dm 587: DIR *d;
588: char *optarget, *cp;
589: int len;
590: int didupdate = 0;
1.32 deraadt 591: char ername[PATH_MAX*4];
1.18 millert 592:
593: /*
594: * Send recvdir command in recvit() format.
595: */
596: ENCODE(ername, rname);
1.24 guenther 597: (void) sendcmd(C_RECVDIR, "%lo %04o 0 0 0 %s %s %s",
1.18 millert 598: opts, stb->st_mode & 07777, user, group, ername);
599: if (response() < 0)
600: return(-1);
1.1 dm 601:
1.25 guenther 602: optarget = ptarget;
603:
1.1 dm 604: /*
1.2 dm 605: * Don't descend into directory
606: */
1.25 guenther 607: if (IS_ON(opts, DO_NODESCEND)) {
608: didupdate = 0;
1.24 guenther 609: goto out;
1.25 guenther 610: }
1.2 dm 611:
1.18 millert 612: if (IS_ON(opts, DO_REMOVE))
613: if (rmchk(opts) > 0)
614: ++didupdate;
615:
1.2 dm 616: if ((d = opendir(target)) == NULL) {
617: error("%s: opendir failed: %s", target, SYSERR);
1.25 guenther 618: didupdate = -1;
619: goto out;
1.2 dm 620: }
621:
1.1 dm 622: len = ptarget - target;
1.18 millert 623: while ((dp = readdir(d)) != NULL) {
1.1 dm 624: if (!strcmp(dp->d_name, ".") ||
625: !strcmp(dp->d_name, ".."))
626: continue;
1.32 deraadt 627: if (len + 1 + (int) strlen(dp->d_name) >= PATH_MAX - 1) {
1.1 dm 628: error("%s/%s: Name too long", target,
629: dp->d_name);
630: continue;
631: }
632: ptarget = optarget;
633: if (ptarget[-1] != '/')
634: *ptarget++ = '/';
635: cp = dp->d_name;
1.18 millert 636: while ((*ptarget++ = *cp++) != '\0')
637: continue;
1.1 dm 638: ptarget--;
639: if (sendit(dp->d_name, opts, destdir) > 0)
640: didupdate = 1;
641: }
642: (void) closedir(d);
643:
1.25 guenther 644: out:
1.1 dm 645: (void) sendcmd(C_END, NULL);
646: (void) response();
647:
648: ptarget = optarget;
649: *ptarget = CNULL;
650:
651: return(didupdate);
652: }
653:
654: /*
655: * Send a link
656: */
1.18 millert 657: static int
658: sendlink(char *rname, opt_t opts, struct stat *stb, char *user,
659: char *group, int destdir)
1.1 dm 660: {
1.7 millert 661: int f, n;
1.1 dm 662: static char tbuf[BUFSIZ];
1.32 deraadt 663: char lbuf[PATH_MAX];
1.1 dm 664: u_char *s;
1.32 deraadt 665: char ername[PATH_MAX*4];
1.1 dm 666:
1.24 guenther 667: debugmsg(DM_CALL, "sendlink(%s, %lx, stb, %d)\n", rname, opts, destdir);
1.1 dm 668:
669: if (stb->st_nlink > 1) {
670: struct linkbuf *lp;
671:
672: if ((lp = linkinfo(stb)) != NULL)
673: return(sendhardlink(opts, lp, rname, destdir));
674: }
675:
676: /*
677: * Gather and send basic link info
678: */
1.18 millert 679: ENCODE(ername, rname);
1.24 guenther 680: (void) sendcmd(C_RECVSYMLINK, "%lo %04o %lld %lld %lld %s %s %s",
1.23 krw 681: opts, stb->st_mode & 07777, (long long) stb->st_size,
1.24 guenther 682: (long long)stb->st_mtime, (long long)stb->st_atime,
1.18 millert 683: user, group, ername);
1.1 dm 684: if (response() < 0)
685: return(-1);
686:
687: /*
688: * Gather and send additional link info
689: */
1.7 millert 690: if ((n = readlink(target, lbuf, sizeof(lbuf)-1)) != -1)
691: lbuf[n] = '\0';
692: else {
693: error("%s: readlink failed", target);
694: err();
695: }
1.18 millert 696: (void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf);
697: ENCODE(ername, tbuf);
698: (void) sendcmd(C_NONE, "%s\n", ername);
1.1 dm 699:
1.7 millert 700: if (n != stb->st_size) {
1.1 dm 701: error("%s: file changed size", target);
702: err();
703: } else
704: ack();
705:
706: /*
707: * Check response
708: */
709: f = response();
710: if (f < 0)
711: return(-1);
712: else if (f == 0 && IS_ON(opts, DO_COMPARE))
713: return(0);
714:
715: /*
716: * Read and process responses from server.
717: * The server may send multiple messages regarding
718: * file deletes if the remote target is a directory.
719: */
720: for (;;) {
721: n = remline(s = respbuff, sizeof(respbuff), TRUE);
722: if (n == -1) /* normal EOF */
723: return(0);
724: if (n == 0) {
725: error("expected control record");
726: continue;
727: }
728:
729: switch (*s++) {
730: case C_END: /* End of send operation */
731: *ptarget = CNULL;
732: ack();
733: runspecial(target, opts, rname, destdir);
734: addcmdspecialfile(target, rname, destdir);
735: return(0);
736:
737: case C_LOGMSG:
738: if (n > 0)
739: message(MT_INFO, "%s", s);
740: break;
1.4 millert 741:
742: case C_NOTEMSG:
743: if (n > 0)
744: message(MT_NOTICE, "%s", s);
745: break;
746: /* Goto top of loop */
1.1 dm 747:
748: case C_ERRMSG:
749: message(MT_NERROR, "%s", s);
750: return(-1);
751:
752: case C_FERRMSG:
753: message(MT_FERROR, "%s", s);
754: finish();
755:
756: default:
757: error("install link: unexpected response '%s'",
758: respbuff);
759: err();
760: }
761: }
762: /*NOTREACHED*/
763: }
764:
765: /*
766: * Check to see if file needs to be updated on the remote machine.
767: * Returns:
768: * US_NOTHING - no update
769: * US_NOENT - remote doesn't exist
770: * US_OUTDATE - out of date
771: * US_DOCOMP - comparing binaries to determine if out of date
1.18 millert 772: * US_CHMOG - File modes or ownership do not match
1.1 dm 773: */
1.18 millert 774: static int
775: update(char *rname, opt_t opts, struct stat *statp)
1.1 dm 776: {
1.13 mpech 777: off_t size;
778: time_t mtime;
1.1 dm 779: unsigned short lmode;
780: unsigned short rmode;
781: char *owner = NULL, *group = NULL;
782: int done, n;
783: u_char *cp;
1.32 deraadt 784: char ername[PATH_MAX*4];
1.1 dm 785:
1.24 guenther 786: debugmsg(DM_CALL, "update(%s, 0x%lx, %p)\n", rname, opts, statp);
1.1 dm 787:
1.21 deraadt 788: switch (statp->st_mode & S_IFMT) {
789: case S_IFBLK:
790: debugmsg(DM_MISC, "%s is a block special; skipping\n", target);
791: return(US_NOTHING);
792: case S_IFCHR:
793: debugmsg(DM_MISC, "%s is a character special; skipping\n",
794: target);
795: return(US_NOTHING);
796: case S_IFIFO:
797: debugmsg(DM_MISC, "%s is a fifo; skipping\n", target);
798: return(US_NOTHING);
799: case S_IFSOCK:
800: debugmsg(DM_MISC, "%s is a socket; skipping\n", target);
801: return(US_NOTHING);
802: }
803:
1.1 dm 804: if (IS_ON(opts, DO_NOEXEC))
805: if (isexec(target, statp)) {
806: debugmsg(DM_MISC, "%s is an executable\n", target);
807: return(US_NOTHING);
808: }
809:
810: /*
811: * Check to see if the file exists on the remote machine.
812: */
1.18 millert 813: ENCODE(ername, rname);
814: (void) sendcmd(C_QUERY, "%s", ername);
1.1 dm 815:
816: for (done = 0; !done;) {
817: n = remline(cp = respbuff, sizeof(respbuff), TRUE);
818: if (n <= 0) {
819: error("update: unexpected control record in response to query");
820: return(US_NOTHING);
821: }
822:
823: switch (*cp++) {
824: case QC_ONNFS: /* Resides on a NFS */
825: debugmsg(DM_MISC,
826: "update: %s is on a NFS. Skipping...\n",
827: rname);
828: return(US_NOTHING);
829:
830: case QC_SYM: /* Is a symbolic link */
831: debugmsg(DM_MISC,
832: "update: %s is a symlink. Skipping...\n",
833: rname);
834: return(US_NOTHING);
835:
836: case QC_ONRO: /* Resides on a Read-Only fs */
837: debugmsg(DM_MISC,
838: "update: %s is on a RO fs. Skipping...\n",
839: rname);
840: return(US_NOTHING);
841:
842: case QC_YES:
843: done = 1;
844: break;
845:
846: case QC_NO: /* file doesn't exist so install it */
847: return(US_NOENT);
848:
849: case C_ERRMSG:
850: if (cp)
851: message(MT_NERROR, "%s", cp);
852: return(US_NOTHING);
853:
854: case C_FERRMSG:
855: if (cp)
856: message(MT_FERROR, "%s", cp);
857: finish();
858:
859: case C_NOTEMSG:
860: if (cp)
861: message(MT_NOTICE, "%s", cp);
862: break;
863: /* Goto top of loop */
864:
865: default:
1.18 millert 866: error("update: unexpected response to query '%s'", respbuff);
1.1 dm 867: return(US_NOTHING);
868: }
869: }
870:
871: /*
872: * Target exists, but no other info passed
873: */
874: if (n <= 1 || !S_ISREG(statp->st_mode))
875: return(US_OUTDATE);
876:
877: if (IS_ON(opts, DO_COMPARE))
878: return(US_DOCOMP);
879:
880: /*
881: * Parse size
882: */
1.23 krw 883: size = (off_t) strtoll(cp, (char **)&cp, 10);
1.1 dm 884: if (*cp++ != ' ') {
885: error("update: size not delimited");
886: return(US_NOTHING);
887: }
888:
889: /*
890: * Parse mtime
891: */
1.7 millert 892: mtime = strtol(cp, (char **)&cp, 10);
1.1 dm 893: if (*cp++ != ' ') {
894: error("update: mtime not delimited");
895: return(US_NOTHING);
896: }
897:
898: /*
899: * Parse remote file mode
900: */
1.7 millert 901: rmode = strtol(cp, (char **)&cp, 8);
1.1 dm 902: if (cp && *cp)
903: ++cp;
904:
905: /*
906: * Be backwards compatible
907: */
908: if (cp && *cp != CNULL) {
909: /*
910: * Parse remote file owner
911: */
912: owner = strtok((char *)cp, " ");
913: if (owner == NULL) {
914: error("update: owner not delimited");
915: return(US_NOTHING);
916: }
917:
918: /*
919: * Parse remote file group
920: */
1.7 millert 921: group = strtok(NULL, " ");
1.1 dm 922: if (group == NULL) {
923: error("update: group not delimited");
924: return(US_NOTHING);
925: }
926: }
927:
928: /*
929: * File needs to be updated?
930: */
931: lmode = statp->st_mode & 07777;
932:
933: debugmsg(DM_MISC, "update(%s,) local mode %04o remote mode %04o\n",
934: rname, lmode, rmode);
1.24 guenther 935: debugmsg(DM_MISC, "update(%s,) size %lld mtime %lld owner '%s' grp '%s'"
936: "\n", rname, (long long) size, (long long)mtime, owner, group);
1.1 dm 937:
938: if (statp->st_mtime != mtime) {
939: if (statp->st_mtime < mtime && IS_ON(opts, DO_YOUNGER)) {
940: message(MT_WARNING,
941: "%s: Warning: remote copy is newer",
942: target);
943: return(US_NOTHING);
944: }
945: return(US_OUTDATE);
946: }
947:
1.18 millert 948: if (statp->st_size != size) {
1.23 krw 949: debugmsg(DM_MISC, "size does not match (%lld != %lld).\n",
950: (long long) statp->st_size, (long long) size);
1.18 millert 951: return(US_OUTDATE);
952: }
953:
1.1 dm 954: if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
955: debugmsg(DM_MISC, "modes do not match (%04o != %04o).\n",
956: lmode, rmode);
1.18 millert 957: return(US_CHMOG);
1.1 dm 958: }
959:
960:
961: /*
962: * Check ownership
963: */
964: if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
965: if (!IS_ON(opts, DO_NUMCHKOWNER)) {
966: /* Check by string compare */
967: if (strcmp(owner, getusername(statp->st_uid,
968: target, opts)) != 0) {
969: debugmsg(DM_MISC,
970: "owner does not match (%s != %s).\n",
971: getusername(statp->st_uid,
972: target, opts), owner);
1.18 millert 973: return(US_CHMOG);
1.1 dm 974: }
975: } else {
976: /*
977: * Check numerically.
978: * Allow negative numbers.
979: */
1.18 millert 980: while (*owner && !isdigit((unsigned char)*owner) &&
981: (*owner != '-'))
1.1 dm 982: ++owner;
1.29 guenther 983: if (owner && (uid_t)atoi(owner) != statp->st_uid) {
1.1 dm 984: debugmsg(DM_MISC,
985: "owner does not match (%d != %s).\n",
986: statp->st_uid, owner);
1.18 millert 987: return(US_CHMOG);
1.1 dm 988: }
989: }
990: }
991:
992: if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
993: if (!IS_ON(opts, DO_NUMCHKGROUP)) {
994: /* Check by string compare */
995: if (strcmp(group, getgroupname(statp->st_gid,
996: target, opts)) != 0) {
997: debugmsg(DM_MISC,
998: "group does not match (%s != %s).\n",
999: getgroupname(statp->st_gid,
1000: target, opts), group);
1.18 millert 1001: return(US_CHMOG);
1.1 dm 1002: }
1003: } else {
1004: /* Check numerically */
1005: /* Allow negative gid */
1.18 millert 1006: while (*group && !isdigit((unsigned char) *group) &&
1007: (*group != '-'))
1.1 dm 1008: ++group;
1.29 guenther 1009: if (group && (gid_t)atoi(group) != statp->st_gid) {
1.1 dm 1010: debugmsg(DM_MISC,
1011: "group does not match (%d != %s).\n",
1012: statp->st_gid, group);
1.18 millert 1013: return(US_CHMOG);
1.1 dm 1014: }
1015: }
1016: }
1017:
1018: return(US_NOTHING);
1019: }
1020:
1021: /*
1022: * Stat a file
1023: */
1.18 millert 1024: static int
1025: dostat(char *file, struct stat *statbuf, opt_t opts)
1.1 dm 1026: {
1027: int s;
1028:
1029: if (IS_ON(opts, DO_FOLLOW))
1030: s = stat(file, statbuf);
1031: else
1032: s = lstat(file, statbuf);
1033:
1034: if (s < 0)
1035: error("%s: %s failed: %s", file,
1036: IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
1037: return(s);
1038: }
1039:
1040: /*
1.18 millert 1041: * We need to just change file info.
1042: */
1043: static int
1.30 guenther 1044: statupdate(int u, char *starget, opt_t opts, char *rname, int destdir,
1.18 millert 1045: struct stat *st, char *user, char *group)
1046: {
1047: int rv = 0;
1.32 deraadt 1048: char ername[PATH_MAX*4];
1.18 millert 1049: int lmode = st->st_mode & 07777;
1050:
1051: if (u == US_CHMOG) {
1052: if (IS_ON(opts, DO_VERIFY)) {
1053: message(MT_INFO,
1054: "%s: need to change to perm %04o, owner %s, group %s",
1.30 guenther 1055: starget, lmode, user, group);
1056: runspecial(starget, opts, rname, destdir);
1.18 millert 1057: }
1058: else {
1059: message(MT_CHANGE, "%s: change to perm %04o, owner %s, group %s",
1.30 guenther 1060: starget, lmode, user, group);
1.18 millert 1061: ENCODE(ername, rname);
1.24 guenther 1062: (void) sendcmd(C_CHMOG, "%lo %04o %s %s %s",
1.18 millert 1063: opts, lmode, user, group, ername);
1064: (void) response();
1065: }
1066: rv = 1;
1067: }
1068: return(rv);
1069: }
1070:
1071:
1072: /*
1073: * We need to install/update:
1074: */
1075: static int
1.30 guenther 1076: fullupdate(int u, char *starget, opt_t opts, char *rname, int destdir,
1.18 millert 1077: struct stat *st, char *user, char *group)
1078: {
1079: /*
1080: * No entry - need to install
1081: */
1082: if (u == US_NOENT) {
1083: if (IS_ON(opts, DO_VERIFY)) {
1.30 guenther 1084: message(MT_INFO, "%s: need to install", starget);
1085: runspecial(starget, opts, rname, destdir);
1.18 millert 1086: return(1);
1087: }
1088: if (!IS_ON(opts, DO_QUIET))
1.30 guenther 1089: message(MT_CHANGE, "%s: installing", starget);
1.18 millert 1090: FLAG_OFF(opts, (DO_COMPARE|DO_REMOVE));
1091: }
1092:
1093: /*
1094: * Handle special file types, including directories and symlinks
1095: */
1096: if (S_ISDIR(st->st_mode)) {
1097: if (senddir(rname, opts, st, user, group, destdir) > 0)
1098: return(1);
1099: return(0);
1100: } else if (S_ISLNK(st->st_mode)) {
1101: if (u == US_NOENT)
1102: FLAG_ON(opts, DO_COMPARE);
1103: /*
1104: * Since we always send link info to the server
1105: * so the server can determine if the remote link
1.21 deraadt 1106: * is correct, we never get any acknowledgement
1.18 millert 1107: * from the server whether the link was really
1108: * updated or not.
1109: */
1110: (void) sendlink(rname, opts, st, user, group, destdir);
1111: return(0);
1112: } else if (S_ISREG(st->st_mode)) {
1113: if (u == US_OUTDATE) {
1114: if (IS_ON(opts, DO_VERIFY)) {
1.30 guenther 1115: message(MT_INFO, "%s: need to update", starget);
1116: runspecial(starget, opts, rname, destdir);
1.18 millert 1117: return(1);
1118: }
1119: if (!IS_ON(opts, DO_QUIET))
1.30 guenther 1120: message(MT_CHANGE, "%s: updating", starget);
1.18 millert 1121: }
1122: return (sendfile(rname, opts, st, user, group, destdir) == 0);
1123: } else {
1.30 guenther 1124: message(MT_INFO, "%s: unknown file type 0%o", starget,
1.18 millert 1125: st->st_mode);
1126: return(0);
1127: }
1128: }
1129:
1130: /*
1.1 dm 1131: * Transfer the file or directory in target[].
1132: * rname is the name of the file on the remote host.
1133: *
1134: * Return < 0 on error.
1135: * Return 0 if nothing happened.
1136: * Return > 0 if anything is updated.
1137: */
1.18 millert 1138: static int
1139: sendit(char *rname, opt_t opts, int destdir)
1.1 dm 1140: {
1141: static struct stat stb;
1142: char *user, *group;
1143: int u, len;
1144:
1145: /*
1146: * Remove possible accidental newline
1147: */
1148: len = strlen(rname);
1149: if (len > 0 && rname[len-1] == '\n')
1150: rname[len-1] = CNULL;
1151:
1152: if (checkfilename(rname) != 0)
1153: return(-1);
1154:
1.24 guenther 1155: debugmsg(DM_CALL, "sendit(%s, 0x%lx) called\n", rname, opts);
1.1 dm 1156:
1157: if (except(target))
1158: return(0);
1159:
1160: if (dostat(target, &stb, opts) < 0)
1161: return(-1);
1162:
1163: /*
1164: * Does rname need updating?
1165: */
1166: u = update(rname, opts, &stb);
1.24 guenther 1167: debugmsg(DM_MISC, "sendit(%s, 0x%lx): update status of %s is %d\n",
1.1 dm 1168: rname, opts, target, u);
1169:
1170: /*
1171: * Don't need to update the file, but we may need to save hardlink
1172: * info.
1173: */
1174: if (u == US_NOTHING) {
1175: if (S_ISREG(stb.st_mode) && stb.st_nlink > 1)
1176: (void) linkinfo(&stb);
1177: return(0);
1178: }
1179:
1180: user = getusername(stb.st_uid, target, opts);
1181: group = getgroupname(stb.st_gid, target, opts);
1182:
1.18 millert 1183: if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM))
1184: u = US_OUTDATE;
1.1 dm 1185:
1.18 millert 1186: if (u == US_NOENT || u == US_OUTDATE || u == US_DOCOMP)
1187: return(fullupdate(u, target, opts, rname, destdir, &stb,
1188: user, group));
1189:
1190: if (u == US_CHMOG)
1191: return(statupdate(u, target, opts, rname, destdir, &stb,
1192: user, group));
1.1 dm 1193:
1.18 millert 1194: return(0);
1.1 dm 1195: }
1196:
1197: /*
1198: * Remove temporary files and do any cleanup operations before exiting.
1199: */
1.18 millert 1200: void
1201: cleanup(int dummy)
1.1 dm 1202: {
1203: char *file;
1204:
1.18 millert 1205: if ((file = getnotifyfile()) != NULL)
1.1 dm 1206: (void) unlink(file);
1207: }
1208:
1209: /*
1210: * Update the file(s) if they are different.
1211: * destdir = 1 if destination should be a directory
1212: * (i.e., more than one source is being copied to the same destination).
1213: *
1214: * Return < 0 on error.
1215: * Return 0 if nothing updated.
1216: * Return > 0 if something was updated.
1217: */
1.18 millert 1218: int
1219: install(char *src, char *dest, int ddir, int destdir, opt_t opts)
1.1 dm 1220: {
1.32 deraadt 1221: static char destcopy[PATH_MAX];
1.1 dm 1222: char *rname;
1223: int didupdate = 0;
1.32 deraadt 1224: char ername[PATH_MAX*4];
1.1 dm 1225:
1226: debugmsg(DM_CALL,
1.24 guenther 1227: "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%ld) start\n",
1.1 dm 1228: (src?src:"NULL"), (dest?dest:"NULL"), ddir, destdir, opts);
1229: /*
1230: * Save source name
1231: */
1232: if (IS_ON(opts, DO_WHOLE))
1233: source[0] = CNULL;
1234: else
1.18 millert 1235: (void) strlcpy(source, src, sizeof(source));
1.1 dm 1236:
1237: if (dest == NULL) {
1238: FLAG_OFF(opts, DO_WHOLE); /* WHOLE only useful if renaming */
1239: dest = src;
1240: }
1241:
1242: if (checkfilename(dest) != 0)
1243: return(-1);
1244:
1245: if (nflag || debug) {
1246: static char buff[BUFSIZ];
1247: char *cp;
1248:
1249: cp = getondistoptlist(opts);
1.18 millert 1250: (void) snprintf(buff, sizeof(buff), "%s%s%s %s %s",
1.1 dm 1251: IS_ON(opts, DO_VERIFY) ? "verify" : "install",
1252: (cp) ? " -o" : "", (cp) ? cp : "",
1253: src, dest);
1254: if (nflag) {
1255: printf("%s\n", buff);
1256: return(0);
1257: } else
1258: debugmsg(DM_MISC, "%s\n", buff);
1259: }
1260:
1.16 millert 1261: rname = exptilde(target, src, sizeof(target));
1.1 dm 1262: if (rname == NULL)
1263: return(-1);
1264: ptarget = target;
1265: while (*ptarget)
1266: ptarget++;
1267: /*
1268: * If we are renaming a directory and we want to preserve
1.20 jsg 1269: * the directory hierarchy (-w), we must strip off the leading
1.1 dm 1270: * directory name and preserve the rest.
1271: */
1272: if (IS_ON(opts, DO_WHOLE)) {
1273: while (*rname == '/')
1274: rname++;
1275: ddir = 1;
1276: destdir = 1;
1277: } else {
1278: rname = strrchr(target, '/');
1279: /* Check if no '/' or target ends in '/' */
1280: if (rname == NULL ||
1281: rname+1 == NULL ||
1282: *(rname+1) == CNULL)
1283: rname = target;
1284: else
1285: rname++;
1286: }
1287:
1288: debugmsg(DM_MISC,
1289: "install: target=%s src=%s rname=%s dest='%s' destdir=%d, ddir=%d\n",
1290: target, source, rname, dest, destdir, ddir);
1291:
1292: /*
1293: * Pass the destination file/directory name to remote.
1294: */
1.18 millert 1295: ENCODE(ername, dest);
1.1 dm 1296: if (ddir)
1.24 guenther 1297: (void) sendcmd(C_DIRTARGET, "%lo %s", opts, ername);
1.1 dm 1298: else
1.24 guenther 1299: (void) sendcmd(C_TARGET, "%lo %s", opts, ername);
1.1 dm 1300: if (response() < 0)
1301: return(-1);
1302:
1303: /*
1304: * Save the name of the remote target destination if we are
1305: * in WHOLE mode (destdir > 0) or if the source and destination
1306: * are not the same. This info will be used later for maintaining
1307: * hardlink info.
1308: */
1309: if (destdir || (src && dest && strcmp(src, dest))) {
1.18 millert 1310: (void) strlcpy(destcopy, dest, sizeof(destcopy));
1.1 dm 1311: Tdest = destcopy;
1312: }
1313:
1314: didupdate = sendit(rname, opts, destdir);
1315: Tdest = 0;
1316:
1317: return(didupdate);
1318: }