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

Annotation of src/usr.bin/rsync/receiver.c, Revision 1.1

1.1     ! benno       1: /*     $Id$ */
        !             2:
        !             3: /*
        !             4:  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
        !             5:  *
        !             6:  * Permission to use, copy, modify, and distribute this software for any
        !             7:  * purpose with or without fee is hereby granted, provided that the above
        !             8:  * copyright notice and this permission notice appear in all copies.
        !             9:  *
        !            10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            17:  */
        !            18: #include <sys/mman.h>
        !            19: #include <sys/stat.h>
        !            20:
        !            21: #include <assert.h>
        !            22: #include <errno.h>
        !            23: #include <fcntl.h>
        !            24: #include <inttypes.h>
        !            25: #include <math.h>
        !            26: #include <poll.h>
        !            27: #include <stdio.h>
        !            28: #include <stdlib.h>
        !            29: #include <string.h>
        !            30: #include <time.h>
        !            31: #include <unistd.h>
        !            32:
        !            33: #include "extern.h"
        !            34:
        !            35: enum   pfdt {
        !            36:        PFD_SENDER_IN = 0, /* input from the sender */
        !            37:        PFD_UPLOADER_IN, /* uploader input from a local file */
        !            38:        PFD_DOWNLOADER_IN, /* downloader input from a local file */
        !            39:        PFD_SENDER_OUT, /* output to the sender */
        !            40:        PFD__MAX
        !            41: };
        !            42:
        !            43: /*
        !            44:  * Pledges: unveil, rpath, cpath, wpath, stdio, fattr.
        !            45:  * Pledges (dry-run): -cpath, -wpath, -fattr.
        !            46:  */
        !            47: int
        !            48: rsync_receiver(struct sess *sess,
        !            49:        int fdin, int fdout, const char *root)
        !            50: {
        !            51:        struct flist    *fl = NULL, *dfl = NULL;
        !            52:        size_t           i, flsz = 0, dflsz = 0, excl;
        !            53:        char            *tofree;
        !            54:        int              rc = 0, dfd = -1, phase = 0, c;
        !            55:        int32_t          ioerror;
        !            56:        struct pollfd    pfd[PFD__MAX];
        !            57:        struct download *dl = NULL;
        !            58:        struct upload   *ul = NULL;
        !            59:        mode_t           oumask;
        !            60:
        !            61:        if (-1 == pledge("unveil rpath cpath wpath stdio fattr", NULL)) {
        !            62:                ERR(sess, "pledge");
        !            63:                goto out;
        !            64:        }
        !            65:
        !            66:        /* Client sends zero-length exclusions. */
        !            67:
        !            68:        if ( ! sess->opts->server &&
        !            69:             ! io_write_int(sess, fdout, 0)) {
        !            70:                ERRX1(sess, "io_write_int");
        !            71:                goto out;
        !            72:        }
        !            73:
        !            74:        if (sess->opts->server && sess->opts->del) {
        !            75:                if ( ! io_read_size(sess, fdin, &excl)) {
        !            76:                        ERRX1(sess, "io_read_size");
        !            77:                        goto out;
        !            78:                } else if (0 != excl) {
        !            79:                        ERRX(sess, "exclusion list is non-empty");
        !            80:                        goto out;
        !            81:                }
        !            82:        }
        !            83:
        !            84:        /*
        !            85:         * Start by receiving the file list and our mystery number.
        !            86:         * These we're going to be touching on our local system.
        !            87:         */
        !            88:
        !            89:        if ( ! flist_recv(sess, fdin, &fl, &flsz)) {
        !            90:                ERRX1(sess, "flist_recv");
        !            91:                goto out;
        !            92:        }
        !            93:
        !            94:        /* The IO error is sent after the file list. */
        !            95:
        !            96:        if ( ! io_read_int(sess, fdin, &ioerror)) {
        !            97:                ERRX1(sess, "io_read_int");
        !            98:                goto out;
        !            99:        } else if (0 != ioerror) {
        !           100:                ERRX1(sess, "io_error is non-zero");
        !           101:                goto out;
        !           102:        }
        !           103:
        !           104:        if (0 == flsz && ! sess->opts->server) {
        !           105:                WARNX(sess, "receiver has empty file list: exiting");
        !           106:                rc = 1;
        !           107:                goto out;
        !           108:        } else if ( ! sess->opts->server)
        !           109:                LOG1(sess, "Transfer starting: %zu files", flsz);
        !           110:
        !           111:        LOG2(sess, "%s: receiver destination", root);
        !           112:
        !           113:        /*
        !           114:         * Create the path for our destination directory, if we're not
        !           115:         * in dry-run mode (which would otherwise crash w/the pledge).
        !           116:         * This uses our current umask: we might set the permissions on
        !           117:         * this directory in post_dir().
        !           118:         */
        !           119:
        !           120:        if ( ! sess->opts->dry_run) {
        !           121:                if (NULL == (tofree = strdup(root))) {
        !           122:                        ERR(sess, "strdup");
        !           123:                        goto out;
        !           124:                } else if (mkpath(sess, tofree) < 0) {
        !           125:                        ERRX1(sess, "%s: mkpath", root);
        !           126:                        free(tofree);
        !           127:                        goto out;
        !           128:                }
        !           129:                free(tofree);
        !           130:        }
        !           131:
        !           132:        /*
        !           133:         * Disable umask() so we can set permissions fully.
        !           134:         * Then open the directory iff we're not in dry_run.
        !           135:         */
        !           136:
        !           137:        oumask = umask(0);
        !           138:
        !           139:        if ( ! sess->opts->dry_run) {
        !           140:                dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
        !           141:                if (-1 == dfd) {
        !           142:                        ERR(sess, "%s: open", root);
        !           143:                        goto out;
        !           144:                }
        !           145:        }
        !           146:
        !           147:        /*
        !           148:         * Begin by conditionally getting all files we have currently
        !           149:         * available in our destination.
        !           150:         * XXX: THIS IS A BUG IN OPENBSD 6.4.
        !           151:         * For newer version of OpenBSD, this is safe to put after the
        !           152:         * unveil.
        !           153:         */
        !           154:
        !           155:        if (sess->opts->del &&
        !           156:            sess->opts->recursive &&
        !           157:            ! flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
        !           158:                ERRX1(sess, "flist_gen_local");
        !           159:                goto out;
        !           160:        }
        !           161:
        !           162:        /*
        !           163:         * Make our entire view of the file-system be limited to what's
        !           164:         * in the root directory.
        !           165:         * This prevents us from accidentally (or "under the influence")
        !           166:         * writing into other parts of the file-system.
        !           167:         */
        !           168:
        !           169:        if (-1 == unveil(root, "rwc")) {
        !           170:                ERR(sess, "%s: unveil", root);
        !           171:                goto out;
        !           172:        } else if (-1 == unveil(NULL, NULL)) {
        !           173:                ERR(sess, "%s: unveil", root);
        !           174:                goto out;
        !           175:        }
        !           176:
        !           177:        /* If we have a local set, go for the deletion. */
        !           178:
        !           179:        if ( ! flist_del(sess, dfd, dfl, dflsz)) {
        !           180:                ERRX1(sess, "flist_del");
        !           181:                goto out;
        !           182:        }
        !           183:
        !           184:        /* Initialise poll events to listen from the sender. */
        !           185:
        !           186:        pfd[PFD_SENDER_IN].fd = fdin;
        !           187:        pfd[PFD_UPLOADER_IN].fd = -1;
        !           188:        pfd[PFD_DOWNLOADER_IN].fd = -1;
        !           189:        pfd[PFD_SENDER_OUT].fd = fdout;
        !           190:
        !           191:        pfd[PFD_SENDER_IN].events = POLLIN;
        !           192:        pfd[PFD_UPLOADER_IN].events = POLLIN;
        !           193:        pfd[PFD_DOWNLOADER_IN].events = POLLIN;
        !           194:        pfd[PFD_SENDER_OUT].events = POLLOUT;
        !           195:
        !           196:        ul = upload_alloc(sess, dfd, fdout,
        !           197:                CSUM_LENGTH_PHASE1, fl, flsz, oumask);
        !           198:        if (NULL == ul) {
        !           199:                ERRX1(sess, "upload_alloc");
        !           200:                goto out;
        !           201:        }
        !           202:
        !           203:        dl = download_alloc(sess, fdin, fl, flsz, dfd);
        !           204:        if (NULL == dl) {
        !           205:                ERRX1(sess, "download_alloc");
        !           206:                goto out;
        !           207:        }
        !           208:
        !           209:        LOG2(sess, "%s: ready for phase 1 data", root);
        !           210:
        !           211:        for (;;) {
        !           212:                if (-1 == (c = poll(pfd, PFD__MAX, INFTIM))) {
        !           213:                        ERR(sess, "poll");
        !           214:                        goto out;
        !           215:                }
        !           216:
        !           217:                for (i = 0; i < PFD__MAX; i++)
        !           218:                        if (pfd[i].revents & (POLLERR|POLLNVAL)) {
        !           219:                                ERRX(sess, "poll: bad fd");
        !           220:                                goto out;
        !           221:                        } else if (pfd[i].revents & POLLHUP) {
        !           222:                                ERRX(sess, "poll: hangup");
        !           223:                                goto out;
        !           224:                        }
        !           225:
        !           226:                /*
        !           227:                 * If we have a read event and we're multiplexing, we
        !           228:                 * might just have error messages in the pipe.
        !           229:                 * It's important to flush these out so that we don't
        !           230:                 * clog the pipe.
        !           231:                 * Unset our polling status if there's nothing that
        !           232:                 * remains in the pipe.
        !           233:                 */
        !           234:
        !           235:                if (sess->mplex_reads &&
        !           236:                    (POLLIN & pfd[PFD_SENDER_IN].revents)) {
        !           237:                        if ( ! io_read_flush(sess, fdin)) {
        !           238:                                ERRX1(sess, "io_read_flush");
        !           239:                                goto out;
        !           240:                        } else if (0 == sess->mplex_read_remain)
        !           241:                                pfd[PFD_SENDER_IN].revents &= ~POLLIN;
        !           242:                }
        !           243:
        !           244:
        !           245:                /*
        !           246:                 * We run the uploader if we have files left to examine
        !           247:                 * (i < flsz) or if we have a file that we've opened and
        !           248:                 * is read to mmap.
        !           249:                 */
        !           250:
        !           251:                if ((POLLIN & pfd[PFD_UPLOADER_IN].revents) ||
        !           252:                    (POLLOUT & pfd[PFD_SENDER_OUT].revents)) {
        !           253:                        c = rsync_uploader(ul,
        !           254:                                &pfd[PFD_UPLOADER_IN].fd,
        !           255:                                sess, &pfd[PFD_SENDER_OUT].fd);
        !           256:                        if (c < 0) {
        !           257:                                ERRX1(sess, "rsync_uploader");
        !           258:                                goto out;
        !           259:                        }
        !           260:                }
        !           261:
        !           262:                /*
        !           263:                 * We need to run the downloader when we either have
        !           264:                 * read events from the sender or an asynchronous local
        !           265:                 * open is ready.
        !           266:                 * XXX: we don't disable PFD_SENDER_IN like with the
        !           267:                 * uploader because we might stop getting error
        !           268:                 * messages, which will otherwise clog up the pipes.
        !           269:                 */
        !           270:
        !           271:                if ((POLLIN & pfd[PFD_SENDER_IN].revents) ||
        !           272:                    (POLLIN & pfd[PFD_DOWNLOADER_IN].revents)) {
        !           273:                        c = rsync_downloader(dl, sess,
        !           274:                                &pfd[PFD_DOWNLOADER_IN].fd);
        !           275:                        if (c < 0) {
        !           276:                                ERRX1(sess, "rsync_downloader");
        !           277:                                goto out;
        !           278:                        } else if (0 == c) {
        !           279:                                assert(0 == phase);
        !           280:                                phase++;
        !           281:                                LOG2(sess, "%s: receiver ready "
        !           282:                                        "for phase 2 data", root);
        !           283:                                break;
        !           284:                        }
        !           285:
        !           286:                        /*
        !           287:                         * FIXME: if we have any errors during the
        !           288:                         * download, most notably files getting out of
        !           289:                         * sync between the send and the receiver, then
        !           290:                         * here we should bump our checksum length and
        !           291:                         * go into the second phase.
        !           292:                         */
        !           293:                }
        !           294:        }
        !           295:
        !           296:        /* Properly close us out by progressing through the phases. */
        !           297:
        !           298:        if (1 == phase) {
        !           299:                if ( ! io_write_int(sess, fdout, -1)) {
        !           300:                        ERRX1(sess, "io_write_int");
        !           301:                        goto out;
        !           302:                } else if ( ! io_read_int(sess, fdin, &ioerror)) {
        !           303:                        ERRX1(sess, "io_read_int");
        !           304:                        goto out;
        !           305:                } else if (-1 != ioerror) {
        !           306:                        ERRX(sess, "expected phase ack");
        !           307:                        goto out;
        !           308:                }
        !           309:        }
        !           310:
        !           311:        /*
        !           312:         * Now all of our transfers are complete, so we can fix up our
        !           313:         * directory permissions.
        !           314:         */
        !           315:
        !           316:        if ( ! rsync_uploader_tail(ul, sess)) {
        !           317:                ERRX1(sess, "rsync_uploader_tail");
        !           318:                goto out;
        !           319:        }
        !           320:
        !           321:        /* Process server statistics and say good-bye. */
        !           322:
        !           323:        if ( ! sess_stats_recv(sess, fdin)) {
        !           324:                ERRX1(sess, "sess_stats_recv");
        !           325:                goto out;
        !           326:        } else if ( ! io_write_int(sess, fdout, -1)) {
        !           327:                ERRX1(sess, "io_write_int");
        !           328:                goto out;
        !           329:        }
        !           330:
        !           331:        LOG2(sess, "receiver finished updating");
        !           332:        rc = 1;
        !           333: out:
        !           334:        if (-1 != dfd)
        !           335:                close(dfd);
        !           336:        upload_free(ul);
        !           337:        download_free(dl);
        !           338:        flist_free(fl, flsz);
        !           339:        flist_free(dfl, dflsz);
        !           340:        return rc;
        !           341: }