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