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