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

1.29.2.1! benno       1: /*     $OpenBSD: receiver.c,v 1.29 2021/08/29 13:43:46 claudio Exp $ */
1.1       benno       2:
                      3: /*
                      4:  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
1.15      florian     5:  * Copyright (c) 2019 Florian Obser <florian@openbsd.org>
1.1       benno       6:  *
                      7:  * Permission to use, copy, modify, and distribute this software for any
                      8:  * purpose with or without fee is hereby granted, provided that the above
                      9:  * copyright notice and this permission notice appear in all copies.
                     10:  *
                     11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     15:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     16:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     17:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19: #include <sys/mman.h>
                     20: #include <sys/stat.h>
                     21:
                     22: #include <assert.h>
1.27      claudio    23: #include <err.h>
1.1       benno      24: #include <errno.h>
                     25: #include <fcntl.h>
                     26: #include <inttypes.h>
                     27: #include <math.h>
                     28: #include <poll.h>
                     29: #include <stdio.h>
                     30: #include <stdlib.h>
                     31: #include <string.h>
                     32: #include <time.h>
                     33: #include <unistd.h>
                     34:
                     35: #include "extern.h"
                     36:
                     37: enum   pfdt {
                     38:        PFD_SENDER_IN = 0, /* input from the sender */
                     39:        PFD_UPLOADER_IN, /* uploader input from a local file */
                     40:        PFD_DOWNLOADER_IN, /* downloader input from a local file */
                     41:        PFD_SENDER_OUT, /* output to the sender */
                     42:        PFD__MAX
                     43: };
1.7       florian    44:
                     45: int
                     46: rsync_set_metadata(struct sess *sess, int newfile,
                     47:        int fd, const struct flist *f, const char *path)
                     48: {
1.8       florian    49:        uid_t            uid = (uid_t)-1;
                     50:        gid_t            gid = (gid_t)-1;
1.22      deraadt    51:        mode_t           mode;
1.20      cheloha    52:        struct timespec  ts[2];
1.7       florian    53:
1.22      deraadt    54:        /* Conditionally adjust file modification time. */
                     55:
                     56:        if (sess->opts->preserve_times) {
                     57:                ts[0].tv_nsec = UTIME_NOW;
                     58:                ts[1].tv_sec = f->st.mtime;
                     59:                ts[1].tv_nsec = 0;
                     60:                if (futimens(fd, ts) == -1) {
1.23      benno      61:                        ERR("%s: futimens", path);
1.22      deraadt    62:                        return 0;
                     63:                }
1.23      benno      64:                LOG4("%s: updated date", f->path);
1.22      deraadt    65:        }
                     66:
1.7       florian    67:        /*
                     68:         * Conditionally adjust identifiers.
                     69:         * If we have an EPERM, report it but continue on: this just
                     70:         * means that we're mapping into an unknown (or disallowed)
                     71:         * group identifier.
                     72:         */
1.8       florian    73:        if (getuid() == 0 && sess->opts->preserve_uids)
                     74:                uid = f->st.uid;
                     75:        if (sess->opts->preserve_gids)
                     76:                gid = f->st.gid;
                     77:
1.22      deraadt    78:        mode = f->st.mode;
1.8       florian    79:        if (uid != (uid_t)-1 || gid != (gid_t)-1) {
1.7       florian    80:                if (fchown(fd, uid, gid) == -1) {
                     81:                        if (errno != EPERM) {
1.23      benno      82:                                ERR("%s: fchown", path);
1.7       florian    83:                                return 0;
                     84:                        }
1.22      deraadt    85:                        if (getuid() == 0)
1.23      benno      86:                                WARNX("%s: identity unknown or not available "
1.22      deraadt    87:                                    "to user.group: %u.%u", f->path, uid, gid);
1.7       florian    88:                } else
1.23      benno      89:                        LOG4("%s: updated uid and/or gid", f->path);
1.22      deraadt    90:                mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
1.13      florian    91:        }
                     92:
                     93:        /* Conditionally adjust file permissions. */
                     94:
                     95:        if (newfile || sess->opts->preserve_perms) {
1.22      deraadt    96:                if (fchmod(fd, mode) == -1) {
1.23      benno      97:                        ERR("%s: fchmod", path);
1.13      florian    98:                        return 0;
                     99:                }
1.23      benno     100:                LOG4("%s: updated permissions", f->path);
1.7       florian   101:        }
                    102:
                    103:        return 1;
                    104: }
