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