Annotation of src/usr.bin/rdist/client.c, Revision 1.36
1.36 ! espie 1: /* $OpenBSD: client.c,v 1.35 2015/12/09 19:39:10 mmcc 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.36 ! espie 42: #include "gram.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: {
1.35 mmcc 308: free(lp->pathname);
309: free(lp->src);
310: free(lp->target);
1.8 millert 311: free(lp);
312: }
313:
1.1 dm 314: /*
315: * Save and retrieve hard link info
316: */
1.18 millert 317: static struct linkbuf *
318: linkinfo(struct stat *statp)
1.1 dm 319: {
320: struct linkbuf *lp;
321:
1.8 millert 322: /* XXX - linear search doesn't scale with many links */
1.1 dm 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:
1.27 guenther 329: lp = xmalloc(sizeof(*lp));
1.1 dm 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;
1.8 millert 335: lp->pathname = xstrdup(target);
336: lp->src = xstrdup(source);
1.1 dm 337: if (Tdest)
1.8 millert 338: lp->target = xstrdup(Tdest);
1.1 dm 339: else
1.8 millert 340: lp->target = NULL;
1.1 dm 341:
1.7 millert 342: return(NULL);
1.1 dm 343: }
344:
345: /*
346: * Send a hardlink
347: */
1.18 millert 348: static int
349: sendhardlink(opt_t opts, struct linkbuf *lp, char *rname, int destdir)
1.1 dm 350: {
1.32 deraadt 351: static char buff[PATH_MAX];
1.18 millert 352: char *lname; /* name of file to link to */
1.32 deraadt 353: char ername[PATH_MAX*4], elname[PATH_MAX*4];
1.1 dm 354:
355: debugmsg(DM_MISC,
356: "sendhardlink: rname='%s' pathname='%s' src='%s' target='%s'\n",
1.18 millert 357: rname, lp->pathname ? lp->pathname : "",
358: lp->src ? lp->src : "", lp->target ? lp->target : "");
1.1 dm 359:
1.10 millert 360: if (lp->target == NULL)
1.18 millert 361: lname = lp->pathname;
1.1 dm 362: else {
1.18 millert 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);
1.34 guenther 371: (void) sendcmd(C_RECVHARDLINK, "%o %s %s", opts, elname, ername);
1.1 dm 372:
373: return(response());
374: }
375:
376: /*
377: * Send a file
378: */
1.18 millert 379: static int
380: sendfile(char *rname, opt_t opts, struct stat *stb, char *user,
381: char *group, int destdir)
1.1 dm 382: {
383: int goterr, f;
384: off_t i;
1.32 deraadt 385: char ername[PATH_MAX*4];
1.1 dm 386:
387: if (stb->st_nlink > 1) {
388: struct linkbuf *lp;
389:
390: if ((lp = linkinfo(stb)) != NULL)
391: return(sendhardlink(opts, lp, rname, destdir));
392: }
393:
394: if ((f = open(target, O_RDONLY)) < 0) {
395: error("%s: open for read failed: %s", target, SYSERR);
396: return(-1);
397: }
398:
399: /*
400: * Send file info
401: */
1.18 millert 402: ENCODE(ername, rname);
403:
1.34 guenther 404: (void) sendcmd(C_RECVREG, "%o %04o %lld %lld %lld %s %s %s",
1.23 krw 405: opts, stb->st_mode & 07777, (long long) stb->st_size,
1.24 guenther 406: (long long)stb->st_mtime, (long long)stb->st_atime,
1.18 millert 407: user, group, ername);
1.1 dm 408: if (response() < 0) {
409: (void) close(f);
410: return(-1);
411: }
412:
1.18 millert 413:
1.23 krw 414: debugmsg(DM_MISC, "Send file '%s' %lld bytes\n", rname,
415: (long long) stb->st_size);
1.1 dm 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) {
1.18 millert 427: off_t amt = BUFSIZ;
1.1 dm 428:
429: (void) alarm(rtimeout);
430: if (i + amt > stb->st_size)
431: amt = stb->st_size - i;
1.18 millert 432: if (read(f, buf, (size_t) amt) != (ssize_t) amt) {
1.1 dm 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: }
1.18 millert 444: if (xwrite(rem_w, buf, (size_t) amt) < 0) {
445: error("%s: Error writing to client: %s",
1.1 dm 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: */
1.18 millert 488: static int
489: rmchk(opt_t opts)
1.1 dm 490: {
1.13 mpech 491: u_char *s;
1.1 dm 492: struct stat stb;
493: int didupdate = 0;
494: int n;
1.32 deraadt 495: char targ[PATH_MAX*4];
1.1 dm 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: */
1.18 millert 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);
1.1 dm 529: debugmsg(DM_MISC, "check %s\n", target);
530: if (except(target))
531: (void) sendcmd(CC_NO, NULL);
532: else if (lstat(target, &stb) < 0) {
533: if (sendcmd(CC_YES, NULL) == 0)
534: didupdate = 1;
535: } else
536: (void) sendcmd(CC_NO, NULL);
537: break;
538:
539: case CC_END:
540: *ptarget = CNULL;
541: ack();
542: return(didupdate);
543:
544: case C_LOGMSG:
545: if (n > 0)
546: message(MT_INFO, "%s", s);
547: break;
548:
1.4 millert 549: case C_NOTEMSG:
550: if (n > 0)
551: message(MT_NOTICE, "%s", s);
552: break;
553: /* Goto top of loop */
554:
1.1 dm 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: */
1.18 millert 578: static int
579: senddir(char *rname, opt_t opts, struct stat *stb, char *user,
580: char *group, int destdir)
1.1 dm 581: {
1.28 guenther 582: struct dirent *dp;
1.1 dm 583: DIR *d;
584: char *optarget, *cp;
585: int len;
586: int didupdate = 0;
1.32 deraadt 587: char ername[PATH_MAX*4];
1.18 millert 588:
589: /*
590: * Send recvdir command in recvit() format.
591: */
592: ENCODE(ername, rname);
1.34 guenther 593: (void) sendcmd(C_RECVDIR, "%o %04o 0 0 0 %s %s %s",
1.18 millert 594: opts, stb->st_mode & 07777, user, group, ername);
595: if (response() < 0)
596: return(-1);
1.1 dm 597:
1.25 guenther 598: optarget = ptarget;
599:
1.1 dm 600: /*
1.2 dm 601: * Don't descend into directory
602: */
1.25 guenther 603: if (IS_ON(opts, DO_NODESCEND)) {
604: didupdate = 0;
1.24 guenther 605: goto out;
1.25 guenther 606: }
1.2 dm 607:
1.18 millert 608: if (IS_ON(opts, DO_REMOVE))
609: if (rmchk(opts) > 0)
610: ++didupdate;
611:
1.2 dm 612: if ((d = opendir(target)) == NULL) {
613: error("%s: opendir failed: %s", target, SYSERR);
1.25 guenther 614: didupdate = -1;
615: goto out;
1.2 dm 616: }
617:
1.1 dm 618: len = ptarget - target;
1.18 millert 619: while ((dp = readdir(d)) != NULL) {
1.1 dm 620: if (!strcmp(dp->d_name, ".") ||
621: !strcmp(dp->d_name, ".."))
622: continue;
1.32 deraadt 623: if (len + 1 + (int) strlen(dp->d_name) >= PATH_MAX - 1) {
1.1 dm 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;
1.18 millert 632: while ((*ptarget++ = *cp++) != '\0')
633: continue;
1.1 dm 634: ptarget--;
635: if (sendit(dp->d_name, opts, destdir) > 0)
636: didupdate = 1;
637: }
638: (void) closedir(d);
639:
1.25 guenther 640: out:
1.1 dm 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: */
1.18 millert 653: static int
654: sendlink(char *rname, opt_t opts, struct stat *stb, char *user,
655: char *group, int destdir)
1.1 dm 656: {
1.7 millert 657: int f, n;
1.1 dm 658: static char tbuf[BUFSIZ];
1.32 deraadt 659: char lbuf[PATH_MAX];
1.1 dm 660: u_char *s;
1.32 deraadt 661: char ername[PATH_MAX*4];
1.1 dm 662:
1.34 guenther 663: debugmsg(DM_CALL, "sendlink(%s, %#x, stb, %d)\n", rname, opts, destdir);
1.1 dm 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: */
1.18 millert 675: ENCODE(ername, rname);
1.34 guenther 676: (void) sendcmd(C_RECVSYMLINK, "%o %04o %lld %lld %lld %s %s %s",
1.23 krw 677: opts, stb->st_mode & 07777, (long long) stb->st_size,
1.24 guenther 678: (long long)stb->st_mtime, (long long)stb->st_atime,
1.18 millert 679: user, group, ername);
1.1 dm 680: if (response() < 0)
681: return(-1);
682:
683: /*
684: * Gather and send additional link info
685: */
1.7 millert 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: }
1.18 millert 692: (void) snprintf(tbuf, sizeof(tbuf), "%.*s", (int) stb->st_size, lbuf);
693: ENCODE(ername, tbuf);
694: (void) sendcmd(C_NONE, "%s\n", ername);
1.1 dm 695:
1.7 millert 696: if (n != stb->st_size) {
1.1 dm 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;
1.4 millert 737:
738: case C_NOTEMSG:
739: if (n > 0)
740: message(MT_NOTICE, "%s", s);
741: break;
742: /* Goto top of loop */
1.1 dm 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
1.18 millert 768: * US_CHMOG - File modes or ownership do not match
1.1 dm 769: */
1.18 millert 770: static int
771: update(char *rname, opt_t opts, struct stat *statp)
1.1 dm 772: {
1.13 mpech 773: off_t size;
774: time_t mtime;
1.1 dm 775: unsigned short lmode;
776: unsigned short rmode;
777: char *owner = NULL, *group = NULL;
778: int done, n;
779: u_char *cp;
1.32 deraadt 780: char ername[PATH_MAX*4];
1.1 dm 781:
1.34 guenther 782: debugmsg(DM_CALL, "update(%s, %#x, %p)\n", rname, opts, statp);
1.1 dm 783:
1.21 deraadt 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:
1.1 dm 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: */
1.18 millert 809: ENCODE(ername, rname);
810: (void) sendcmd(C_QUERY, "%s", ername);
1.1 dm 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:
1.18 millert 862: error("update: unexpected response to query '%s'", respbuff);
1.1 dm 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: */
1.23 krw 879: size = (off_t) strtoll(cp, (char **)&cp, 10);
1.1 dm 880: if (*cp++ != ' ') {
881: error("update: size not delimited");
882: return(US_NOTHING);
883: }
884:
885: /*
886: * Parse mtime
887: */
1.7 millert 888: mtime = strtol(cp, (char **)&cp, 10);
1.1 dm 889: if (*cp++ != ' ') {
890: error("update: mtime not delimited");
891: return(US_NOTHING);
892: }
893:
894: /*
895: * Parse remote file mode
896: */
1.7 millert 897: rmode = strtol(cp, (char **)&cp, 8);
1.1 dm 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: */
1.7 millert 917: group = strtok(NULL, " ");
1.1 dm 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:
1.34 guenther 929: debugmsg(DM_MISC, "update(%s,) local mode %#04o remote mode %#04o\n",
1.1 dm 930: rname, lmode, rmode);
1.24 guenther 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);
1.1 dm 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:
1.18 millert 944: if (statp->st_size != size) {
1.23 krw 945: debugmsg(DM_MISC, "size does not match (%lld != %lld).\n",
946: (long long) statp->st_size, (long long) size);
1.18 millert 947: return(US_OUTDATE);
948: }
949:
1.1 dm 950: if (!IS_ON(opts, DO_NOCHKMODE) && lmode != rmode) {
1.34 guenther 951: debugmsg(DM_MISC, "modes do not match (%#04o != %#04o).\n",
1.1 dm 952: lmode, rmode);
1.18 millert 953: return(US_CHMOG);
1.1 dm 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);
1.18 millert 969: return(US_CHMOG);
1.1 dm 970: }
971: } else {
972: /*
973: * Check numerically.
974: * Allow negative numbers.
975: */
1.18 millert 976: while (*owner && !isdigit((unsigned char)*owner) &&
977: (*owner != '-'))
1.1 dm 978: ++owner;
1.29 guenther 979: if (owner && (uid_t)atoi(owner) != statp->st_uid) {
1.1 dm 980: debugmsg(DM_MISC,
981: "owner does not match (%d != %s).\n",
982: statp->st_uid, owner);
1.18 millert 983: return(US_CHMOG);
1.1 dm 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);
1.18 millert 997: return(US_CHMOG);
1.1 dm 998: }
999: } else {
1000: /* Check numerically */
1001: /* Allow negative gid */
1.18 millert 1002: while (*group && !isdigit((unsigned char) *group) &&
1003: (*group != '-'))
1.1 dm 1004: ++group;
1.29 guenther 1005: if (group && (gid_t)atoi(group) != statp->st_gid) {
1.1 dm 1006: debugmsg(DM_MISC,
1007: "group does not match (%d != %s).\n",
1008: statp->st_gid, group);
1.18 millert 1009: return(US_CHMOG);
1.1 dm 1010: }
1011: }
1012: }
1013:
1014: return(US_NOTHING);
1015: }
1016:
1017: /*
1018: * Stat a file
1019: */
1.18 millert 1020: static int
1021: dostat(char *file, struct stat *statbuf, opt_t opts)
1.1 dm 1022: {
1023: int s;
1024:
1025: if (IS_ON(opts, DO_FOLLOW))
1026: s = stat(file, statbuf);
1027: else
1028: s = lstat(file, statbuf);
1029:
1030: if (s < 0)
1031: error("%s: %s failed: %s", file,
1032: IS_ON(opts, DO_FOLLOW) ? "stat" : "lstat", SYSERR);
1033: return(s);
1034: }
1035:
1036: /*
1.18 millert 1037: * We need to just change file info.
1038: */
1039: static int
1.30 guenther 1040: statupdate(int u, char *starget, opt_t opts, char *rname, int destdir,
1.18 millert 1041: struct stat *st, char *user, char *group)
1042: {
1043: int rv = 0;
1.32 deraadt 1044: char ername[PATH_MAX*4];
1.18 millert 1045: int lmode = st->st_mode & 07777;
1046:
1047: if (u == US_CHMOG) {
1048: if (IS_ON(opts, DO_VERIFY)) {
1049: message(MT_INFO,
1.34 guenther 1050: "%s: need to change to perm %#04o, owner %s, group %s",
1.30 guenther 1051: starget, lmode, user, group);
1052: runspecial(starget, opts, rname, destdir);
1.18 millert 1053: }
1054: else {
1.34 guenther 1055: message(MT_CHANGE, "%s: change to perm %#04o, owner %s, group %s",
1.30 guenther 1056: starget, lmode, user, group);
1.18 millert 1057: ENCODE(ername, rname);
1.34 guenther 1058: (void) sendcmd(C_CHMOG, "%o %04o %s %s %s",
1.18 millert 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
1.30 guenther 1072: fullupdate(int u, char *starget, opt_t opts, char *rname, int destdir,
1.18 millert 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)) {
1.30 guenther 1080: message(MT_INFO, "%s: need to install", starget);
1081: runspecial(starget, opts, rname, destdir);
1.18 millert 1082: return(1);
1083: }
1084: if (!IS_ON(opts, DO_QUIET))
1.30 guenther 1085: message(MT_CHANGE, "%s: installing", starget);
1.18 millert 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
1.21 deraadt 1102: * is correct, we never get any acknowledgement
1.18 millert 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)) {
1.30 guenther 1111: message(MT_INFO, "%s: need to update", starget);
1112: runspecial(starget, opts, rname, destdir);
1.18 millert 1113: return(1);
1114: }
1115: if (!IS_ON(opts, DO_QUIET))
1.30 guenther 1116: message(MT_CHANGE, "%s: updating", starget);
1.18 millert 1117: }
1118: return (sendfile(rname, opts, st, user, group, destdir) == 0);
1119: } else {
1.34 guenther 1120: message(MT_INFO, "%s: unknown file type %#o", starget,
1.18 millert 1121: st->st_mode);
1122: return(0);
1123: }
1124: }
1125:
1126: /*
1.1 dm 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: */
1.18 millert 1134: static int
1135: sendit(char *rname, opt_t opts, int destdir)
1.1 dm 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:
1.34 guenther 1151: debugmsg(DM_CALL, "sendit(%s, %#x) called\n", rname, opts);
1.1 dm 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);
1.34 guenther 1163: debugmsg(DM_MISC, "sendit(%s, %#x): update status of %s is %d\n",
1.1 dm 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:
1.18 millert 1179: if (u == US_CHMOG && IS_OFF(opts, DO_UPDATEPERM))
1180: u = US_OUTDATE;
1.1 dm 1181:
1.18 millert 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));
1.1 dm 1189:
1.18 millert 1190: return(0);
1.1 dm 1191: }
1192:
1193: /*
1194: * Remove temporary files and do any cleanup operations before exiting.
1195: */
1.18 millert 1196: void
1197: cleanup(int dummy)
1.1 dm 1198: {
1199: char *file;
1200:
1.18 millert 1201: if ((file = getnotifyfile()) != NULL)
1.1 dm 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: */
1.18 millert 1214: int
1215: install(char *src, char *dest, int ddir, int destdir, opt_t opts)
1.1 dm 1216: {
1.32 deraadt 1217: static char destcopy[PATH_MAX];
1.1 dm 1218: char *rname;
1219: int didupdate = 0;
1.32 deraadt 1220: char ername[PATH_MAX*4];
1.1 dm 1221:
1222: debugmsg(DM_CALL,
1.34 guenther 1223: "install(src=%s,dest=%s,ddir=%d,destdir=%d,opts=%#x) start\n",
1.1 dm 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
1.18 millert 1231: (void) strlcpy(source, src, sizeof(source));
1.1 dm 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);
1.18 millert 1246: (void) snprintf(buff, sizeof(buff), "%s%s%s %s %s",
1.1 dm 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:
1.16 millert 1257: rname = exptilde(target, src, sizeof(target));
1.1 dm 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
1.20 jsg 1265: * the directory hierarchy (-w), we must strip off the leading
1.1 dm 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: */
1.18 millert 1291: ENCODE(ername, dest);
1.1 dm 1292: if (ddir)
1.34 guenther 1293: (void) sendcmd(C_DIRTARGET, "%o %s", opts, ername);
1.1 dm 1294: else
1.34 guenther 1295: (void) sendcmd(C_TARGET, "%o %s", opts, ername);
1.1 dm 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))) {
1.18 millert 1306: (void) strlcpy(destcopy, dest, sizeof(destcopy));
1.1 dm 1307: Tdest = destcopy;
1308: }
1309:
1310: didupdate = sendit(rname, opts, destdir);
1311: Tdest = 0;
1312:
1313: return(didupdate);
1314: }