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