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