[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.7

1.7     ! florian     1: /*     $Id: receiver.c,v 1.6 2019/02/12 17:58:35 benno Exp $ */
1.1       benno       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: };
1.7     ! florian    42:
        !            43: int
        !            44: rsync_set_metadata(struct sess *sess, int newfile,
        !            45:        int fd, const struct flist *f, const char *path)
        !            46: {
        !            47:        uid_t            uid;
        !            48:        gid_t            gid;
        !            49:        struct timespec  tv[2];
        !            50:
        !            51:        /*
        !            52:         * Conditionally adjust identifiers.
        !            53:         * If we have an EPERM, report it but continue on: this just
        !            54:         * means that we're mapping into an unknown (or disallowed)
        !            55:         * group identifier.
        !            56:         */
        !            57:
        !            58:        if (sess->opts->preserve_gids ||
        !            59:            sess->opts->preserve_uids) {
        !            60:                uid = sess->opts->preserve_uids ? f->st.uid : -1;
        !            61:                gid = sess->opts->preserve_gids ? f->st.gid : -1;
        !            62:                if (fchown(fd, uid, gid) == -1) {
        !            63:                        if (errno != EPERM) {
        !            64:                                ERR(sess, "%s: fchown", path);
        !            65:                                return 0;
        !            66:                        }
        !            67:                        WARNX(sess, "%s: identity unknown or not available "
        !            68:                                "to user.group: %u.%u", f->path, uid, gid);
        !            69:                } else
        !            70:                        LOG4(sess, "%s: updated uid and/or gid", f->path);
        !            71:        }
        !            72:
        !            73:        /* Conditionally adjust file modification time. */
        !            74:
        !            75:        if (sess->opts->preserve_times) {
        !            76:                tv[0].tv_sec = time(NULL);
        !            77:                tv[0].tv_nsec = 0;
        !            78:                tv[1].tv_sec = f->st.mtime;
        !            79:                tv[1].tv_nsec = 0;
        !            80:                if (futimens(fd, tv) == -1) {
        !            81:                        ERR(sess, "%s: futimens", path);
        !            82:                        return 0;
        !            83:                }
        !            84:                LOG4(sess, "%s: updated date", f->path);
        !            85:        }
        !            86:
        !            87:        return 1;
        !            88: }
