Bug Summary

File:src/usr.bin/rdistd/server.c
Warning:line 1151, column 34
Call to function 'mktemp' is insecure as it always creates or uses insecure temporary file. Use 'mkstemp' instead

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)) {
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) ||
Call to function 'mktemp' is insecure as it always creates or uses insecure temporary file. Use 'mkstemp' instead
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}