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