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