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