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