Annotation of src/usr.bin/rdistd/server.c, Revision 1.20
1.20 ! deraadt 1: /* $OpenBSD: server.c,v 1.19 2008/05/25 22:33:56 millert Exp $ */
1.4 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.15 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: */
1.14 millert 31: #include "defs.h"
32:
1.1 dm 33: #ifndef lint
1.7 millert 34: #if 0
1.14 millert 35: static char RCSid[] __attribute__((__unused__)) =
36: "$From: server.c,v 1.10 1999/08/04 15:57:33 christos Exp $";
1.7 millert 37: #else
1.14 millert 38: static char RCSid[] __attribute__((__unused__)) =
1.20 ! deraadt 39: "$OpenBSD: server.c,v 1.19 2008/05/25 22:33:56 millert Exp $";
1.7 millert 40: #endif
1.1 dm 41:
1.14 millert 42: static char sccsid[] __attribute__((__unused__)) =
43: "@(#)server.c 5.3 (Berkeley) 6/7/86";
1.1 dm 44:
1.14 millert 45: static char copyright[] __attribute__((__unused__)) =
1.1 dm 46: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
47: All rights reserved.\n";
48: #endif /* not lint */
49:
50: /*
51: * Server routines
52: */
53:
54: char tempname[sizeof _RDIST_TMP + 1]; /* Tmp file name */
55: char buf[BUFSIZ]; /* general purpose buffer */
56: char target[MAXPATHLEN]; /* target/source directory name */
57: char *ptarget; /* pointer to end of target name */
58: int catname = 0; /* cat name to target name */
59: char *sptarget[32]; /* stack of saved ptarget's for directories */
60: char *fromhost = NULL; /* Client hostname */
61: static long min_freespace = 0; /* Minimium free space on a filesystem */
62: static long min_freefiles = 0; /* Minimium free # files on a filesystem */
63: int oumask; /* Old umask */
64:
1.14 millert 65: static int cattarget(char *);
66: static int setownership(char *, int, UID_T, GID_T, int);
67: static int setfilemode(char *, int, int, int);
68: static int fchog(int, char *, char *, char *, int);
69: static int removefile(struct stat *, int);
70: static void doclean(char *);
71: static void clean(char *);
72: static void dospecial(char *);
73: static void docmdspecial(void);
74: static void query(char *);
75: static int chkparent(char *, opt_t);
76: static char *savetarget(char *, opt_t);
1.19 millert 77: static void recvfile(char *, opt_t, int, char *, char *, time_t, time_t, off_t);
1.14 millert 78: static void recvdir(opt_t, int, char *, char *);
79: static void recvlink(char *, opt_t, int, off_t);
80: static void hardlink(char *);
81: static void setconfig(char *);
82: static void recvit(char *, int);
83: static void dochmog(char *);
84: static void settarget(char *, int);
85:
1.1 dm 86: /*
87: * Cat "string" onto the target buffer with error checking.
88: */
1.14 millert 89: static int
90: cattarget(char *string)
1.1 dm 91: {
92: if (strlen(string) + strlen(target) + 2 > sizeof(target)) {
93: message(MT_INFO, "target buffer is not large enough.");
94: return(-1);
95: }
96: if (!ptarget) {
97: message(MT_INFO, "NULL target pointer set.");
98: return(-10);
99: }
100:
1.14 millert 101: (void) snprintf(ptarget, sizeof(target) - (ptarget - target),
102: "/%s", string);
1.1 dm 103:
104: return(0);
105: }
106:
107: /*
108: * Set uid and gid ownership of a file.
109: */
1.14 millert 110: static int
111: setownership(char *file, int fd, UID_T uid, GID_T gid, int link)
1.1 dm 112: {
113: int status = -1;
114:
115: /*
116: * We assume only the Superuser can change uid ownership.
117: */
1.14 millert 118: if (getuid() != 0)
119: uid = -1;
120:
121: /*
122: * If we are dealing with a symlink, only try to change it if
123: * we have lchown, if we don't leave it alone.
124: */
125: #if defined(HAVE_LCHOWN)
126: if (link)
127: status = lchown(file, (CHOWN_UID_T) uid,
128: (CHOWN_GID_T) gid);
129: #else
130: if (link)
131: return 0;
1.1 dm 132: #endif
1.14 millert 133:
1.1 dm 134: #if defined(HAVE_FCHOWN)
1.14 millert 135: if (fd != -1 && !link)
136: status = fchown(fd, (CHOWN_UID_T) uid,
137: (CHOWN_GID_T) gid);
1.1 dm 138: #endif
1.14 millert 139: if (status < 0 && !link)
140: status = chown(file, (CHOWN_UID_T) uid,
141: (CHOWN_GID_T) gid);
1.1 dm 142:
1.14 millert 143: if (status < 0) {
144: if (uid == (UID_T) -1)
1.1 dm 145: message(MT_NOTICE, "%s: chgrp %d failed: %s",
1.14 millert 146: target, gid, SYSERR);
147: else
148: message(MT_NOTICE, "%s: chown %d.%d failed: %s",
149: target, uid, gid, SYSERR);
150: return(-1);
1.1 dm 151: }
152:
153: return(0);
154: }
155:
156: /*
157: * Set mode of a file
158: */
1.14 millert 159: static int
160: setfilemode(char *file, int fd, int mode, int link)
1.1 dm 161: {
162: int status = -1;
163:
164: if (mode == -1)
165: return(0);
166:
1.14 millert 167: /*
168: * If we are dealing with a symlink, only try to change it if
169: * we have lchown, if we don't leave it alone.
170: */
171: #if defined(HAVE_LCHMOD)
172: if (link)
173: status = lchmod(file, mode);
174: #else
175: if (link)
176: return 0;
177: #endif
178:
1.1 dm 179: #if defined(HAVE_FCHMOD)
1.14 millert 180: if (fd != -1 && !link)
1.1 dm 181: status = fchmod(fd, mode);
182: #endif
183:
1.14 millert 184: if (status < 0 && !link)
1.1 dm 185: status = chmod(file, mode);
186:
187: if (status < 0) {
188: message(MT_NOTICE, "%s: chmod failed: %s", target, SYSERR);
189: return(-1);
190: }
191:
192: return(0);
193: }
194: /*
195: * Change owner, group and mode of file.
196: */
1.14 millert 197: static int
198: fchog(int fd, char *file, char *owner, char *group, int mode)
1.1 dm 199: {
1.14 millert 200: static struct group *gr = NULL;
1.1 dm 201: extern char *locuser;
1.10 mpech 202: int i;
1.14 millert 203: struct stat st;
1.1 dm 204: UID_T uid;
205: GID_T gid;
206: GID_T primegid = (GID_T)-2;
207:
208: uid = userid;
209: if (userid == 0) { /* running as root; take anything */
210: if (*owner == ':') {
211: uid = (UID_T) atoi(owner + 1);
1.14 millert 212: } else if (pw == NULL || strcmp(owner, pw->pw_name) != 0) {
1.1 dm 213: if ((pw = getpwnam(owner)) == NULL) {
214: if (mode != -1 && IS_ON(mode, S_ISUID)) {
215: message(MT_NOTICE,
216: "%s: unknown login name \"%s\", clearing setuid",
217: target, owner);
218: mode &= ~S_ISUID;
219: uid = 0;
220: } else
221: message(MT_NOTICE,
222: "%s: unknown login name \"%s\"",
223: target, owner);
1.14 millert 224: } else
225: uid = pw->pw_uid;
1.1 dm 226: } else {
1.14 millert 227: uid = pw->pw_uid;
228: primegid = pw->pw_gid;
1.1 dm 229: }
230: if (*group == ':') {
231: gid = (GID_T) atoi(group + 1);
232: goto ok;
233: }
234: } else { /* not root, setuid only if user==owner */
235: struct passwd *lupw;
236:
237: if (mode != -1) {
238: if (IS_ON(mode, S_ISUID) &&
239: strcmp(locuser, owner) != 0)
240: mode &= ~S_ISUID;
241: if (mode)
242: mode &= ~S_ISVTX; /* and strip sticky too */
243: }
244:
245: if ((lupw = getpwnam(locuser)) != NULL)
246: primegid = lupw->pw_gid;
247: }
248:
249: gid = (GID_T) -1;
1.14 millert 250: if (gr == NULL || strcmp(group, gr->gr_name) != 0) {
251: if ((*group == ':' &&
252: (getgrgid(gid = atoi(group + 1)) == NULL))
253: || ((gr = (struct group *)getgrnam(group)) == NULL)) {
1.1 dm 254: if (mode != -1 && IS_ON(mode, S_ISGID)) {
255: message(MT_NOTICE,
256: "%s: unknown group \"%s\", clearing setgid",
257: target, group);
258: mode &= ~S_ISGID;
259: } else
260: message(MT_NOTICE,
261: "%s: unknown group \"%s\"",
262: target, group);
1.14 millert 263: } else
264: gid = gr->gr_gid;
265: } else
266: gid = gr->gr_gid;
1.1 dm 267:
268: if (userid && gid >= 0 && gid != primegid) {
269: if (gr)
270: for (i = 0; gr->gr_mem[i] != NULL; i++)
1.14 millert 271: if (strcmp(locuser, gr->gr_mem[i]) == 0)
1.1 dm 272: goto ok;
273: if (mode != -1 && IS_ON(mode, S_ISGID)) {
274: message(MT_NOTICE,
275: "%s: user %s not in group %s, clearing setgid",
276: target, locuser, group);
277: mode &= ~S_ISGID;
278: }
279: gid = (GID_T) -1;
280: }
281: ok:
1.14 millert 282: if (stat(file, &st) == -1) {
283: error("%s: Stat failed %s", file, SYSERR);
284: return -1;
285: }
1.1 dm 286: /*
287: * Set uid and gid ownership. If that fails, strip setuid and
288: * setgid bits from mode. Once ownership is set, successful
289: * or otherwise, set the new file mode.
290: */
1.14 millert 291: if (setownership(file, fd, uid, gid, S_ISLNK(st.st_mode)) < 0) {
1.1 dm 292: if (mode != -1 && IS_ON(mode, S_ISUID)) {
293: message(MT_NOTICE,
294: "%s: chown failed, clearing setuid", target);
295: mode &= ~S_ISUID;
296: }
297: if (mode != -1 && IS_ON(mode, S_ISGID)) {
298: message(MT_NOTICE,
299: "%s: chown failed, clearing setgid", target);
300: mode &= ~S_ISGID;
301: }
302: }
1.14 millert 303: (void) setfilemode(file, fd, mode, S_ISLNK(st.st_mode));
1.1 dm 304:
305:
306: return(0);
307: }
308:
309: /*
310: * Remove a file or directory (recursively) and send back an acknowledge
311: * or an error message.
312: */
1.14 millert 313: static int
314: removefile(struct stat *statb, int silent)
1.1 dm 315: {
316: DIR *d;
317: static DIRENTRY *dp;
1.10 mpech 318: char *cp;
1.1 dm 319: struct stat stb;
320: char *optarget;
321: int len, failures = 0;
322:
323: switch (statb->st_mode & S_IFMT) {
324: case S_IFREG:
325: case S_IFLNK:
1.14 millert 326: case S_IFCHR:
327: case S_IFBLK:
328: #ifdef S_IFSOCK
329: case S_IFSOCK:
330: #endif
331: #ifdef S_IFIFO
332: case S_IFIFO:
333: #endif
1.1 dm 334: if (unlink(target) < 0) {
335: if (errno == ETXTBSY) {
1.14 millert 336: if (!silent)
337: message(MT_REMOTE|MT_NOTICE,
338: "%s: unlink failed: %s",
339: target, SYSERR);
1.1 dm 340: return(0);
341: } else {
342: error("%s: unlink failed: %s", target, SYSERR);
343: return(-1);
344: }
345: }
346: goto removed;
347:
348: case S_IFDIR:
349: break;
350:
351: default:
352: error("%s: not a plain file", target);
353: return(-1);
354: }
355:
356: errno = 0;
357: if ((d = opendir(target)) == NULL) {
358: error("%s: opendir failed: %s", target, SYSERR);
359: return(-1);
360: }
361:
362: optarget = ptarget;
363: len = ptarget - target;
1.14 millert 364: while ((dp = readdir(d)) != NULL) {
1.1 dm 365: if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
366: (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
367: dp->d_name[1] == '.'))
368: continue;
369:
370: if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
1.14 millert 371: if (!silent)
372: message(MT_REMOTE|MT_WARNING,
373: "%s/%s: Name too long",
374: target, dp->d_name);
1.1 dm 375: continue;
376: }
377: ptarget = optarget;
378: *ptarget++ = '/';
1.17 otto 379: cp = dp->d_name;
1.14 millert 380: while ((*ptarget++ = *cp++) != '\0')
381: continue;
1.1 dm 382: ptarget--;
383: if (lstat(target, &stb) < 0) {
1.14 millert 384: if (!silent)
385: message(MT_REMOTE|MT_WARNING,
386: "%s: lstat failed: %s",
387: target, SYSERR);
1.1 dm 388: continue;
389: }
1.14 millert 390: if (removefile(&stb, 0) < 0)
1.1 dm 391: ++failures;
392: }
393: (void) closedir(d);
394: ptarget = optarget;
395: *ptarget = CNULL;
396:
397: if (failures)
398: return(-1);
399:
400: if (rmdir(target) < 0) {
401: error("%s: rmdir failed: %s", target, SYSERR);
402: return(-1);
403: }
404: removed:
1.14 millert 405: #if NEWWAY
406: if (!silent)
407: message(MT_CHANGE|MT_REMOTE, "%s: removed", target);
408: #else
1.5 millert 409: /*
410: * We use MT_NOTICE instead of MT_CHANGE because this function is
411: * sometimes called by other functions that are suppose to return a
412: * single ack() back to the client (rdist). This is a kludge until
413: * the Rdist protocol is re-done. Sigh.
414: */
415: message(MT_NOTICE|MT_REMOTE, "%s: removed", target);
1.14 millert 416: #endif
1.1 dm 417: return(0);
418: }
419:
420: /*
421: * Check the current directory (initialized by the 'T' command to server())
422: * for extraneous files and remove them.
423: */
1.14 millert 424: static void
425: doclean(char *cp)
1.1 dm 426: {
427: DIR *d;
1.10 mpech 428: DIRENTRY *dp;
1.1 dm 429: struct stat stb;
430: char *optarget, *ep;
431: int len;
432: opt_t opts;
1.14 millert 433: char targ[MAXPATHLEN*4];
1.1 dm 434:
435: opts = strtol(cp, &ep, 8);
436: if (*ep != CNULL) {
437: error("clean: options not delimited");
438: return;
439: }
440: if ((d = opendir(target)) == NULL) {
441: error("%s: opendir failed: %s", target, SYSERR);
442: return;
443: }
444: ack();
445:
446: optarget = ptarget;
447: len = ptarget - target;
1.14 millert 448: while ((dp = readdir(d)) != NULL) {
1.1 dm 449: if ((D_NAMLEN(dp) == 1 && dp->d_name[0] == '.') ||
450: (D_NAMLEN(dp) == 2 && dp->d_name[0] == '.' &&
451: dp->d_name[1] == '.'))
452: continue;
453:
454: if (len + 1 + (int)strlen(dp->d_name) >= MAXPATHLEN - 1) {
455: message(MT_REMOTE|MT_WARNING, "%s/%s: Name too long",
456: target, dp->d_name);
457: continue;
458: }
459: ptarget = optarget;
460: *ptarget++ = '/';
1.17 otto 461: cp = dp->d_name;
1.14 millert 462: while ((*ptarget++ = *cp++) != '\0')
463: continue;
1.1 dm 464: ptarget--;
465: if (lstat(target, &stb) < 0) {
466: message(MT_REMOTE|MT_WARNING, "%s: lstat failed: %s",
467: target, SYSERR);
468: continue;
469: }
470:
1.14 millert 471: ENCODE(targ, dp->d_name);
472: (void) sendcmd(CC_QUERY, "%s", targ);
1.1 dm 473: (void) remline(cp = buf, sizeof(buf), TRUE);
474:
475: if (*cp != CC_YES)
476: continue;
477:
478: if (IS_ON(opts, DO_VERIFY))
479: message(MT_REMOTE|MT_INFO, "%s: need to remove",
480: target);
481: else
1.14 millert 482: (void) removefile(&stb, 0);
1.1 dm 483: }
484: (void) closedir(d);
485:
486: ptarget = optarget;
487: *ptarget = CNULL;
488: }
489:
490: /*
491: * Frontend to doclean().
492: */
1.14 millert 493: static void
494: clean(char *cp)
1.1 dm 495: {
496: doclean(cp);
497: (void) sendcmd(CC_END, NULL);
498: (void) response();
499: }
500:
501: /*
502: * Execute a shell command to handle special cases.
503: * We can't really set an alarm timeout here since we
504: * have no idea how long the command should take.
505: */
1.14 millert 506: static void
507: dospecial(char *xcmd)
1.1 dm 508: {
1.14 millert 509: char cmd[BUFSIZ];
510: if (DECODE(cmd, xcmd) == -1) {
511: error("dospecial: Cannot decode command.");
512: return;
513: }
1.1 dm 514: runcommand(cmd);
515: }
516:
517: /*
518: * Do a special cmd command. This differs from normal special
519: * commands in that it's done after an entire command has been updated.
520: * The list of updated target files is sent one at a time with RC_FILE
521: * commands. Each one is added to an environment variable defined by
522: * E_FILES. When an RC_COMMAND is finally received, the E_FILES variable
523: * is stuffed into our environment and a normal dospecial() command is run.
524: */
1.14 millert 525: static void
526: docmdspecial(void)
1.1 dm 527: {
1.10 mpech 528: char *cp;
1.1 dm 529: char *cmd, *env = NULL;
530: int n;
1.13 millert 531: size_t len;
1.1 dm 532:
533: /* We're ready */
534: ack();
535:
536: for ( ; ; ) {
537: n = remline(cp = buf, sizeof(buf), FALSE);
538: if (n <= 0) {
539: error("cmdspecial: premature end of input.");
540: return;
541: }
542:
543: switch (*cp++) {
544: case RC_FILE:
545: if (env == NULL) {
546: len = (2 * sizeof(E_FILES)) + strlen(cp) + 10;
547: env = (char *) xmalloc(len);
1.11 deraadt 548: (void) snprintf(env, len, "export %s;%s=%s",
1.1 dm 549: E_FILES, E_FILES, cp);
550: } else {
1.13 millert 551: len = strlen(env) + 1 + strlen(cp) + 1;
552: env = (char *) xrealloc(env, len);
553: (void) strlcat(env, ":", len);
554: (void) strlcat(env, cp, len);
1.1 dm 555: }
556: ack();
557: break;
558:
559: case RC_COMMAND:
560: if (env) {
1.13 millert 561: len = strlen(env) + 1 + strlen(cp) + 1;
562: env = (char *) xrealloc(env, len);
563: (void) strlcat(env, ";", len);
564: (void) strlcat(env, cp, len);
1.1 dm 565: cmd = env;
566: } else
567: cmd = cp;
568:
569: dospecial(cmd);
570: if (env)
571: (void) free(env);
572: return;
573:
574: default:
575: error("Unknown cmdspecial command '%s'.", cp);
576: return;
577: }
578: }
579: }
580:
581: /*
582: * Query. Check to see if file exists. Return one of the following:
583: *
584: #ifdef NFS_CHECK
585: * QC_ONNFS - resides on a NFS
586: #endif NFS_CHECK
587: #ifdef RO_CHECK
588: * QC_ONRO - resides on a Read-Only filesystem
589: #endif RO_CHECK
590: * QC_NO - doesn't exist
591: * QC_YESsize mtime - exists and its a regular file (size & mtime of file)
592: * QC_YES - exists and its a directory or symbolic link
593: * QC_ERRMSGmessage - error message
594: */
1.14 millert 595: static void
596: query(char *xname)
1.1 dm 597: {
598: static struct stat stb;
599: int s = -1, stbvalid = 0;
1.14 millert 600: char name[MAXPATHLEN];
601:
602: if (DECODE(name, xname) == -1) {
603: error("query: Cannot decode filename");
604: return;
605: }
1.1 dm 606:
607: if (catname && cattarget(name) < 0)
608: return;
609:
610: #if defined(NFS_CHECK)
611: if (IS_ON(options, DO_CHKNFS)) {
612: s = is_nfs_mounted(target, &stb, &stbvalid);
613: if (s > 0)
614: (void) sendcmd(QC_ONNFS, NULL);
615:
1.9 mpech 616: /* Either the above check was true or an error occurred */
1.1 dm 617: /* and is_nfs_mounted sent the error message */
618: if (s != 0) {
619: *ptarget = CNULL;
620: return;
621: }
622: }
623: #endif /* NFS_CHECK */
624:
625: #if defined(RO_CHECK)
626: if (IS_ON(options, DO_CHKREADONLY)) {
627: s = is_ro_mounted(target, &stb, &stbvalid);
628: if (s > 0)
629: (void) sendcmd(QC_ONRO, NULL);
630:
1.9 mpech 631: /* Either the above check was true or an error occurred */
1.1 dm 632: /* and is_ro_mounted sent the error message */
633: if (s != 0) {
634: *ptarget = CNULL;
635: return;
636: }
637: }
638: #endif /* RO_CHECK */
639:
640: if (IS_ON(options, DO_CHKSYM)) {
641: if (is_symlinked(target, &stb, &stbvalid) > 0) {
642: (void) sendcmd(QC_SYM, NULL);
643: return;
644: }
645: }
646:
647: /*
648: * If stbvalid is false, "stb" is not valid because:
649: * a) RO_CHECK and NFS_CHECK were not defined
650: * b) The stat by is_*_mounted() either failed or
651: * does not match "target".
652: */
653: if (!stbvalid && lstat(target, &stb) < 0) {
654: if (errno == ENOENT)
655: (void) sendcmd(QC_NO, NULL);
656: else
657: error("%s: lstat failed: %s", target, SYSERR);
658: *ptarget = CNULL;
659: return;
660: }
661:
662: switch (stb.st_mode & S_IFMT) {
663: case S_IFLNK:
664: case S_IFDIR:
665: case S_IFREG:
1.14 millert 666: #ifdef notyet
667: case S_IFCHR:
668: case S_IFBLK:
669: #ifdef S_IFSOCK
670: case S_IFSOCK:
671: #endif
672: #ifdef S_IFIFO
673: case S_IFIFO:
674: #endif
675: #endif
1.1 dm 676: (void) sendcmd(QC_YES, "%ld %ld %o %s %s",
1.14 millert 677: (long) stb.st_size, stb.st_mtime,
1.1 dm 678: stb.st_mode & 07777,
679: getusername(stb.st_uid, target, options),
680: getgroupname(stb.st_gid, target, options));
681: break;
682:
683: default:
684: error("%s: not a file or directory", target);
685: break;
686: }
687: *ptarget = CNULL;
688: }
689:
690: /*
691: * Check to see if parent directory exists and create one if not.
692: */
1.14 millert 693: static int
694: chkparent(char *name, opt_t opts)
1.1 dm 695: {
1.10 mpech 696: char *cp;
1.1 dm 697: struct stat stb;
698: int r = -1;
699:
700: debugmsg(DM_CALL, "chkparent(%s, %o) start\n", name, opts);
701:
702: cp = strrchr(name, '/');
703: if (cp == NULL || cp == name)
704: return(0);
705:
706: *cp = CNULL;
707:
708: if (lstat(name, &stb) < 0) {
709: if (errno == ENOENT && chkparent(name, opts) >= 0) {
710: if (mkdir(name, 0777 & ~oumask) == 0) {
711: message(MT_NOTICE, "%s: mkdir", name);
712: r = 0;
713: } else
714: debugmsg(DM_MISC,
715: "chkparent(%s, %o) mkdir fail: %s\n",
716: name, opts, SYSERR);
717: }
718: } else /* It exists */
719: r = 0;
720:
721: /* Put back what we took away */
722: *cp = '/';
723:
724: return(r);
725: }
726:
727: /*
728: * Save a copy of 'file' by renaming it.
729: */
1.14 millert 730: static char *
731: savetarget(char *file, opt_t opts)
1.1 dm 732: {
733: static char savefile[MAXPATHLEN];
734:
735: if (strlen(file) + sizeof(SAVE_SUFFIX) + 1 > MAXPATHLEN) {
736: error("%s: Cannot save: Save name too long", file);
1.7 millert 737: return(NULL);
1.1 dm 738: }
739:
1.14 millert 740: if (IS_ON(opts, DO_HISTORY)) {
741: int i;
742: struct stat st;
743: /*
744: * There is a race here, but the worst that can happen
745: * is to lose a version of the file
746: */
747: for (i = 1; i < 1000; i++) {
748: (void) snprintf(savefile, sizeof(savefile),
749: "%s;%.3d", file, i);
750: if (stat(savefile, &st) == -1 && errno == ENOENT)
751: break;
1.1 dm 752:
1.14 millert 753: }
754: if (i == 1000) {
755: message(MT_NOTICE,
756: "%s: More than 1000 versions for %s; reusing 1\n",
757: savefile, SYSERR);
758: i = 1;
759: (void) snprintf(savefile, sizeof(savefile),
760: "%s;%.3d", file, i);
761: }
762: }
763: else {
764: (void) snprintf(savefile, sizeof(savefile), "%s%s",
765: file, SAVE_SUFFIX);
766:
767: if (unlink(savefile) != 0 && errno != ENOENT) {
768: message(MT_NOTICE, "%s: remove failed: %s",
769: savefile, SYSERR);
770: return(NULL);
771: }
1.1 dm 772: }
773:
774: if (rename(file, savefile) != 0 && errno != ENOENT) {
775: error("%s -> %s: rename failed: %s",
776: file, savefile, SYSERR);
1.7 millert 777: return(NULL);
1.1 dm 778: }
779:
780: return(savefile);
781: }
782:
783: /*
784: * Receive a file
785: */
1.14 millert 786: static void
1.19 millert 787: recvfile(char *new, opt_t opts, int mode, char *owner, char *group,
788: time_t mtime, time_t atime, off_t size)
1.1 dm 789: {
1.19 millert 790: int f, wrerr, olderrno;
1.1 dm 791: off_t i;
1.10 mpech 792: char *cp;
1.1 dm 793: char *savefile = NULL;
1.5 millert 794: static struct stat statbuff;
1.1 dm 795:
796: /*
1.19 millert 797: * Create temporary file
798: */
799: if ((f = mkstemp(new)) < 0) {
800: if (errno != ENOENT || chkparent(new, opts) < 0 ||
801: (f = mkstemp(new)) < 0) {
802: error("%s: create failed: %s", new, SYSERR);
803: return;
804: }
805: }
806:
807: /*
1.1 dm 808: * Receive the file itself
809: */
810: ack();
811: wrerr = 0;
812: olderrno = 0;
813: for (i = 0; i < size; i += BUFSIZ) {
814: int amt = BUFSIZ;
815:
816: cp = buf;
817: if (i + amt > size)
818: amt = size - i;
819: do {
820: int j;
821:
822: j = readrem(cp, amt);
823: if (j <= 0) {
824: (void) close(f);
825: (void) unlink(new);
826: fatalerr(
1.9 mpech 827: "Read error occurred while receiving file.");
1.1 dm 828: finish();
829: }
830: amt -= j;
831: cp += j;
832: } while (amt > 0);
833: amt = BUFSIZ;
834: if (i + amt > size)
835: amt = size - i;
1.14 millert 836: if (wrerr == 0 && xwrite(f, buf, amt) != amt) {
1.1 dm 837: olderrno = errno;
838: wrerr++;
839: }
840: }
841:
842: if (response() < 0) {
843: (void) close(f);
844: (void) unlink(new);
845: return;
846: }
1.2 dm 847:
1.1 dm 848: if (wrerr) {
849: error("%s: Write error: %s", new, strerror(olderrno));
850: (void) close(f);
851: (void) unlink(new);
852: return;
853: }
854:
855: /*
856: * Do file comparison if enabled
857: */
858: if (IS_ON(opts, DO_COMPARE)) {
859: FILE *f1, *f2;
860: int c;
861:
862: errno = 0; /* fopen is not a syscall */
863: if ((f1 = fopen(target, "r")) == NULL) {
864: error("%s: open for read failed: %s", target, SYSERR);
865: (void) close(f);
866: (void) unlink(new);
867: return;
868: }
869: errno = 0;
870: if ((f2 = fopen(new, "r")) == NULL) {
871: error("%s: open for read failed: %s", new, SYSERR);
1.14 millert 872: (void) fclose(f1);
1.1 dm 873: (void) close(f);
874: (void) unlink(new);
875: return;
876: }
877: while ((c = getc(f1)) == getc(f2))
878: if (c == EOF) {
879: debugmsg(DM_MISC,
880: "Files are the same '%s' '%s'.",
881: target, new);
882: (void) fclose(f1);
883: (void) fclose(f2);
884: (void) close(f);
885: (void) unlink(new);
886: /*
887: * This isn't an error per-se, but we
888: * need to indicate to the master that
889: * the file was not updated.
890: */
891: error("");
892: return;
893: }
894: debugmsg(DM_MISC, "Files are different '%s' '%s'.",
895: target, new);
896: (void) fclose(f1);
897: (void) fclose(f2);
898: if (IS_ON(opts, DO_VERIFY)) {
899: message(MT_REMOTE|MT_INFO, "%s: need to update",
900: target);
901: (void) close(f);
902: (void) unlink(new);
903: return;
904: }
905: }
906:
907: /*
908: * Set owner, group, and file mode
909: */
910: if (fchog(f, new, owner, group, mode) < 0) {
911: (void) close(f);
912: (void) unlink(new);
913: return;
914: }
915: (void) close(f);
916:
917: /*
918: * Perform utimes() after file is closed to make
919: * certain OS's, such as NeXT 2.1, happy.
920: */
921: if (setfiletime(new, time((time_t *) 0), mtime) < 0)
922: message(MT_NOTICE, "%s: utimes failed: %s", new, SYSERR);
923:
924: /*
925: * Try to save target file from being over-written
926: */
927: if (IS_ON(opts, DO_SAVETARGETS))
1.14 millert 928: if ((savefile = savetarget(target, opts)) == NULL) {
1.1 dm 929: (void) unlink(new);
930: return;
931: }
932:
933: /*
1.5 millert 934: * If the target is a directory, we need to remove it first
935: * before we can rename the new file.
936: */
937: if ((stat(target, &statbuff) == 0) && S_ISDIR(statbuff.st_mode)) {
938: char *saveptr = ptarget;
939:
940: ptarget = &target[strlen(target)];
1.14 millert 941: removefile(&statbuff, 0);
1.5 millert 942: ptarget = saveptr;
943: }
944:
945: /*
1.1 dm 946: * Install new (temporary) file as the actual target
947: */
948: if (rename(new, target) < 0) {
1.14 millert 949: static char fmt[] = "%s -> %s: rename failed: %s";
950: struct stat stb;
1.1 dm 951: /*
952: * If the rename failed due to "Text file busy", then
953: * try to rename the target file and retry the rename.
954: */
1.14 millert 955: switch (errno) {
956: case ETXTBSY:
1.1 dm 957: /* Save the target */
1.14 millert 958: if ((savefile = savetarget(target, opts)) != NULL) {
1.1 dm 959: /* Retry installing new file as target */
960: if (rename(new, target) < 0) {
1.14 millert 961: error(fmt, new, target, SYSERR);
1.1 dm 962: /* Try to put back save file */
963: if (rename(savefile, target) < 0)
1.14 millert 964: error(fmt,
965: savefile, target, SYSERR);
966: (void) unlink(new);
1.1 dm 967: } else
968: message(MT_NOTICE, "%s: renamed to %s",
969: target, savefile);
1.14 millert 970: /*
971: * XXX: We should remove the savefile here.
972: * But we are nice to nfs clients and
973: * we keep it.
974: */
975: }
976: break;
977: case EISDIR:
978: /*
979: * See if target is a directory and remove it if it is
980: */
981: if (lstat(target, &stb) == 0) {
982: if (S_ISDIR(stb.st_mode)) {
983: char *optarget = ptarget;
984: for (ptarget = target; *ptarget;
985: ptarget++);
986: /* If we failed to remove, we'll catch
987: it later */
988: (void) removefile(&stb, 1);
989: ptarget = optarget;
990: }
1.1 dm 991: }
1.14 millert 992: if (rename(new, target) >= 0)
993: break;
994: /*FALLTHROUGH*/
995:
996: default:
997: error(fmt, new, target, SYSERR);
1.1 dm 998: (void) unlink(new);
1.14 millert 999: break;
1.1 dm 1000: }
1001: }
1002:
1003: if (IS_ON(opts, DO_COMPARE))
1004: message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1005: else
1006: ack();
1007: }
1008:
1009: /*
1010: * Receive a directory
1011: */
1.14 millert 1012: static void
1013: recvdir(opt_t opts, int mode, char *owner, char *group)
1.1 dm 1014: {
1015: static char lowner[100], lgroup[100];
1.10 mpech 1016: char *cp;
1.1 dm 1017: struct stat stb;
1018: int s;
1019:
1020: s = lstat(target, &stb);
1021: if (s == 0) {
1022: /*
1023: * If target is not a directory, remove it
1024: */
1025: if (!S_ISDIR(stb.st_mode)) {
1026: if (IS_ON(opts, DO_VERIFY))
1027: message(MT_NOTICE, "%s: need to remove",
1028: target);
1029: else {
1030: if (unlink(target) < 0) {
1031: error("%s: remove failed: %s",
1032: target, SYSERR);
1033: return;
1034: }
1035: }
1036: s = -1;
1037: errno = ENOENT;
1038: } else {
1039: if (!IS_ON(opts, DO_NOCHKMODE) &&
1040: (stb.st_mode & 07777) != mode) {
1041: if (IS_ON(opts, DO_VERIFY))
1042: message(MT_NOTICE,
1043: "%s: need to chmod to %o",
1044: target, mode);
1045: else {
1046: if (chmod(target, mode) != 0)
1047: message(MT_NOTICE,
1048: "%s: chmod from %o to %o failed: %s",
1049: target,
1050: stb.st_mode & 07777,
1051: mode,
1052: SYSERR);
1053: else
1054: message(MT_NOTICE,
1055: "%s: chmod from %o to %o",
1056: target,
1057: stb.st_mode & 07777,
1058: mode);
1059: }
1060: }
1061:
1062: /*
1063: * Check ownership and set if necessary
1064: */
1065: lowner[0] = CNULL;
1066: lgroup[0] = CNULL;
1067:
1068: if (!IS_ON(opts, DO_NOCHKOWNER) && owner) {
1069: int o;
1070:
1071: o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER :
1072: opts;
1.14 millert 1073: if ((cp = getusername(stb.st_uid, target, o))
1074: != NULL)
1.1 dm 1075: if (strcmp(owner, cp))
1.11 deraadt 1076: (void) strlcpy(lowner, cp,
1.14 millert 1077: sizeof(lowner));
1.1 dm 1078: }
1079: if (!IS_ON(opts, DO_NOCHKGROUP) && group) {
1080: int o;
1081:
1082: o = (group[0] == ':') ? opts & DO_NUMCHKGROUP :
1083: opts;
1.14 millert 1084: if ((cp = getgroupname(stb.st_gid, target, o))
1085: != NULL)
1.1 dm 1086: if (strcmp(group, cp))
1.11 deraadt 1087: (void) strlcpy(lgroup, cp,
1.14 millert 1088: sizeof(lgroup));
1.1 dm 1089: }
1090:
1091: /*
1092: * Need to set owner and/or group
1093: */
1094: #define PRN(n) ((n[0] == ':') ? n+1 : n)
1095: if (lowner[0] != CNULL || lgroup[0] != CNULL) {
1096: if (lowner[0] == CNULL &&
1097: (cp = getusername(stb.st_uid,
1098: target, opts)))
1.11 deraadt 1099: (void) strlcpy(lowner, cp,
1.14 millert 1100: sizeof(lowner));
1.1 dm 1101: if (lgroup[0] == CNULL &&
1102: (cp = getgroupname(stb.st_gid,
1103: target, opts)))
1.11 deraadt 1104: (void) strlcpy(lgroup, cp,
1.14 millert 1105: sizeof(lgroup));
1.1 dm 1106:
1107: if (IS_ON(opts, DO_VERIFY))
1108: message(MT_NOTICE,
1.12 millert 1109: "%s: need to chown from %s:%s to %s:%s",
1.1 dm 1110: target,
1111: PRN(lowner), PRN(lgroup),
1112: PRN(owner), PRN(group));
1113: else {
1114: if (fchog(-1, target, owner,
1115: group, -1) == 0)
1116: message(MT_NOTICE,
1.12 millert 1117: "%s: chown from %s:%s to %s:%s",
1.1 dm 1118: target,
1119: PRN(lowner),
1120: PRN(lgroup),
1121: PRN(owner),
1122: PRN(group));
1123: }
1124: }
1125: #undef PRN
1126: ack();
1127: return;
1128: }
1129: }
1130:
1131: if (IS_ON(opts, DO_VERIFY)) {
1132: ack();
1133: return;
1134: }
1135:
1136: /*
1137: * Create the directory
1138: */
1139: if (s < 0) {
1140: if (errno == ENOENT) {
1141: if (mkdir(target, mode) == 0 ||
1.7 millert 1142: (chkparent(target, opts) == 0 &&
1143: mkdir(target, mode) == 0)) {
1.1 dm 1144: message(MT_NOTICE, "%s: mkdir", target);
1145: (void) fchog(-1, target, owner, group, mode);
1146: ack();
1147: } else {
1148: error("%s: mkdir failed: %s", target, SYSERR);
1149: ptarget = sptarget[--catname];
1150: *ptarget = CNULL;
1151: }
1152: return;
1153: }
1154: }
1155: error("%s: lstat failed: %s", target, SYSERR);
1156: ptarget = sptarget[--catname];
1157: *ptarget = CNULL;
1158: }
1159:
1160: /*
1161: * Receive a link
1162: */
1.14 millert 1163: static void
1164: recvlink(char *new, opt_t opts, int mode, off_t size)
1.1 dm 1165: {
1.20 ! deraadt 1166: char tbuf[MAXPATHLEN], dbuf[BUFSIZ];
1.1 dm 1167: struct stat stb;
1168: char *optarget;
1.14 millert 1169: int uptodate;
1.1 dm 1170: off_t i;
1171:
1172: /*
1173: * Read basic link info
1174: */
1175: ack();
1176: (void) remline(buf, sizeof(buf), TRUE);
1177:
1178: if (response() < 0) {
1179: err();
1180: return;
1181: }
1182:
1.20 ! deraadt 1183: if (DECODE(dbuf, buf) == -1) {
! 1184: error("recvlink: cannot decode symlink target");
! 1185: return;
! 1186: }
! 1187:
1.14 millert 1188: uptodate = 0;
1.16 mpech 1189: if ((i = readlink(target, tbuf, sizeof(tbuf)-1)) != -1) {
1.14 millert 1190: tbuf[i] = '\0';
1.20 ! deraadt 1191: if (i == size && strncmp(dbuf, tbuf, (int) size) == 0)
1.14 millert 1192: uptodate = 1;
1193: }
1194: mode &= 0777;
1195:
1196: if (IS_ON(opts, DO_VERIFY) || uptodate) {
1197: if (uptodate)
1198: message(MT_REMOTE|MT_INFO, "");
1199: else
1200: message(MT_REMOTE|MT_INFO, "%s: need to update",
1201: target);
1202: if (IS_ON(opts, DO_COMPARE))
1203: return;
1204: (void) sendcmd(C_END, NULL);
1205: (void) response();
1206: return;
1207: }
1208:
1.1 dm 1209: /*
1210: * Make new symlink using a temporary name
1211: */
1.20 ! deraadt 1212: if (mktemp(new) == NULL || symlink(dbuf, new) < 0) {
1.1 dm 1213: if (errno != ENOENT || chkparent(new, opts) < 0 ||
1.20 ! deraadt 1214: mktemp(new) == NULL || symlink(dbuf, new) < 0) {
! 1215: error("%s -> %s: symlink failed: %s", new, dbuf,
! 1216: SYSERR);
1.1 dm 1217: return;
1218: }
1219: }
1220:
1221: /*
1222: * See if target is a directory and remove it if it is
1223: */
1224: if (lstat(target, &stb) == 0) {
1225: if (S_ISDIR(stb.st_mode)) {
1226: optarget = ptarget;
1227: for (ptarget = target; *ptarget; ptarget++);
1.14 millert 1228: if (removefile(&stb, 0) < 0) {
1.1 dm 1229: ptarget = optarget;
1230: (void) unlink(new);
1231: (void) sendcmd(C_END, NULL);
1232: (void) response();
1233: return;
1234: }
1235: ptarget = optarget;
1236: }
1237: }
1238:
1239: /*
1240: * Install link as the target
1241: */
1242: if (rename(new, target) < 0) {
1243: error("%s -> %s: symlink rename failed: %s",
1244: new, target, SYSERR);
1245: (void) unlink(new);
1246: (void) sendcmd(C_END, NULL);
1247: (void) response();
1248: return;
1249: }
1250:
1.14 millert 1251: message(MT_REMOTE|MT_CHANGE, "%s: updated", target);
1.1 dm 1252:
1253: /*
1254: * Indicate end of receive operation
1255: */
1256: (void) sendcmd(C_END, NULL);
1257: (void) response();
1258: }
1259:
1260: /*
1261: * Creat a hard link to existing file.
1262: */
1.14 millert 1263: static void
1264: hardlink(char *cmd)
1.1 dm 1265: {
1266: struct stat stb;
1267: int exists = 0;
1.14 millert 1268: char *xoldname, *xnewname;
1.1 dm 1269: char *cp = cmd;
1270: static char expbuf[BUFSIZ];
1.14 millert 1271: char oldname[BUFSIZ], newname[BUFSIZ];
1.1 dm 1272:
1273: /* Skip over opts */
1274: (void) strtol(cp, &cp, 8);
1275: if (*cp++ != ' ') {
1276: error("hardlink: options not delimited");
1277: return;
1278: }
1279:
1.14 millert 1280: xoldname = strtok(cp, " ");
1281: if (xoldname == NULL) {
1.1 dm 1282: error("hardlink: oldname name not delimited");
1283: return;
1284: }
1285:
1.14 millert 1286: if (DECODE(oldname, xoldname) == -1) {
1287: error("hardlink: Cannot decode oldname");
1288: return;
1289: }
1290:
1291: xnewname = strtok(NULL, " ");
1292: if (xnewname == NULL) {
1.1 dm 1293: error("hardlink: new name not specified");
1294: return;
1295: }
1296:
1.14 millert 1297: if (DECODE(newname, xnewname) == -1) {
1298: error("hardlink: Cannot decode newname");
1299: return;
1300: }
1301:
1.13 millert 1302: if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL) {
1.1 dm 1303: error("hardlink: tilde expansion failed");
1304: return;
1305: }
1306:
1307: if (catname && cattarget(newname) < 0) {
1308: error("Cannot set newname target.");
1309: return;
1310: }
1311:
1312: if (lstat(target, &stb) == 0) {
1313: int mode = stb.st_mode & S_IFMT;
1314:
1315: if (mode != S_IFREG && mode != S_IFLNK) {
1316: error("%s: not a regular file", target);
1317: return;
1318: }
1319: exists = 1;
1320: }
1321:
1322: if (chkparent(target, options) < 0 ) {
1323: error("%s: no parent: %s ", target, SYSERR);
1324: return;
1325: }
1326: if (exists && (unlink(target) < 0)) {
1327: error("%s: unlink failed: %s", target, SYSERR);
1328: return;
1329: }
1.14 millert 1330: if (link(expbuf, target) < 0) {
1.1 dm 1331: error("%s: cannot link to %s: %s", target, oldname, SYSERR);
1332: return;
1333: }
1334: ack();
1335: }
1336:
1337: /*
1338: * Set configuration information.
1339: *
1340: * A key letter is followed immediately by the value
1341: * to set. The keys are:
1342: * SC_FREESPACE - Set minimium free space of filesystem
1343: * SC_FREEFILES - Set minimium free number of files of filesystem
1344: */
1.14 millert 1345: static void
1346: setconfig(char *cmd)
1.1 dm 1347: {
1.10 mpech 1348: char *cp = cmd;
1.1 dm 1349: char *estr;
1350:
1351: switch (*cp++) {
1352: case SC_HOSTNAME: /* Set hostname */
1353: /*
1354: * Only use info if we don't know who this is.
1355: */
1356: if (!fromhost) {
1.8 millert 1357: fromhost = xstrdup(cp);
1.14 millert 1358: message(MT_SYSLOG, "startup for %s", fromhost);
1.7 millert 1359: #if defined(SETARGS) || defined(HAVE_SETPROCTITLE)
1.1 dm 1360: setproctitle("serving %s", cp);
1.7 millert 1361: #endif /* SETARGS || HAVE_SETPROCTITLE */
1.1 dm 1362: }
1363: break;
1364:
1365: case SC_FREESPACE: /* Minimium free space */
1.14 millert 1366: if (!isdigit((unsigned char)*cp)) {
1.1 dm 1367: fatalerr("Expected digit, got '%s'.", cp);
1368: return;
1369: }
1370: min_freespace = (unsigned long) atoi(cp);
1371: break;
1372:
1373: case SC_FREEFILES: /* Minimium free files */
1.14 millert 1374: if (!isdigit((unsigned char)*cp)) {
1.1 dm 1375: fatalerr("Expected digit, got '%s'.", cp);
1376: return;
1377: }
1378: min_freefiles = (unsigned long) atoi(cp);
1379: break;
1380:
1381: case SC_LOGGING: /* Logging options */
1.14 millert 1382: if ((estr = msgparseopts(cp, TRUE)) != NULL) {
1.1 dm 1383: fatalerr("Bad message option string (%s): %s",
1384: cp, estr);
1385: return;
1386: }
1387: break;
1388:
1.14 millert 1389: case SC_DEFOWNER:
1390: (void) strlcpy(defowner, cp, sizeof(defowner));
1391: break;
1392:
1393: case SC_DEFGROUP:
1394: (void) strlcpy(defgroup, cp, sizeof(defgroup));
1395: break;
1396:
1.1 dm 1397: default:
1398: message(MT_NOTICE, "Unknown config command \"%s\".", cp-1);
1399: return;
1400: }
1401: }
1402:
1403: /*
1404: * Receive something
1405: */
1.14 millert 1406: static void
1407: recvit(char *cmd, int type)
1.1 dm 1408: {
1.19 millert 1409: int mode;
1.1 dm 1410: opt_t opts;
1411: off_t size;
1412: time_t mtime, atime;
1413: char *owner, *group, *file;
1414: char new[MAXPATHLEN];
1.14 millert 1415: char fileb[MAXPATHLEN];
1.5 millert 1416: long freespace = -1, freefiles = -1;
1.1 dm 1417: char *cp = cmd;
1418:
1419: /*
1420: * Get rdist option flags
1421: */
1422: opts = strtol(cp, &cp, 8);
1423: if (*cp++ != ' ') {
1424: error("recvit: options not delimited");
1425: return;
1426: }
1427:
1428: /*
1429: * Get file mode
1430: */
1431: mode = strtol(cp, &cp, 8);
1432: if (*cp++ != ' ') {
1433: error("recvit: mode not delimited");
1434: return;
1435: }
1436:
1437: /*
1438: * Get file size
1439: */
1440: size = strtol(cp, &cp, 10);
1441: if (*cp++ != ' ') {
1442: error("recvit: size not delimited");
1443: return;
1444: }
1445:
1446: /*
1447: * Get modification time
1448: */
1449: mtime = strtol(cp, &cp, 10);
1450: if (*cp++ != ' ') {
1451: error("recvit: mtime not delimited");
1452: return;
1453: }
1454:
1455: /*
1456: * Get access time
1457: */
1458: atime = strtol(cp, &cp, 10);
1459: if (*cp++ != ' ') {
1460: error("recvit: atime not delimited");
1461: return;
1462: }
1463:
1464: /*
1465: * Get file owner name
1466: */
1467: owner = strtok(cp, " ");
1468: if (owner == NULL) {
1469: error("recvit: owner name not delimited");
1470: return;
1471: }
1472:
1473: /*
1474: * Get file group name
1475: */
1.7 millert 1476: group = strtok(NULL, " ");
1.1 dm 1477: if (group == NULL) {
1478: error("recvit: group name not delimited");
1479: return;
1480: }
1481:
1482: /*
1.14 millert 1483: * Get file name. Can't use strtok() since there could
1.1 dm 1484: * be white space in the file name.
1485: */
1.14 millert 1486: if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1487: error("recvit: Cannot decode file name");
1488: return;
1489: }
1490:
1491: if (fileb[0] == '\0') {
1.1 dm 1492: error("recvit: no file name");
1493: return;
1494: }
1.14 millert 1495: file = fileb;
1.1 dm 1496:
1497: debugmsg(DM_MISC,
1498: "recvit: opts = %04o mode = %04o size = %d mtime = %d",
1499: opts, mode, size, mtime);
1500: debugmsg(DM_MISC,
1501: "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
1502: owner, group, file, catname, (type == S_IFDIR) ? 1 : 0);
1503:
1504: if (type == S_IFDIR) {
1.14 millert 1505: if ((size_t) catname >= sizeof(sptarget)) {
1.1 dm 1506: error("%s: too many directory levels", target);
1507: return;
1508: }
1509: sptarget[catname] = ptarget;
1510: if (catname++) {
1511: *ptarget++ = '/';
1.14 millert 1512: while ((*ptarget++ = *file++) != '\0')
1513: continue;
1.1 dm 1514: ptarget--;
1515: }
1516: } else {
1517: /*
1518: * Create name of temporary file
1519: */
1520: if (catname && cattarget(file) < 0) {
1521: error("Cannot set file name.");
1522: return;
1523: }
1524: file = strrchr(target, '/');
1525: if (file == NULL)
1.14 millert 1526: (void) strlcpy(new, tempname, sizeof(new));
1.1 dm 1527: else if (file == target)
1.14 millert 1528: (void) snprintf(new, sizeof(new), "/%s", tempname);
1.1 dm 1529: else {
1530: *file = CNULL;
1.14 millert 1531: (void) snprintf(new, sizeof(new), "%s/%s", target,
1532: tempname);
1.1 dm 1533: *file = '/';
1534: }
1535: }
1536:
1537: /*
1538: * Check to see if there is enough free space and inodes
1539: * to install this file.
1540: */
1541: if (min_freespace || min_freefiles) {
1542: /* Convert file size to kilobytes */
1.5 millert 1543: long fsize = (long) (size / 1024);
1.1 dm 1544:
1545: if (getfilesysinfo(target, &freespace, &freefiles) != 0)
1546: return;
1547:
1548: /*
1549: * filesystem values < 0 indicate unsupported or unavailable
1550: * information.
1551: */
1552: if (min_freespace && (freespace >= 0) &&
1553: (freespace - fsize < min_freespace)) {
1554: error(
1555: "%s: Not enough free space on filesystem: min %d free %d",
1556: target, min_freespace, freespace);
1557: return;
1558: }
1559: if (min_freefiles && (freefiles >= 0) &&
1560: (freefiles - 1 < min_freefiles)) {
1561: error(
1562: "%s: Not enough free files on filesystem: min %d free %d",
1563: target, min_freefiles, freefiles);
1564: return;
1565: }
1566: }
1567:
1568: /*
1569: * Call appropriate receive function to receive file
1570: */
1571: switch (type) {
1572: case S_IFDIR:
1573: recvdir(opts, mode, owner, group);
1574: break;
1575:
1576: case S_IFLNK:
1577: recvlink(new, opts, mode, size);
1578: break;
1579:
1580: case S_IFREG:
1.19 millert 1581: recvfile(new, opts, mode, owner, group, mtime, atime, size);
1.1 dm 1582: break;
1583:
1584: default:
1585: error("%d: unknown file type", type);
1586: break;
1587: }
1588: }
1589:
1590: /*
1.14 millert 1591: * Chmog something
1592: */
1593: static void
1594: dochmog(char *cmd)
1595: {
1596: int mode;
1597: opt_t opts;
1598: char *owner, *group, *file;
1599: char *cp = cmd;
1600: char fileb[MAXPATHLEN];
1601:
1602: /*
1603: * Get rdist option flags
1604: */
1605: opts = strtol(cp, &cp, 8);
1606: if (*cp++ != ' ') {
1607: error("dochmog: options not delimited");
1608: return;
1609: }
1610:
1611: /*
1612: * Get file mode
1613: */
1614: mode = strtol(cp, &cp, 8);
1615: if (*cp++ != ' ') {
1616: error("dochmog: mode not delimited");
1617: return;
1618: }
1619:
1620: /*
1621: * Get file owner name
1622: */
1623: owner = strtok(cp, " ");
1624: if (owner == NULL) {
1625: error("dochmog: owner name not delimited");
1626: return;
1627: }
1628:
1629: /*
1630: * Get file group name
1631: */
1632: group = strtok(NULL, " ");
1633: if (group == NULL) {
1634: error("dochmog: group name not delimited");
1635: return;
1636: }
1637:
1638: /*
1639: * Get file name. Can't use strtok() since there could
1640: * be white space in the file name.
1641: */
1642: if (DECODE(fileb, group + strlen(group) + 1) == -1) {
1643: error("dochmog: Cannot decode file name");
1644: return;
1645: }
1646:
1647: if (fileb[0] == '\0') {
1648: error("dochmog: no file name");
1649: return;
1650: }
1651: file = fileb;
1652:
1653: debugmsg(DM_MISC,
1654: "dochmog: opts = %04o mode = %04o", opts, mode);
1655: debugmsg(DM_MISC,
1656: "dochmog: owner = '%s' group = '%s' file = '%s' catname = %d",
1657: owner, group, file, catname);
1658:
1659: if (catname && cattarget(file) < 0) {
1660: error("Cannot set newname target.");
1661: return;
1662: }
1663:
1664: (void) fchog(-1, target, owner, group, mode);
1665:
1666: ack();
1667: }
1668:
1669: /*
1.1 dm 1670: * Set target information
1671: */
1.14 millert 1672: static void
1673: settarget(char *cmd, int isdir)
1.1 dm 1674: {
1675: char *cp = cmd;
1676: opt_t opts;
1.14 millert 1677: char file[BUFSIZ];
1.1 dm 1678:
1679: catname = isdir;
1680:
1681: /*
1682: * Parse options for this target
1683: */
1684: opts = strtol(cp, &cp, 8);
1685: if (*cp++ != ' ') {
1686: error("settarget: options not delimited");
1687: return;
1688: }
1689: options = opts;
1690:
1.14 millert 1691: if (DECODE(file, cp) == -1) {
1692: error("settarget: Cannot decode target name");
1693: return;
1694: }
1695:
1.1 dm 1696: /*
1697: * Handle target
1698: */
1.13 millert 1699: if (exptilde(target, cp, sizeof(target)) == NULL)
1.1 dm 1700: return;
1701: ptarget = target;
1702: while (*ptarget)
1703: ptarget++;
1704:
1705: ack();
1706: }
1707:
1708: /*
1709: * Cleanup in preparation for exiting.
1710: */
1.14 millert 1711: void
1712: cleanup(int dummy)
1.1 dm 1713: {
1714: /* We don't need to do anything */
1715: }
1716:
1717: /*
1718: * Server routine to read requests and process them.
1719: */
1.14 millert 1720: void
1721: server(void)
1.1 dm 1722: {
1723: static char cmdbuf[BUFSIZ];
1.10 mpech 1724: char *cp;
1725: int n;
1.1 dm 1726: extern jmp_buf finish_jmpbuf;
1727:
1.14 millert 1728: if (setjmp(finish_jmpbuf))
1.1 dm 1729: return;
1730: (void) signal(SIGHUP, sighandler);
1731: (void) signal(SIGINT, sighandler);
1732: (void) signal(SIGQUIT, sighandler);
1733: (void) signal(SIGTERM, sighandler);
1734: (void) signal(SIGPIPE, sighandler);
1735: (void) umask(oumask = umask(0));
1.14 millert 1736: (void) strlcpy(tempname, _RDIST_TMP, sizeof(tempname));
1.1 dm 1737: if (fromhost) {
1738: message(MT_SYSLOG, "Startup for %s", fromhost);
1739: #if defined(SETARGS)
1740: setproctitle("Serving %s", fromhost);
1741: #endif /* SETARGS */
1742: }
1743:
1744: /*
1745: * Let client know we want it to send it's version number
1746: */
1747: (void) sendcmd(S_VERSION, NULL);
1748:
1749: if (remline(cmdbuf, sizeof(cmdbuf), TRUE) < 0) {
1750: error("server: expected control record");
1751: return;
1752: }
1753:
1.14 millert 1754: if (cmdbuf[0] != S_VERSION || !isdigit((unsigned char)cmdbuf[1])) {
1.1 dm 1755: error("Expected version command, received: \"%s\".", cmdbuf);
1756: return;
1757: }
1758:
1759: proto_version = atoi(&cmdbuf[1]);
1760: if (proto_version != VERSION) {
1761: error("Protocol version %d is not supported.", proto_version);
1762: return;
1763: }
1764:
1765: /* Version number is okay */
1766: ack();
1767:
1768: /*
1769: * Main command loop
1770: */
1771: for ( ; ; ) {
1772: n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE);
1.14 millert 1773: if (n == -1) /* EOF */
1.1 dm 1774: return;
1775: if (n == 0) {
1776: error("server: expected control record");
1777: continue;
1778: }
1779:
1780: switch (*cp++) {
1781: case C_SETCONFIG: /* Configuration info */
1782: setconfig(cp);
1783: ack();
1784: continue;
1785:
1786: case C_DIRTARGET: /* init target file/directory name */
1787: settarget(cp, TRUE);
1788: continue;
1789:
1790: case C_TARGET: /* init target file/directory name */
1791: settarget(cp, FALSE);
1792: continue;
1793:
1794: case C_RECVREG: /* Transfer a regular file. */
1795: recvit(cp, S_IFREG);
1796: continue;
1797:
1798: case C_RECVDIR: /* Transfer a directory. */
1799: recvit(cp, S_IFDIR);
1800: continue;
1801:
1802: case C_RECVSYMLINK: /* Transfer symbolic link. */
1803: recvit(cp, S_IFLNK);
1804: continue;
1805:
1806: case C_RECVHARDLINK: /* Transfer hard link. */
1807: hardlink(cp);
1808: continue;
1809:
1810: case C_END: /* End of transfer */
1811: *ptarget = CNULL;
1812: if (catname <= 0) {
1813: error("server: too many '%c's", C_END);
1814: continue;
1815: }
1816: ptarget = sptarget[--catname];
1817: *ptarget = CNULL;
1818: ack();
1819: continue;
1820:
1821: case C_CLEAN: /* Clean. Cleanup a directory */
1822: clean(cp);
1823: continue;
1824:
1825: case C_QUERY: /* Query file/directory */
1826: query(cp);
1827: continue;
1828:
1829: case C_SPECIAL: /* Special. Execute commands */
1830: dospecial(cp);
1831: continue;
1832:
1833: case C_CMDSPECIAL: /* Cmd Special. Execute commands */
1834: docmdspecial();
1835: continue;
1836:
1.14 millert 1837: case C_CHMOG: /* Set owner, group, mode */
1838: dochmog(cp);
1.1 dm 1839: continue;
1840:
1841: case C_ERRMSG: /* Normal error message */
1842: if (cp && *cp)
1843: message(MT_NERROR|MT_NOREMOTE, "%s", cp);
1844: continue;
1845:
1846: case C_FERRMSG: /* Fatal error message */
1847: if (cp && *cp)
1848: message(MT_FERROR|MT_NOREMOTE, "%s", cp);
1849: return;
1850:
1851: default:
1852: error("server: unknown command '%s'", cp - 1);
1853: case CNULL:
1854: continue;
1855: }
1856: }
1857: }