1.1       benno     105:
1.14      florian   106: int
                    107: rsync_set_metadata_at(struct sess *sess, int newfile, int rootfd,
                    108:        const struct flist *f, const char *path)
                    109: {
                    110:        uid_t            uid = (uid_t)-1;
                    111:        gid_t            gid = (gid_t)-1;
1.22      deraadt   112:        mode_t           mode;
1.20      cheloha   113:        struct timespec  ts[2];
1.14      florian   114:
1.22      deraadt   115:        /* Conditionally adjust file modification time. */
                    116:
                    117:        if (sess->opts->preserve_times) {
                    118:                ts[0].tv_nsec = UTIME_NOW;
                    119:                ts[1].tv_sec = f->st.mtime;
                    120:                ts[1].tv_nsec = 0;
                    121:                if (utimensat(rootfd, path, ts, AT_SYMLINK_NOFOLLOW) == -1) {
1.23      benno     122:                        ERR("%s: utimensat", path);
1.22      deraadt   123:                        return 0;
                    124:                }
1.23      benno     125:                LOG4("%s: updated date", f->path);
1.22      deraadt   126:        }
                    127:
1.14      florian   128:        /*
                    129:         * Conditionally adjust identifiers.
                    130:         * If we have an EPERM, report it but continue on: this just
                    131:         * means that we're mapping into an unknown (or disallowed)
                    132:         * group identifier.
                    133:         */
                    134:        if (getuid() == 0 && sess->opts->preserve_uids)
                    135:                uid = f->st.uid;
                    136:        if (sess->opts->preserve_gids)
                    137:                gid = f->st.gid;
                    138:
1.22      deraadt   139:        mode = f->st.mode;
1.14      florian   140:        if (uid != (uid_t)-1 || gid != (gid_t)-1) {
1.22      deraadt   141:                if (fchownat(rootfd, path, uid, gid, AT_SYMLINK_NOFOLLOW) == -1) {
1.14      florian   142:                        if (errno != EPERM) {
1.23      benno     143:                                ERR("%s: fchownat", path);
1.14      florian   144:                                return 0;
                    145:                        }
1.22      deraadt   146:                        if (getuid() == 0)
1.23      benno     147:                                WARNX("%s: identity unknown or not available "
1.22      deraadt   148:                                    "to user.group: %u.%u", f->path, uid, gid);
1.14      florian   149:                } else
1.23      benno     150:                        LOG4("%s: updated uid and/or gid", f->path);
1.22      deraadt   151:                mode &= ~(S_ISTXT | S_ISUID | S_ISGID);
1.14      florian   152:        }
                    153:
                    154:        /* Conditionally adjust file permissions. */
                    155:
                    156:        if (newfile || sess->opts->preserve_perms) {
1.22      deraadt   157:                if (fchmodat(rootfd, path, mode, AT_SYMLINK_NOFOLLOW) == -1) {
1.23      benno     158:                        ERR("%s: fchmodat", path);
1.14      florian   159:                        return 0;
                    160:                }
1.23      benno     161:                LOG4("%s: updated permissions", f->path);
1.14      florian   162:        }
                    163:
                    164:        return 1;
                    165: }
                    166:
1.2       benno     167: /*
1.14      florian   168:  * Pledges: unveil, unix, rpath, cpath, wpath, stdio, fattr, chown.
                    169:  * Pledges (dry-run): -unix, -cpath, -wpath, -fattr, -chown.
1.1       benno     170:  */
                    171: int
1.9       deraadt   172: rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
1.1       benno     173: {
                    174:        struct flist    *fl = NULL, *dfl = NULL;
1.29      claudio   175:        size_t           i, flsz = 0, dflsz = 0;
1.1       benno     176:        char            *tofree;
                    177:        int              rc = 0, dfd = -1, phase = 0, c;
1.2       benno     178:        int32_t          ioerror;
1.1       benno     179:        struct pollfd    pfd[PFD__MAX];
                    180:        struct download *dl = NULL;
1.2       benno     181:        struct upload   *ul = NULL;
1.1       benno     182:        mode_t           oumask;
                    183:
1.27      claudio   184:        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1)
                    185:                err(ERR_IPC, "pledge");
