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