Bug Summary

File:src/usr.bin/rdistd/server.c
Warning:line 869, column 8
Although the value stored to 'savefile' is used in the enclosing expression, the value is never actually read from 'savefile'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name server.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/rdistd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/rdistd/../rdist -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/rdistd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/rdistd/server.c
1/* $OpenBSD: server.c,v 1.47 2019/06/28 13:35:03 deraadt 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(*__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
50char tempname[sizeof _RDIST_TMP"rdistXXXXXXXX" + 1]; /* Tmp file name */
51char buf[BUFSIZ1024]; /* general purpose buffer */
52char target[PATH_MAX1024]; /* target/source directory name */
53char *ptarget; /* pointer to end of target name */
54int catname = 0; /* cat name to target name */
55char *sptarget[32]; /* stack of saved ptarget's for directories */
56char *fromhost = NULL((void *)0); /* Client hostname */
57static int64_t min_freespace = 0; /* Minimium free space on a filesystem */
58static int64_t min_freefiles = 0; /* Minimium free # files on a filesystem */
59int oumask; /* Old umask */
60
61static int cattarget(char *);
62static int setownership(char *, int, uid_t, gid_t, int);
63static int setfilemode(char *, int, int, int);
64static int fchog(int, char *, char *, char *, int);
65static int removefile(struct stat *, int);
66static void doclean(char *);
67static void clean(char *);
68static void dospecial(char *);
69static void docmdspecial(void);
70static void query(char *);
71static int chkparent(char *, opt_t);
72static char *savetarget(char *, opt_t);
73static void recvfile(char *, opt_t, int, char *, char *, time_t, time_t, off_t);
74static void recvdir(opt_t, int, char *, char *);
75static void recvlink(char *, opt_t, int, off_t);
76static void hardlink(char *);
77static void setconfig(char *);
78static void recvit(char *, int);
79static void dochmog(char *);
80static void settarget(char *, int);
81
82/*
83 * Cat "string" onto the target buffer with error checking.
84 */
85static int
86cattarget(char *string)
87{
88 if (strlen(string) + strlen(target) + 2 > sizeof(target)) {
89 message(MT_INFO0x0040, "target buffer is not large enough.");
90 return(-1);
91 }
92 if (!ptarget) {
93 message(MT_INFO0x0040, "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 */
106static int
107setownership(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-100, file, uid, gid,
132 AT_SYMLINK_NOFOLLOW0x02);
133
134 if (status == -1) {
135 if (uid == (uid_t)-1)
136 message(MT_NOTICE0x0100, "%s: chgrp %d failed: %s",
137 target, gid, SYSERRstrerror((*__errno())));
138 else
139 message(MT_NOTICE0x0100, "%s: chown %d:%d failed: %s",
140 target, uid, gid, SYSERRstrerror((*__errno())));
141 return(-1);
142 }
143
144 return(0);
145}
146
147/*
148 * Set mode of a file
149 */
150static int
151setfilemode(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-100, file, mode, AT_SYMLINK_NOFOLLOW0x02);
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_NOTICE0x0100, "%s: chmod failed: %s", target, SYSERRstrerror((*__errno())));
169 return(-1);
170 }
171
172 return(0);
173}
174/*
175 * Change owner, group and mode of file.
176 */
177static int
178fchog(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)(mode & 0004000)) {
193 message(MT_NOTICE0x0100,
194 "%s: unknown login name \"%s\", clearing setuid",
195 target, owner);
196 mode &= ~S_ISUID0004000;
197 uid = 0;
198 } else
199 message(MT_NOTICE0x0100,
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)(mode & 0004000) &&
214 strcmp(locuser, owner) != 0)
215 mode &= ~S_ISUID0004000;
216 if (mode)
217 mode &= ~S_ISVTX0001000; /* 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)(mode & 0002000)) {
227 message(MT_NOTICE0x0100,
228 "%s: unknown group \"%s\", clearing setgid",
229 target, group);
230 mode &= ~S_ISGID0002000;
231 } else
232 message(MT_NOTICE0x0100,
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)(mode & 0002000)) {
243 message(MT_NOTICE0x0100,
244 "%s: user %s not in group %s, clearing setgid",
245 target, locuser, group);
246 mode &= ~S_ISGID0002000;
247 }
248 gid = (gid_t)-1;
249 }
250ok:
251 if (stat(file, &st) == -1) {
252 error("%s: Stat failed %s", file, SYSERRstrerror((*__errno())));
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)((st.st_mode & 0170000) == 0120000)) < 0) {
261 if (mode != -1 && IS_ON(mode, S_ISUID)(mode & 0004000)) {
262 message(MT_NOTICE0x0100,
263 "%s: chown failed, clearing setuid", target);
264 mode &= ~S_ISUID0004000;
265 }
266 if (mode != -1 && IS_ON(mode, S_ISGID)(mode & 0002000)) {
267 message(MT_NOTICE0x0100,
268 "%s: chown failed, clearing setgid", target);
269 mode &= ~S_ISGID0002000;
270 }
271 }
272 (void) setfilemode(file, fd, mode, S_ISLNK(st.st_mode)((st.st_mode & 0170000) == 0120000));
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 */
282static int
283removefile(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_IFMT0170000) {
293 case S_IFREG0100000:
294 case S_IFLNK0120000:
295 case S_IFCHR0020000:
296 case S_IFBLK0060000:
297 case S_IFSOCK0140000:
298 case S_IFIFO0010000:
299 if (unlink(target) == -1) {
300 if (errno(*__errno()) == ETXTBSY26) {
301 if (!silent)
302 message(MT_REMOTE0x0400|MT_NOTICE0x0100,
303 "%s: unlink failed: %s",
304 target, SYSERRstrerror((*__errno())));
305 return(0);
306 } else {
307 error("%s: unlink failed: %s", target, SYSERRstrerror((*__errno())));
308 return(-1);
309 }
310 }
311 goto removed;
312
313 case S_IFDIR0040000:
314 break;
315
316 default:
317 error("%s: not a plain file", target);
318 return(-1);
319 }
320
321 errno(*__errno()) = 0;
322 if ((d = opendir(target)) == NULL((void *)0)) {
323 error("%s: opendir failed: %s", target, SYSERRstrerror((*__errno())));
324 return(-1);
325 }
326
327 optarget = ptarget;
328 len = ptarget - target;
329 while ((dp = readdir(d)) != NULL((void *)0)) {
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_MAX1024 - 1) {
335 if (!silent)
336 message(MT_REMOTE0x0400|MT_WARNING0x0010,
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_REMOTE0x0400|MT_WARNING0x0010,
350 "%s: lstat failed: %s",
351 target, SYSERRstrerror((*__errno())));
352 continue;
353 }
354 if (removefile(&stb, 0) < 0)
355 ++failures;
356 }
357 (void) closedir(d);
358 ptarget = optarget;
359 *ptarget = CNULL'\0';
360
361 if (failures)
362 return(-1);
363
364 if (rmdir(target) == -1) {
365 error("%s: rmdir failed: %s", target, SYSERRstrerror((*__errno())));
366 return(-1);
367 }
368removed:
369#if NEWWAY
370 if (!silent)
371 message(MT_CHANGE0x0020|MT_REMOTE0x0400, "%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_NOTICE0x0100|MT_REMOTE0x0400, "%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 */
388static void
389doclean(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_MAX1024*4];
398
399 opts = strtol(cp, &ep, 8);
400 if (*ep != CNULL'\0') {
401 error("clean: options not delimited");
402 return;
403 }
404 if ((d = opendir(target)) == NULL((void *)0)) {
405 error("%s: opendir failed: %s", target, SYSERRstrerror((*__errno())));
406 return;
407 }
408 ack()(void) sendcmd('\5', ((void *)0));
409
410 optarget = ptarget;
411 len = ptarget - target;
412 while ((dp = readdir(d)) != NULL((void *)0)) {
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_MAX1024 - 1) {
418 message(MT_REMOTE0x0400|MT_WARNING0x0010, "%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_REMOTE0x0400|MT_WARNING0x0010, "%s: lstat failed: %s",
430 target, SYSERRstrerror((*__errno())));
431 continue;
432 }
433
434 ENCODE(targ, dp->d_name)strvis(targ, dp->d_name, (0x04 | 0x08 | 0x10));
435 (void) sendcmd(CC_QUERY'Q', "%s", targ);
436 (void) remline(cp = buf, sizeof(buf), TRUE1);
437
438 if (*cp != CC_YES'Y')
439 continue;
440
441 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001))
442 message(MT_REMOTE0x0400|MT_INFO0x0040, "%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'\0';
451}
452
453/*
454 * Frontend to doclean().
455 */
456static void
457clean(char *cp)
458{
459 doclean(cp);
460 (void) sendcmd(CC_END'E', NULL((void *)0));
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 */
469static void
470dospecial(char *xcmd)
471{
472 char cmd[BUFSIZ1024];
473 if (DECODE(cmd, xcmd)strunvis(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 */
488static void
489docmdspecial(void)
490{
491 char *cp;
492 char *cmd, *env = NULL((void *)0);
493 int n;
494 size_t len;
495
496 /* We're ready */
497 ack()(void) sendcmd('\5', ((void *)0));
498
499 for ( ; ; ) {
500 n = remline(cp = buf, sizeof(buf), FALSE0);
501 if (n <= 0) {
502 error("cmdspecial: premature end of input.");
503 return;
504 }
505
506 switch (*cp++) {
507 case RC_FILE'F':
508 if (env == NULL((void *)0)) {
509 len = (2 * sizeof(E_FILES"FILES")) + strlen(cp) + 10;
510 env = xmalloc(len);
511 (void) snprintf(env, len, "export %s;%s=%s",
512 E_FILES"FILES", E_FILES"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()(void) sendcmd('\5', ((void *)0));
520 break;
521
522 case RC_COMMAND'C':
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 */
554static void
555query(char *xname)
556{
557 static struct stat stb;
558 int s = -1, stbvalid = 0;
559 char name[PATH_MAX1024];
560
561 if (DECODE(name, xname)strunvis(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)(options & 0x0000200)) {
570 s = is_nfs_mounted(target, &stb, &stbvalid);
571 if (s > 0)
572 (void) sendcmd(QC_ONNFS'F', NULL((void *)0));
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'\0';
578 return;
579 }
580 }
581
582 if (IS_ON(options, DO_CHKREADONLY)(options & 0x0000400)) {
583 s = is_ro_mounted(target, &stb, &stbvalid);
584 if (s > 0)
585 (void) sendcmd(QC_ONRO'O', NULL((void *)0));
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'\0';
591 return;
592 }
593 }
594
595 if (IS_ON(options, DO_CHKSYM)(options & 0x0020000)) {
596 if (is_symlinked(target, &stb, &stbvalid) > 0) {
597 (void) sendcmd(QC_SYM'l', NULL((void *)0));
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(*__errno()) == ENOENT2)
608 (void) sendcmd(QC_NO'N', NULL((void *)0));
609 else
610 error("%s: lstat failed: %s", target, SYSERRstrerror((*__errno())));
611 *ptarget = CNULL'\0';
612 return;
613 }
614
615 switch (stb.st_mode & S_IFMT0170000) {
616 case S_IFLNK0120000:
617 case S_IFDIR0040000:
618 case S_IFREG0100000:
619 (void) sendcmd(QC_YES'Y', "%lld %lld %o %s %s",
620 (long long) stb.st_size,
621 (long long) stb.st_mtimest_mtim.tv_sec,
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'\0';
632}
633
634/*
635 * Check to see if parent directory exists and create one if not.
636 */
637static int
638chkparent(char *name, opt_t opts)
639{
640 char *cp;
641 struct stat stb;
642 int r = -1;
643
644 debugmsg(DM_CALL0x01, "chkparent(%s, %#x) start\n", name, opts);
645
646 cp = strrchr(name, '/');
647 if (cp == NULL((void *)0) || cp == name)
648 return(0);
649
650 *cp = CNULL'\0';
651
652 if (lstat(name, &stb) == -1) {
653 if (errno(*__errno()) == ENOENT2 && chkparent(name, opts) >= 0) {
654 if (mkdir(name, 0777 & ~oumask) == 0) {
655 message(MT_NOTICE0x0100, "%s: mkdir", name);
656 r = 0;
657 } else
658 debugmsg(DM_MISC0x10,
659 "chkparent(%s, %#04o) mkdir fail: %s\n",
660 name, opts, SYSERRstrerror((*__errno())));
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 */
674static char *
675savetarget(char *file, opt_t opts)
676{
677 static char savefile[PATH_MAX1024];
678
679 if (strlen(file) + sizeof(SAVE_SUFFIX".OLD") + 1 > PATH_MAX1024) {
680 error("%s: Cannot save: Save name too long", file);
681 return(NULL((void *)0));
682 }
683
684 if (IS_ON(opts, DO_HISTORY)(opts & 0x0100000)) {
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(*__errno()) == ENOENT2)
695 break;
696
697 }
698 if (i == 1000) {
699 message(MT_NOTICE0x0100,
700 "%s: More than 1000 versions for %s; reusing 1\n",
701 savefile, SYSERRstrerror((*__errno())));
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".OLD");
710
711 if (unlink(savefile) != 0 && errno(*__errno()) != ENOENT2) {
712 message(MT_NOTICE0x0100, "%s: remove failed: %s",
713 savefile, SYSERRstrerror((*__errno())));
714 return(NULL((void *)0));
715 }
716 }
717
718 if (rename(file, savefile) != 0 && errno(*__errno()) != ENOENT2) {
719 error("%s -> %s: rename failed: %s",
720 file, savefile, SYSERRstrerror((*__errno())));
721 return(NULL((void *)0));
722 }
723
724 return(savefile);
725}
726
727/*
728 * Receive a file
729 */
730static void
731recvfile(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((void *)0);
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, SYSERRstrerror((*__errno())));
745 return;
746 }
747
748 /*
749 * Receive the file itself
750 */
751 ack()(void) sendcmd('\5', ((void *)0));
752 wrerr = 0;
753 olderrno = 0;
754 for (i = 0; i < size; i += BUFSIZ1024) {
755 off_t amt = BUFSIZ1024;
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 = BUFSIZ1024;
775 if (i + amt > size)
776 amt = size - i;
777 if (wrerr == 0 && xwrite(f, buf, amt) != amt) {
778 olderrno = errno(*__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)(opts & 0x0000008)) {
800 FILE *f1, *f2;
801 int c;
802
803 errno(*__errno()) = 0; /* fopen is not a syscall */
804 if ((f1 = fopen(target, "r")) == NULL((void *)0)) {
805 error("%s: open for read failed: %s", target, SYSERRstrerror((*__errno())));
806 (void) close(f);
807 (void) unlink(new);
808 return;
809 }
810 errno(*__errno()) = 0;
811 if ((f2 = fopen(new, "r")) == NULL((void *)0)) {
812 error("%s: open for read failed: %s", new, SYSERRstrerror((*__errno())));
813 (void) fclose(f1);
814 (void) close(f);
815 (void) unlink(new);
816 return;
817 }
818 while ((c = getc(f1)(!__isthreaded ? (--(f1)->_r < 0 ? __srget(f1) : (int)(
*(f1)->_p++)) : (getc)(f1))
) == getc(f2)(!__isthreaded ? (--(f2)->_r < 0 ? __srget(f2) : (int)(
*(f2)->_p++)) : (getc)(f2))
)
819 if (c == EOF(-1)) {
820 debugmsg(DM_MISC0x10,
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((void *)0));
833 return;
834 }
835 debugmsg(DM_MISC0x10, "Files are different '%s' '%s'.",
836 target, new);
837 (void) fclose(f1);
838 (void) fclose(f2);
839 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001)) {
840 message(MT_REMOTE0x0400|MT_INFO0x0040, "%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((void *)0)), mtime) < 0)
863 message(MT_NOTICE0x0100, "%s: utimes failed: %s", new, SYSERRstrerror((*__errno())));
864
865 /*
866 * Try to save target file from being over-written
867 */
868 if (IS_ON(opts, DO_SAVETARGETS)(opts & 0x0001000))
869 if ((savefile = savetarget(target, opts)) == NULL((void *)0)) {
Although the value stored to 'savefile' is used in the enclosing expression, the value is never actually read from 'savefile'
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)((statbuff.st_mode & 0170000) == 0040000)) {
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(*__errno())) {
897 case ETXTBSY26:
898 /* Save the target */
899 if ((savefile = savetarget(target, opts)) != NULL((void *)0)) {
900 /* Retry installing new file as target */
901 if (rename(new, target) == -1) {
902 error(fmt, new, target, SYSERRstrerror((*__errno())));
903 /* Try to put back save file */
904 if (rename(savefile, target) == -1)
905 error(fmt,
906 savefile, target, SYSERRstrerror((*__errno())));
907 (void) unlink(new);
908 } else
909 message(MT_NOTICE0x0100, "%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 EISDIR21:
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)((stb.st_mode & 0170000) == 0040000)) {
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, SYSERRstrerror((*__errno())));
939 (void) unlink(new);
940 break;
941 }
942 }
943
944 if (IS_ON(opts, DO_COMPARE)(opts & 0x0000008))
945 message(MT_REMOTE0x0400|MT_CHANGE0x0020, "%s: updated", target);
946 else
947 ack()(void) sendcmd('\5', ((void *)0));
948}
949
950/*
951 * Receive a directory
952 */
953static void
954recvdir(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)((stb.st_mode & 0170000) == 0040000)) {
967 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001))
968 message(MT_NOTICE0x0100, "%s: need to remove",
969 target);
970 else {
971 if (unlink(target) == -1) {
972 error("%s: remove failed: %s",
973 target, SYSERRstrerror((*__errno())));
974 return;
975 }
976 }
977 s = -1;
978 errno(*__errno()) = ENOENT2;
979 } else {
980 if (!IS_ON(opts, DO_NOCHKMODE)(opts & 0x0008000) &&
981 (stb.st_mode & 07777) != mode) {
982 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001))
983 message(MT_NOTICE0x0100,
984 "%s: need to chmod to %#04o",
985 target, mode);
986 else if (chmod(target, mode) != 0)
987 message(MT_NOTICE0x0100,
988 "%s: chmod from %#04o to %#04o failed: %s",
989 target,
990 stb.st_mode & 07777,
991 mode,
992 SYSERRstrerror((*__errno())));
993 else
994 message(MT_NOTICE0x0100,
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'\0';
1005 lgroup[0] = CNULL'\0';
1006
1007 if (!IS_ON(opts, DO_NOCHKOWNER)(opts & 0x0004000) && owner) {
1008 int o;
1009
1010 o = (owner[0] == ':') ? opts & DO_NUMCHKOWNER0x0080000 :
1011 opts;
1012 if ((cp = getusername(stb.st_uid, target, o))
1013 != NULL((void *)0))
1014 if (strcmp(owner, cp))
1015 (void) strlcpy(lowner, cp,
1016 sizeof(lowner));
1017 }
1018 if (!IS_ON(opts, DO_NOCHKGROUP)(opts & 0x0010000) && group) {
1019 int o;
1020
1021 o = (group[0] == ':') ? opts & DO_NUMCHKGROUP0x0040000 :
1022 opts;
1023 if ((cp = getgroupname(stb.st_gid, target, o))
1024 != NULL((void *)0))
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'\0' || lgroup[0] != CNULL'\0') {
1035 if (lowner[0] == CNULL'\0' &&
1036 (cp = getusername(stb.st_uid,
1037 target, opts)))
1038 (void) strlcpy(lowner, cp,
1039 sizeof(lowner));
1040 if (lgroup[0] == CNULL'\0' &&
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)(opts & 0x0000001))
1047 message(MT_NOTICE0x0100,
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_NOTICE0x0100,
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()(void) sendcmd('\5', ((void *)0));
1066 return;
1067 }
1068 }
1069
1070 if (IS_ON(opts, DO_VERIFY)(opts & 0x0000001)) {
1071 ack()(void) sendcmd('\5', ((void *)0));
1072 return;
1073 }
1074
1075 /*
1076 * Create the directory
1077 */
1078 if (s < 0) {
1079 if (errno(*__errno()) == ENOENT2) {
1080 if (mkdir(target, mode) == 0 ||
1081 (chkparent(target, opts) == 0 &&
1082 mkdir(target, mode) == 0)) {
1083 message(MT_NOTICE0x0100, "%s: mkdir", target);
1084 (void) fchog(-1, target, owner, group, mode);
1085 ack()(void) sendcmd('\5', ((void *)0));
1086 } else {
1087 error("%s: mkdir failed: %s", target, SYSERRstrerror((*__errno())));
1088 ptarget = sptarget[--catname];
1089 *ptarget = CNULL'\0';
1090 }
1091 return;
1092 }
1093 }
1094 error("%s: lstat failed: %s", target, SYSERRstrerror((*__errno())));
1095 ptarget = sptarget[--catname];
1096 *ptarget = CNULL'\0';
1097}
1098
1099/*
1100 * Receive a link
1101 */
1102static void
1103recvlink(char *new, opt_t opts, int mode, off_t size)
1104{
1105 char tbuf[PATH_MAX1024], dbuf[BUFSIZ1024];
1106 struct stat stb;
1107 char *optarget;
1108 int uptodate;
1109 off_t i;
1110
1111 /*
1112 * Read basic link info
1113 */
1114 ack()(void) sendcmd('\5', ((void *)0));
1115 (void) remline(buf, sizeof(buf), TRUE1);
1116
1117 if (response() < 0) {
1118 err()(void) sendcmd('\1', ((void *)0));
1119 return;
1120 }
1121
1122 if (DECODE(dbuf, buf)strunvis(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)(opts & 0x0000001) || uptodate) {
1136 if (uptodate)
1137 message(MT_REMOTE0x0400|MT_INFO0x0040, NULL((void *)0));
1138 else
1139 message(MT_REMOTE0x0400|MT_INFO0x0040, "%s: need to update",
1140 target);
1141 if (IS_ON(opts, DO_COMPARE)(opts & 0x0000008))
1142 return;
1143 (void) sendcmd(C_END'E', NULL((void *)0));
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((void *)0) ||
1152 symlink(dbuf, new) == -1) {
1153 error("%s -> %s: symlink failed: %s", new, dbuf, SYSERRstrerror((*__errno())));
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)((stb.st_mode & 0170000) == 0040000)) {
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'E', NULL((void *)0));
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, SYSERRstrerror((*__errno())));
1181 (void) unlink(new);
1182 (void) sendcmd(C_END'E', NULL((void *)0));
1183 (void) response();
1184 return;
1185 }
1186
1187 message(MT_REMOTE0x0400|MT_CHANGE0x0020, "%s: updated", target);
1188
1189 /*
1190 * Indicate end of receive operation
1191 */
1192 (void) sendcmd(C_END'E', NULL((void *)0));
1193 (void) response();
1194}
1195
1196/*
1197 * Creat a hard link to existing file.
1198 */
1199static void
1200hardlink(char *cmd)
1201{
1202 struct stat stb;
1203 int exists = 0;
1204 char *xoldname, *xnewname;
1205 char *cp = cmd;
1206 static char expbuf[BUFSIZ1024];
1207 char oldname[BUFSIZ1024], newname[BUFSIZ1024];
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((void *)0)) {
1218 error("hardlink: oldname name not delimited");
1219 return;
1220 }
1221
1222 if (DECODE(oldname, xoldname)strunvis(oldname, xoldname) == -1) {
1223 error("hardlink: Cannot decode oldname");
1224 return;
1225 }
1226
1227 xnewname = strtok(NULL((void *)0), " ");
1228 if (xnewname == NULL((void *)0)) {
1229 error("hardlink: new name not specified");
1230 return;
1231 }
1232
1233 if (DECODE(newname, xnewname)strunvis(newname, xnewname) == -1) {
1234 error("hardlink: Cannot decode newname");
1235 return;
1236 }
1237
1238 if (exptilde(expbuf, oldname, sizeof(expbuf)) == NULL((void *)0)) {
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_IFMT0170000;
1250
1251 if (mode != S_IFREG0100000 && mode != S_IFLNK0120000) {
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, SYSERRstrerror((*__errno())));
1260 return;
1261 }
1262 if (exists && (unlink(target) == -1)) {
1263 error("%s: unlink failed: %s", target, SYSERRstrerror((*__errno())));
1264 return;
1265 }
1266 if (linkat(AT_FDCWD-100, expbuf, AT_FDCWD-100, target, 0) == -1) {
1267 error("%s: cannot link to %s: %s", target, oldname, SYSERRstrerror((*__errno())));
1268 return;
1269 }
1270 ack()(void) sendcmd('\5', ((void *)0));
1271}
1272
1273/*
1274 * Set configuration information.
1275 *
1276 * A key letter is followed immediately by the value
1277 * to set. The keys are:
1278 * SC_FREESPACE - Set minimium free space of filesystem
1279 * SC_FREEFILES - Set minimium free number of files of filesystem
1280 */
1281static void
1282setconfig(char *cmd)
1283{
1284 char *cp = cmd;
1285 char *estr;
1286 const char *errstr;
1287
1288 switch (*cp++) {
1289 case SC_HOSTNAME'H': /* 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_SYSLOG0x0200, "startup for %s", fromhost);
1296 setproctitle("serving %s", cp);
1297 }
1298 break;
1299
1300 case SC_FREESPACE's': /* Minimium free space */
1301 min_freespace = (int64_t)strtonum(cp, 0, LLONG_MAX9223372036854775807LL, &errstr);
1302 if (errstr)
1303 fatalerr("Minimum free space is %s: '%s'", errstr,
1304 optarg);
1305 break;
1306
1307 case SC_FREEFILES'f': /* Minimium free files */
1308 min_freefiles = (int64_t)strtonum(cp, 0, LLONG_MAX9223372036854775807LL, &errstr);
1309 if (errstr)
1310 fatalerr("Minimum free files is %s: '%s'", errstr,
1311 optarg);
1312 break;
1313
1314 case SC_LOGGING'L': /* Logging options */
1315 if ((estr = msgparseopts(cp, TRUE1)) != NULL((void *)0)) {
1316 fatalerr("Bad message option string (%s): %s",
1317 cp, estr);
1318 return;
1319 }
1320 break;
1321
1322 case SC_DEFOWNER'o':
1323 (void) strlcpy(defowner, cp, sizeof(defowner));
1324 break;
1325
1326 case SC_DEFGROUP'g':
1327 (void) strlcpy(defgroup, cp, sizeof(defgroup));
1328 break;
1329
1330 default:
1331 message(MT_NOTICE0x0100, "Unknown config command \"%s\".", cp-1);
1332 return;
1333 }
1334}
1335
1336/*
1337 * Receive something
1338 */
1339static void
1340recvit(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_MAX1024];
1348 char fileb[PATH_MAX1024];
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((void *)0)) {
1402 error("recvit: owner name not delimited");
1403 return;
1404 }
1405
1406 /*
1407 * Get file group name
1408 */
1409 group = strtok(NULL((void *)0), " ");
1410 if (group == NULL((void *)0)) {
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)strunvis(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_MISC0x10,
1431 "recvit: opts = %#x mode = %#04o size = %lld mtime = %lld",
1432 opts, mode, (long long) size, (long long)mtime);
1433 debugmsg(DM_MISC0x10,
1434 "recvit: owner = '%s' group = '%s' file = '%s' catname = %d isdir = %d",
1435 owner, group, file, catname, (type == S_IFDIR0040000) ? 1 : 0);
1436
1437 if (type == S_IFDIR0040000) {
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((void *)0))
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'\0';
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_IFDIR0040000:
1506 recvdir(opts, mode, owner, group);
1507 break;
1508
1509 case S_IFLNK0120000:
1510 recvlink(new, opts, mode, size);
1511 break;
1512
1513 case S_IFREG0100000:
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 */
1526static void
1527dochmog(char *cmd)
1528{
1529 int mode;
1530 opt_t opts;
1531 char *owner, *group, *file;
1532 char *cp = cmd;
1533 char fileb[PATH_MAX1024];
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((void *)0)) {
1558 error("dochmog: owner name not delimited");
1559 return;
1560 }
1561
1562 /*
1563 * Get file group name
1564 */
1565 group = strtok(NULL((void *)0), " ");
1566 if (group == NULL((void *)0)) {
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)strunvis(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_MISC0x10,
1587 "dochmog: opts = %#x mode = %#04o", opts, mode);
1588 debugmsg(DM_MISC0x10,
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()(void) sendcmd('\5', ((void *)0));
1600}
1601
1602/*
1603 * Set target information
1604 */
1605static void
1606settarget(char *cmd, int isdir)
1607{
1608 char *cp = cmd;
1609 opt_t opts;
1610 char file[BUFSIZ1024];
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)strunvis(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((void *)0))
1633 return;
1634 ptarget = target;
1635 while (*ptarget)
1636 ptarget++;
1637
1638 ack()(void) sendcmd('\5', ((void *)0));
1639}
1640
1641/*
1642 * Cleanup in preparation for exiting.
1643 */
1644void
1645cleanup(int dummy)
1646{
1647 /* We don't need to do anything */
1648}
1649
1650/*
1651 * Server routine to read requests and process them.
1652 */
1653void
1654server(void)
1655{
1656 static char cmdbuf[BUFSIZ1024];
1657 char *cp;
1658 int n, proto_version;
1659
1660 if (setjmp(finish_jmpbuf))
1661 return;
1662 (void) signal(SIGHUP1, sighandler);
1663 (void) signal(SIGINT2, sighandler);
1664 (void) signal(SIGQUIT3, sighandler);
1665 (void) signal(SIGTERM15, sighandler);
1666 (void) signal(SIGPIPE13, sighandler);
1667 (void) umask(oumask = umask(0));
1668 (void) strlcpy(tempname, _RDIST_TMP"rdistXXXXXXXX", sizeof(tempname));
1669 if (fromhost) {
1670 message(MT_SYSLOG0x0200, "Startup for %s", fromhost);
1671#if defined(SETARGS)
1672 setproctitle("Serving %s", fromhost);
1673#endif /* SETARGS */
1674 }
1675
1676 /*
1677 * Let client know we want it to send it's version number
1678 */
1679 (void) sendcmd(S_VERSION'V', NULL((void *)0));
1680
1681 if (remline(cmdbuf, sizeof(cmdbuf), TRUE1) < 0) {
1682 error("server: expected control record");
1683 return;
1684 }
1685
1686 if (cmdbuf[0] != S_VERSION'V' || !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 != VERSION6) {
1693 error("Protocol version %d is not supported.", proto_version);
1694 return;
1695 }
1696
1697 /* Version number is okay */
1698 ack()(void) sendcmd('\5', ((void *)0));
1699
1700 /*
1701 * Main command loop
1702 */
1703 for ( ; ; ) {
1704 n = remline(cp = cmdbuf, sizeof(cmdbuf), TRUE1);
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'c': /* Configuration info */
1714 setconfig(cp);
1715 ack()(void) sendcmd('\5', ((void *)0));
1716 continue;
1717
1718 case C_DIRTARGET'T': /* init target file/directory name */
1719 settarget(cp, TRUE1);
1720 continue;
1721
1722 case C_TARGET't': /* init target file/directory name */
1723 settarget(cp, FALSE0);
1724 continue;
1725
1726 case C_RECVREG'R': /* Transfer a regular file. */
1727 recvit(cp, S_IFREG0100000);
1728 continue;
1729
1730 case C_RECVDIR'D': /* Transfer a directory. */
1731 recvit(cp, S_IFDIR0040000);
1732 continue;
1733
1734 case C_RECVSYMLINK'K': /* Transfer symbolic link. */
1735 recvit(cp, S_IFLNK0120000);
1736 continue;
1737
1738 case C_RECVHARDLINK'k': /* Transfer hard link. */
1739 hardlink(cp);
1740 continue;
1741
1742 case C_END'E': /* End of transfer */
1743 *ptarget = CNULL'\0';
1744 if (catname <= 0) {
1745 error("server: too many '%c's", C_END'E');
1746 continue;
1747 }
1748 ptarget = sptarget[--catname];
1749 *ptarget = CNULL'\0';
1750 ack()(void) sendcmd('\5', ((void *)0));
1751 continue;
1752
1753 case C_CLEAN'C': /* Clean. Cleanup a directory */
1754 clean(cp);
1755 continue;
1756
1757 case C_QUERY'Q': /* Query file/directory */
1758 query(cp);
1759 continue;
1760
1761 case C_SPECIAL'S': /* Special. Execute commands */
1762 dospecial(cp);
1763 continue;
1764
1765 case C_CMDSPECIAL's': /* Cmd Special. Execute commands */
1766 docmdspecial();
1767 continue;
1768
1769 case C_CHMOG'M': /* Set owner, group, mode */
1770 dochmog(cp);
1771 continue;
1772
1773 case C_ERRMSG'\1': /* Normal error message */
1774 if (cp && *cp)
1775 message(MT_NERROR0x0002|MT_NOREMOTE0x1000, "%s", cp);
1776 continue;
1777
1778 case C_FERRMSG'\2': /* Fatal error message */
1779 if (cp && *cp)
1780 message(MT_FERROR0x0004|MT_NOREMOTE0x1000, "%s", cp);
1781 return;
1782
1783 default:
1784 error("server: unknown command '%s'", cp - 1);
1785 case CNULL'\0':
1786 continue;
1787 }
1788 }
1789}