1.1       benno     186:
1.29.2.1! benno     187:        /*
        !           188:         * Create the path for our destination directory, if we're not
        !           189:         * in dry-run mode (which would otherwise crash w/the pledge).
        !           190:         * This uses our current umask: we might set the permissions on
        !           191:         * this directory in post_dir().
        !           192:         */
        !           193:
        !           194:        if (!sess->opts->dry_run) {
        !           195:                if ((tofree = strdup(root)) == NULL)
        !           196:                        err(ERR_NOMEM, NULL);
        !           197:                if (mkpath(tofree) < 0)
        !           198:                        err(ERR_FILE_IO, "%s: mkpath", tofree);
        !           199:                free(tofree);
        !           200:        }
        !           201:
        !           202:        /*
        !           203:         * Make our entire view of the file-system be limited to what's
        !           204:         * in the root directory.
        !           205:         * This prevents us from accidentally (or "under the influence")
        !           206:         * writing into other parts of the file-system.
        !           207:         */
        !           208:        if (sess->opts->basedir[0]) {
        !           209:                /*
        !           210:                 * XXX just unveil everything for read
        !           211:                 * Could unveil each basedir or maybe a common path
        !           212:                 * also the fact that relative path are relative to the
        !           213:                 * root does not help.
        !           214:                 */
        !           215:                if (unveil("/", "r") == -1)
        !           216:                        err(ERR_IPC, "%s: unveil", root);
        !           217:        }
        !           218:
        !           219:        if (unveil(root, "rwc") == -1)
        !           220:                err(ERR_IPC, "%s: unveil", root);
        !           221:
        !           222:        if (unveil(NULL, NULL) == -1)
        !           223:                err(ERR_IPC, "unveil");
        !           224:
1.29      claudio   225:        /* Client sends exclusions. */
                    226:        if (!sess->opts->server)
                    227:                send_rules(sess, fdout);
                    228:
                    229:        /* Server receives exclusions if delete is on. */
                    230:        if (sess->opts->server && sess->opts->del)
                    231:                recv_rules(sess, fdin);
1.1       benno     232:
                    233:        /*
                    234:         * Start by receiving the file list and our mystery number.
                    235:         * These we're going to be touching on our local system.
                    236:         */
                    237:
1.4       deraadt   238:        if (!flist_recv(sess, fdin, &fl, &flsz)) {
1.23      benno     239:                ERRX1("flist_recv");
1.1       benno     240:                goto out;
1.2       benno     241:        }
                    242:
1.1       benno     243:        /* The IO error is sent after the file list. */
                    244:
1.4       deraadt   245:        if (!io_read_int(sess, fdin, &ioerror)) {
1.23      benno     246:                ERRX1("io_read_int");
1.1       benno     247:                goto out;
1.19      benno     248:        } else if (ioerror != 0) {
1.23      benno     249:                ERRX1("io_error is non-zero");
1.1       benno     250:                goto out;
                    251:        }
                    252:
1.5       deraadt   253:        if (flsz == 0 && !sess->opts->server) {
1.23      benno     254:                WARNX("receiver has empty file list: exiting");
1.1       benno     255:                rc = 1;
                    256:                goto out;
1.4       deraadt   257:        } else if (!sess->opts->server)
1.23      benno     258:                LOG1("Transfer starting: %zu files", flsz);
1.1       benno     259:
1.23      benno     260:        LOG2("%s: receiver destination", root);
1.1       benno     261:
                    262:        /*
                    263:         * Disable umask() so we can set permissions fully.
                    264:         * Then open the directory iff we're not in dry_run.
                    265:         */
                    266:
                    267:        oumask = umask(0);
                    268:
1.4       deraadt   269:        if (!sess->opts->dry_run) {
1.29.2.1! benno     270:                dfd = open(root, O_RDONLY | O_DIRECTORY);
1.27      claudio   271:                if (dfd == -1)
                    272:                        err(ERR_FILE_IO, "%s: open", root);
1.1       benno     273:        }
                    274:
                    275:        /*
                    276:         * Begin by conditionally getting all files we have currently
                    277:         * available in our destination.
                    278:         */
                    279:
