[BACK]Return to server.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rdistd

Annotation of src/usr.bin/rdistd/server.c, Revision 1.35

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