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

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