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