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