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