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