1.1       benno      89:
1.2       benno      90: /*
1.1       benno      91:  * Pledges: unveil, rpath, cpath, wpath, stdio, fattr.
                     92:  * Pledges (dry-run): -cpath, -wpath, -fattr.
                     93:  */
                     94: int
                     95: rsync_receiver(struct sess *sess,
                     96:        int fdin, int fdout, const char *root)
                     97: {
                     98:        struct flist    *fl = NULL, *dfl = NULL;
                     99:        size_t           i, flsz = 0, dflsz = 0, excl;
                    100:        char            *tofree;
                    101:        int              rc = 0, dfd = -1, phase = 0, c;
1.2       benno     102:        int32_t          ioerror;
1.1       benno     103:        struct pollfd    pfd[PFD__MAX];
                    104:        struct download *dl = NULL;
1.2       benno     105:        struct upload   *ul = NULL;
1.1       benno     106:        mode_t           oumask;
                    107:
1.6       benno     108:        if (pledge("stdio rpath wpath cpath fattr getpw unveil", NULL) == -1) {
1.1       benno     109:                ERR(sess, "pledge");
                    110:                goto out;
                    111:        }
                    112:
                    113:        /* Client sends zero-length exclusions. */
                    114:
1.4       deraadt   115:        if (!sess->opts->server &&
                    116:             !io_write_int(sess, fdout, 0)) {
1.1       benno     117:                ERRX1(sess, "io_write_int");
                    118:                goto out;
                    119:        }
                    120:
                    121:        if (sess->opts->server && sess->opts->del) {
1.4       deraadt   122:                if (!io_read_size(sess, fdin, &excl)) {
1.1       benno     123:                        ERRX1(sess, "io_read_size");
                    124:                        goto out;
                    125:                } else if (0 != excl) {
                    126:                        ERRX(sess, "exclusion list is non-empty");
                    127:                        goto out;
                    128:                }
                    129:        }
                    130:
                    131:        /*
                    132:         * Start by receiving the file list and our mystery number.
                    133:         * These we're going to be touching on our local system.
                    134:         */
                    135:
1.4       deraadt   136:        if (!flist_recv(sess, fdin, &fl, &flsz)) {
1.1       benno     137:                ERRX1(sess, "flist_recv");
                    138:                goto out;
1.2       benno     139:        }
                    140:
1.1       benno     141:        /* The IO error is sent after the file list. */
                    142:
1.4       deraadt   143:        if (!io_read_int(sess, fdin, &ioerror)) {
1.1       benno     144:                ERRX1(sess, "io_read_int");
                    145:                goto out;
                    146:        } else if (0 != ioerror) {
                    147:                ERRX1(sess, "io_error is non-zero");
                    148:                goto out;
                    149:        }
                    150:
1.5       deraadt   151:        if (flsz == 0 && !sess->opts->server) {
1.1       benno     152:                WARNX(sess, "receiver has empty file list: exiting");
                    153:                rc = 1;
                    154:                goto out;
1.4       deraadt   155:        } else if (!sess->opts->server)
1.1       benno     156:                LOG1(sess, "Transfer starting: %zu files", flsz);
                    157:
                    158:        LOG2(sess, "%s: receiver destination", root);
                    159:
                    160:        /*
                    161:         * Create the path for our destination directory, if we're not
                    162:         * in dry-run mode (which would otherwise crash w/the pledge).
                    163:         * This uses our current umask: we might set the permissions on
                    164:         * this directory in post_dir().
                    165:         */
                    166:
1.4       deraadt   167:        if (!sess->opts->dry_run) {
1.5       deraadt   168:                if ((tofree = strdup(root)) == NULL) {
1.1       benno     169:                        ERR(sess, "strdup");
                    170:                        goto out;
                    171:                } else if (mkpath(sess, tofree) < 0) {
                    172:                        ERRX1(sess, "%s: mkpath", root);
                    173:                        free(tofree);
                    174:                        goto out;
                    175:                }
                    176:                free(tofree);
                    177:        }
                    178:
                    179:        /*
                    180:         * Disable umask() so we can set permissions fully.
                    181:         * Then open the directory iff we're not in dry_run.
                    182:         */
                    183:
                    184:        oumask = umask(0);
                    185:
1.4       deraadt   186:        if (!sess->opts->dry_run) {
1.1       benno     187:                dfd = open(root, O_RDONLY | O_DIRECTORY, 0);
1.5       deraadt   188:                if (dfd == -1) {
1.1       benno     189:                        ERR(sess, "%s: open", root);
                    190:                        goto out;
                    191:                }
                    192:        }
                    193:
                    194:        /*
                    195:         * Begin by conditionally getting all files we have currently
                    196:         * available in our destination.
                    197:         * XXX: THIS IS A BUG IN OPENBSD 6.4.
                    198:         * For newer version of OpenBSD, this is safe to put after the
                    199:         * unveil.
                    200:         */
                    201:
1.2       benno     202:        if (sess->opts->del &&
1.1       benno     203:            sess->opts->recursive &&
1.4       deraadt   204:            !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
1.1       benno     205:                ERRX1(sess, "flist_gen_local");
                    206:                goto out;
                    207:        }
                    208:
                    209:        /*
                    210:         * Make our entire view of the file-system be limited to what's
                    211:         * in the root directory.
                    212:         * This prevents us from accidentally (or "under the influence")
                    213:         * writing into other parts of the file-system.
                    214:         */
                    215:
1.5       deraadt   216:        if (unveil(root, "rwc") == -1) {
1.1       benno     217:                ERR(sess, "%s: unveil", root);
                    218:                goto out;
1.5       deraadt   219:        } else if (unveil(NULL, NULL) == -1) {
1.1       benno     220:                ERR(sess, "%s: unveil", root);
                    221:                goto out;
                    222:        }
                    223:
                    224:        /* If we have a local set, go for the deletion. */
                    225:
1.4       deraadt   226:        if (!flist_del(sess, dfd, dfl, dflsz)) {
1.1       benno     227:                ERRX1(sess, "flist_del");
                    228:                goto out;
                    229:        }
                    230:
                    231:        /* Initialise poll events to listen from the sender. */
                    232:
                    233:        pfd[PFD_SENDER_IN].fd = fdin;
                    234:        pfd[PFD_UPLOADER_IN].fd = -1;
                    235:        pfd[PFD_DOWNLOADER_IN].fd = -1;
                    236:        pfd[PFD_SENDER_OUT].fd = fdout;
                    237:
                    238:        pfd[PFD_SENDER_IN].events = POLLIN;
                    239:        pfd[PFD_UPLOADER_IN].events = POLLIN;
                    240:        pfd[PFD_DOWNLOADER_IN].events = POLLIN;
                    241:        pfd[PFD_SENDER_OUT].events = POLLOUT;
                    242:
1.2       benno     243:        ul = upload_alloc(sess, dfd, fdout,
1.1       benno     244:                CSUM_LENGTH_PHASE1, fl, flsz, oumask);
1.5       deraadt   245:        if (ul == NULL) {
1.1       benno     246:                ERRX1(sess, "upload_alloc");
                    247:                goto out;
                    248:        }
                    249:
                    250:        dl = download_alloc(sess, fdin, fl, flsz, dfd);
1.5       deraadt   251:        if (dl == NULL) {
1.1       benno     252:                ERRX1(sess, "download_alloc");
                    253:                goto out;
                    254:        }
                    255:
                    256:        LOG2(sess, "%s: ready for phase 1 data", root);
                    257:
                    258:        for (;;) {
1.5       deraadt   259:                if ((c = poll(pfd, PFD__MAX, INFTIM)) == -1) {
1.1       benno     260:                        ERR(sess, "poll");
                    261:                        goto out;
1.2       benno     262:                }
1.1       benno     263:
1.2       benno     264:                for (i = 0; i < PFD__MAX; i++)
1.1       benno     265:                        if (pfd[i].revents & (POLLERR|POLLNVAL)) {
                    266:                                ERRX(sess, "poll: bad fd");
                    267:                                goto out;
                    268:                        } else if (pfd[i].revents & POLLHUP) {
                    269:                                ERRX(sess, "poll: hangup");
                    270:                                goto out;
                    271:                        }
                    272:
                    273:                /*
                    274:                 * If we have a read event and we're multiplexing, we
                    275:                 * might just have error messages in the pipe.
                    276:                 * It's important to flush these out so that we don't
                    277:                 * clog the pipe.
                    278:                 * Unset our polling status if there's nothing that
                    279:                 * remains in the pipe.
                    280:                 */
                    281:
                    282:                if (sess->mplex_reads &&
                    283:                    (POLLIN & pfd[PFD_SENDER_IN].revents)) {
1.4       deraadt   284:                        if (!io_read_flush(sess, fdin)) {
1.1       benno     285:                                ERRX1(sess, "io_read_flush");
                    286:                                goto out;
1.5       deraadt   287:                        } else if (sess->mplex_read_remain == 0)
1.1       benno     288:                                pfd[PFD_SENDER_IN].revents &= ~POLLIN;
                    289:                }
                    290:
                    291:
                    292:                /*
                    293:                 * We run the uploader if we have files left to examine
                    294:                 * (i < flsz) or if we have a file that we've opened and
                    295:                 * is read to mmap.
                    296:                 */
                    297:
                    298:                if ((POLLIN & pfd[PFD_UPLOADER_IN].revents) ||
                    299:                    (POLLOUT & pfd[PFD_SENDER_OUT].revents)) {
1.2       benno     300:                        c = rsync_uploader(ul,
                    301:                                &pfd[PFD_UPLOADER_IN].fd,
1.1       benno     302:                                sess, &pfd[PFD_SENDER_OUT].fd);
                    303:                        if (c < 0) {
                    304:                                ERRX1(sess, "rsync_uploader");
                    305:                                goto out;
                    306:                        }
                    307:                }
                    308:
1.2       benno     309:                /*
1.1       benno     310:                 * We need to run the downloader when we either have
                    311:                 * read events from the sender or an asynchronous local
                    312:                 * open is ready.
                    313:                 * XXX: we don't disable PFD_SENDER_IN like with the
                    314:                 * uploader because we might stop getting error
                    315:                 * messages, which will otherwise clog up the pipes.
                    316:                 */
                    317:
1.2       benno     318:                if ((POLLIN & pfd[PFD_SENDER_IN].revents) ||
1.1       benno     319:                    (POLLIN & pfd[PFD_DOWNLOADER_IN].revents)) {
1.2       benno     320:                        c = rsync_downloader(dl, sess,
1.1       benno     321:                                &pfd[PFD_DOWNLOADER_IN].fd);
                    322:                        if (c < 0) {
                    323:                                ERRX1(sess, "rsync_downloader");
                    324:                                goto out;
1.5       deraadt   325:                        } else if (c == 0) {
                    326:                                assert(phase == 0);
1.1       benno     327:                                phase++;
                    328:                                LOG2(sess, "%s: receiver ready "
                    329:                                        "for phase 2 data", root);
                    330:                                break;
                    331:                        }
                    332:
                    333:                        /*
                    334:                         * FIXME: if we have any errors during the
                    335:                         * download, most notably files getting out of
                    336:                         * sync between the send and the receiver, then
                    337:                         * here we should bump our checksum length and
                    338:                         * go into the second phase.
                    339:                         */
1.2       benno     340:                }
1.1       benno     341:        }
                    342:
                    343:        /* Properly close us out by progressing through the phases. */
                    344:
1.5       deraadt   345:        if (phase == 1) {
1.4       deraadt   346:                if (!io_write_int(sess, fdout, -1)) {
1.1       benno     347:                        ERRX1(sess, "io_write_int");
                    348:                        goto out;
1.4       deraadt   349:                } else if (!io_read_int(sess, fdin, &ioerror)) {
1.1       benno     350:                        ERRX1(sess, "io_read_int");
                    351:                        goto out;
1.5       deraadt   352:                } else if (ioerror != -1) {
1.1       benno     353:                        ERRX(sess, "expected phase ack");
                    354:                        goto out;
                    355:                }
                    356:        }
                    357:
                    358:        /*
                    359:         * Now all of our transfers are complete, so we can fix up our
                    360:         * directory permissions.
                    361:         */
                    362:
1.4       deraadt   363:        if (!rsync_uploader_tail(ul, sess)) {
1.1       benno     364:                ERRX1(sess, "rsync_uploader_tail");
                    365:                goto out;
                    366:        }
                    367:
                    368:        /* Process server statistics and say good-bye. */
                    369:
1.4       deraadt   370:        if (!sess_stats_recv(sess, fdin)) {
1.1       benno     371:                ERRX1(sess, "sess_stats_recv");
                    372:                goto out;
1.4       deraadt   373:        } else if (!io_write_int(sess, fdout, -1)) {
1.1       benno     374:                ERRX1(sess, "io_write_int");
                    375:                goto out;
                    376:        }
                    377:
                    378:        LOG2(sess, "receiver finished updating");
                    379:        rc = 1;
                    380: out:
1.5       deraadt   381:        if (dfd != -1)
1.1       benno     382:                close(dfd);
                    383:        upload_free(ul);
                    384:        download_free(dl);
                    385:        flist_free(fl, flsz);
                    386:        flist_free(dfl, dflsz);
                    387:        return rc;
                    388: }