1.2       benno     280:        if (sess->opts->del &&
1.1       benno     281:            sess->opts->recursive &&
1.4       deraadt   282:            !flist_gen_dels(sess, root, &dfl, &dflsz, fl, flsz)) {
1.23      benno     283:                ERRX1("flist_gen_local");
1.1       benno     284:                goto out;
                    285:        }
                    286:
                    287:        /* If we have a local set, go for the deletion. */
                    288:
1.4       deraadt   289:        if (!flist_del(sess, dfd, dfl, dflsz)) {
1.23      benno     290:                ERRX1("flist_del");
1.1       benno     291:                goto out;
                    292:        }
                    293:
                    294:        /* Initialise poll events to listen from the sender. */
                    295:
                    296:        pfd[PFD_SENDER_IN].fd = fdin;
                    297:        pfd[PFD_UPLOADER_IN].fd = -1;
                    298:        pfd[PFD_DOWNLOADER_IN].fd = -1;
                    299:        pfd[PFD_SENDER_OUT].fd = fdout;
                    300:
                    301:        pfd[PFD_SENDER_IN].events = POLLIN;
                    302:        pfd[PFD_UPLOADER_IN].events = POLLIN;
                    303:        pfd[PFD_DOWNLOADER_IN].events = POLLIN;
                    304:        pfd[PFD_SENDER_OUT].events = POLLOUT;
                    305:
1.24      benno     306:        ul = upload_alloc(root, dfd, fdout, CSUM_LENGTH_PHASE1, fl, flsz,
                    307:            oumask);
                    308:
1.5       deraadt   309:        if (ul == NULL) {
1.23      benno     310:                ERRX1("upload_alloc");
1.1       benno     311:                goto out;
                    312:        }
                    313:
                    314:        dl = download_alloc(sess, fdin, fl, flsz, dfd);
1.5       deraadt   315:        if (dl == NULL) {
1.23      benno     316:                ERRX1("download_alloc");
1.1       benno     317:                goto out;
                    318:        }
                    319:
