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