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