1.23      benno     320:        LOG2("%s: ready for phase 1 data", root);
1.1       benno     321:
                    322:        for (;;) {
1.25      claudio   323:                if ((c = poll(pfd, PFD__MAX, poll_timeout)) == -1) {
1.23      benno     324:                        ERR("poll");
1.16      florian   325:                        goto out;
                    326:                } else if (c == 0) {
1.23      benno     327:                        ERRX("poll: timeout");
1.1       benno     328:                        goto out;
1.2       benno     329:                }
1.1       benno     330:
1.2       benno     331:                for (i = 0; i < PFD__MAX; i++)
1.1       benno     332:                        if (pfd[i].revents & (POLLERR|POLLNVAL)) {
1.23      benno     333:                                ERRX("poll: bad fd");
1.1       benno     334:                                goto out;
                    335:                        } else if (pfd[i].revents & POLLHUP) {
1.23      benno     336:                                ERRX("poll: hangup");
1.1       benno     337:                                goto out;
                    338:                        }
                    339:
                    340:                /*
                    341:                 * If we have a read event and we're multiplexing, we
                    342:                 * might just have error messages in the pipe.
                    343:                 * It's important to flush these out so that we don't
                    344:                 * clog the pipe.
                    345:                 * Unset our polling status if there's nothing that
                    346:                 * remains in the pipe.
                    347:                 */
                    348:
                    349:                if (sess->mplex_reads &&
1.26      claudio   350:                    (pfd[PFD_SENDER_IN].revents & POLLIN)) {
1.4       deraadt   351:                        if (!io_read_flush(sess, fdin)) {
1.23      benno     352:                                ERRX1("io_read_flush");
1.1       benno     353:                                goto out;
1.5       deraadt   354:                        } else if (sess->mplex_read_remain == 0)
1.1       benno     355:                                pfd[PFD_SENDER_IN].revents &= ~POLLIN;
                    356:                }
                    357:
                    358:
                    359:                /*
                    360:                 * We run the uploader if we have files left to examine
                    361:                 * (i < flsz) or if we have a file that we've opened and
                    362:                 * is read to mmap.
                    363:                 */
                    364:
1.26      claudio   365:                if ((pfd[PFD_UPLOADER_IN].revents & POLLIN) ||
                    366:                    (pfd[PFD_SENDER_OUT].revents & POLLOUT)) {
1.2       benno     367:                        c = rsync_uploader(ul,
                    368:                                &pfd[PFD_UPLOADER_IN].fd,
1.1       benno     369:                                sess, &pfd[PFD_SENDER_OUT].fd);
                    370:                        if (c < 0) {
1.23      benno     371:                                ERRX1("rsync_uploader");
1.1       benno     372:                                goto out;
                    373:                        }
                    374:                }
                    375:
1.2       benno     376:                /*
1.1       benno     377:                 * We need to run the downloader when we either have
                    378:                 * read events from the sender or an asynchronous local
                    379:                 * open is ready.
                    380:                 * XXX: we don't disable PFD_SENDER_IN like with the
                    381:                 * uploader because we might stop getting error
                    382:                 * messages, which will otherwise clog up the pipes.
                    383:                 */
                    384:
1.26      claudio   385:                if ((pfd[PFD_SENDER_IN].revents & POLLIN) ||
                    386:                    (pfd[PFD_DOWNLOADER_IN].revents & POLLIN)) {
1.2       benno     387:                        c = rsync_downloader(dl, sess,
1.1       benno     388:                                &pfd[PFD_DOWNLOADER_IN].fd);
                    389:                        if (c < 0) {
1.23      benno     390:                                ERRX1("rsync_downloader");
1.1       benno     391:                                goto out;
1.5       deraadt   392:                        } else if (c == 0) {
                    393:                                assert(phase == 0);
1.1       benno     394:                                phase++;
1.23      benno     395:                                LOG2("%s: receiver ready for phase 2 data", root);
1.1       benno     396:                                break;
                    397:                        }
                    398:
                    399:                        /*
                    400:                         * FIXME: if we have any errors during the
                    401:                         * download, most notably files getting out of
                    402:                         * sync between the send and the receiver, then
                    403:                         * here we should bump our checksum length and
                    404:                         * go into the second phase.
                    405:                         */
1.2       benno     406:                }
1.1       benno     407:        }
                    408:
                    409:        /* Properly close us out by progressing through the phases. */
                    410:
1.5       deraadt   411:        if (phase == 1) {
1.4       deraadt   412:                if (!io_write_int(sess, fdout, -1)) {
1.23      benno     413:                        ERRX1("io_write_int");
1.1       benno     414:                        goto out;
1.26      claudio   415:                }
                    416:                if (!io_read_int(sess, fdin, &ioerror)) {
1.23      benno     417:                        ERRX1("io_read_int");
1.1       benno     418:                        goto out;
1.26      claudio   419:                }
                    420:                if (ioerror != -1) {
1.23      benno     421:                        ERRX("expected phase ack");
1.1       benno     422:                        goto out;
                    423:                }
                    424:        }
                    425:
                    426:        /*
                    427:         * Now all of our transfers are complete, so we can fix up our
                    428:         * directory permissions.
                    429:         */
                    430:
1.4       deraadt   431:        if (!rsync_uploader_tail(ul, sess)) {
1.23      benno     432:                ERRX1("rsync_uploader_tail");
1.1       benno     433:                goto out;
                    434:        }
                    435:
                    436:        /* Process server statistics and say good-bye. */
                    437:
1.4       deraadt   438:        if (!sess_stats_recv(sess, fdin)) {
1.23      benno     439:                ERRX1("sess_stats_recv");
1.1       benno     440:                goto out;
1.26      claudio   441:        }
                    442:        if (!io_write_int(sess, fdout, -1)) {
1.23      benno     443:                ERRX1("io_write_int");
1.1       benno     444:                goto out;
                    445:        }
                    446:
1.23      benno     447:        LOG2("receiver finished updating");
1.1       benno     448:        rc = 1;
                    449: out:
1.5       deraadt   450:        if (dfd != -1)
1.1       benno     451:                close(dfd);
                    452:        upload_free(ul);
                    453:        download_free(dl);
                    454:        flist_free(fl, flsz);
                    455:        flist_free(dfl, dflsz);
                    456:        return rc;
                    457: }