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

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

1.1     ! benno       1: /*     $Id$ */
        !             2: /*
        !             3:  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
        !             4:  *
        !             5:  * Permission to use, copy, modify, and distribute this software for any
        !             6:  * purpose with or without fee is hereby granted, provided that the above
        !             7:  * copyright notice and this permission notice appear in all copies.
        !             8:  *
        !             9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            16:  */
        !            17: #include <sys/param.h>
        !            18: #include <sys/stat.h>
        !            19:
        !            20: #include <assert.h>
        !            21: #include <errno.h>
        !            22: #include <fcntl.h>
        !            23: #include <inttypes.h>
        !            24: #include <fts.h>
        !            25: #include <search.h>
        !            26: #include <stdio.h>
        !            27: #include <stdlib.h>
        !            28: #include <string.h>
        !            29: #include <unistd.h>
        !            30:
        !            31: #include "extern.h"
        !            32:
        !            33: /*
        !            34:  * We allocate our file list in chunk sizes so as not to do it one by
        !            35:  * one.
        !            36:  * Preferrably we get one or two allocation.
        !            37:  */
        !            38: #define        FLIST_CHUNK_SIZE (1024)
        !            39:
        !            40: /*
        !            41:  * These flags are part of the rsync protocol.
        !            42:  * They are sent as the first byte for a file transmission and encode
        !            43:  * information that affects subsequent transmissions.
        !            44:  */
        !            45: #define FLIST_MODE_SAME  0x0002 /* mode is repeat */
        !            46: #define        FLIST_NAME_SAME  0x0020 /* name is repeat */
        !            47: #define FLIST_NAME_LONG         0x0040 /* name >255 bytes */
        !            48: #define FLIST_TIME_SAME  0x0080 /* time is repeat */
        !            49:
        !            50: /*
        !            51:  * Requied way to sort a filename list.
        !            52:  */
        !            53: static int
        !            54: flist_cmp(const void *p1, const void *p2)
        !            55: {
        !            56:        const struct flist *f1 = p1, *f2 = p2;
        !            57:
        !            58:        return strcmp(f1->wpath, f2->wpath);
        !            59: }
        !            60:
        !            61: /*
        !            62:  * Deduplicate our file list (which may be zero-length).
        !            63:  * Returns zero on failure, non-zero on success.
        !            64:  */
        !            65: static int
        !            66: flist_dedupe(struct sess *sess, struct flist **fl, size_t *sz)
        !            67: {
        !            68:        size_t           i, j;
        !            69:        struct flist    *new;
        !            70:        struct flist    *f, *fnext;
        !            71:
        !            72:        if (0 == *sz)
        !            73:                return 1;
        !            74:
        !            75:        /* Create a new buffer, "new", and copy. */
        !            76:
        !            77:        new = calloc(*sz, sizeof(struct flist));
        !            78:        if (NULL == new) {
        !            79:                ERR(sess, "calloc");
        !            80:                return 0;
        !            81:        }
        !            82:
        !            83:        for (i = j = 0; i < *sz - 1; i++) {
        !            84:                f = &(*fl)[i];
        !            85:                fnext = &(*fl)[i + 1];
        !            86:
        !            87:                if (strcmp(f->wpath, fnext->wpath)) {
        !            88:                        new[j++] = *f;
        !            89:                        continue;
        !            90:                }
        !            91:
        !            92:                /*
        !            93:                 * Our working (destination) paths are the same.
        !            94:                 * If the actual file is the same (as given on the
        !            95:                 * command-line), then we can just discard the first.
        !            96:                 * Otherwise, we need to bail out: it means we have two
        !            97:                 * different files with the relative path on the
        !            98:                 * destination side.
        !            99:                 */
        !           100:
        !           101:                if (0 == strcmp(f->path, fnext->path)) {
        !           102:                        new[j++] = *f;
        !           103:                        i++;
        !           104:                        WARNX(sess, "%s: duplicate path: %s",
        !           105:                                f->wpath, f->path);
        !           106:                        free(fnext->path);
        !           107:                        free(fnext->link);
        !           108:                        fnext->path = fnext->link = NULL;
        !           109:                        continue;
        !           110:                }
        !           111:
        !           112:                ERRX(sess, "%s: duplicate working path for "
        !           113:                        "possibly different file: %s, %s",
        !           114:                        f->wpath, f->path, fnext->path);
        !           115:                free(new);
        !           116:                return 0;
        !           117:        }
        !           118:
        !           119:        /* Don't forget the last entry. */
        !           120:
        !           121:        if (i == *sz - 1)
        !           122:                new[j++] = (*fl)[i];
        !           123:
        !           124:        /*
        !           125:         * Reassign to the deduplicated array.
        !           126:         * If we started out with *sz > 0, which we check for at the
        !           127:         * beginning, then we'll always continue having *sz > 0.
        !           128:         */
        !           129:
        !           130:        free(*fl);
        !           131:        *fl = new;
        !           132:        *sz = j;
        !           133:        assert(*sz);
        !           134:        return 1;
        !           135: }
        !           136:
        !           137: /*
        !           138:  * We're now going to find our top-level directories.
        !           139:  * This only applies to recursive mode.
        !           140:  * If we have the first element as the ".", then that's the "top
        !           141:  * directory" of our transfer.
        !           142:  * Otherwise, mark up all top-level directories in the set.
        !           143:  */
        !           144: static void
        !           145: flist_topdirs(struct sess *sess, struct flist *fl, size_t flsz)
        !           146: {
        !           147:        size_t           i;
        !           148:        const char      *cp;
        !           149:
        !           150:        if ( ! sess->opts->recursive)
        !           151:                return;
        !           152:
        !           153:        if (flsz && strcmp(fl[0].wpath, ".")) {
        !           154:                for (i = 0; i < flsz; i++) {
        !           155:                        if ( ! S_ISDIR(fl[i].st.mode))
        !           156:                                continue;
        !           157:                        cp = strchr(fl[i].wpath, '/');
        !           158:                        if (NULL != cp && '\0' != cp[1])
        !           159:                                continue;
        !           160:                        fl[i].st.flags |= FLSTAT_TOP_DIR;
        !           161:                        LOG4(sess, "%s: top-level", fl[i].wpath);
        !           162:                }
        !           163:        } else if (flsz) {
        !           164:                fl[0].st.flags |= FLSTAT_TOP_DIR;
        !           165:                LOG4(sess, "%s: top-level", fl[0].wpath);
        !           166:        }
        !           167: }
        !           168:
        !           169: /*
        !           170:  * Filter through the fts() file information.
        !           171:  * We want directories (pre-order), regular files, and symlinks.
        !           172:  * Everything else is skipped and possibly warned about.
        !           173:  * Return zero to skip, non-zero to examine.
        !           174:  */
        !           175: static int
        !           176: flist_fts_check(struct sess *sess, FTSENT *ent)
        !           177: {
        !           178:
        !           179:        if (FTS_F == ent->fts_info ||
        !           180:            FTS_D == ent->fts_info ||
        !           181:            FTS_SL == ent->fts_info ||
        !           182:            FTS_SLNONE == ent->fts_info)
        !           183:                return 1;
        !           184:
        !           185:        if (FTS_DC == ent->fts_info) {
        !           186:                WARNX(sess, "%s: directory cycle", ent->fts_path);
        !           187:        } else if (FTS_DNR == ent->fts_info) {
        !           188:                errno = ent->fts_errno;
        !           189:                WARN(sess, "%s: unreadable directory", ent->fts_path);
        !           190:        } else if (FTS_DOT == ent->fts_info) {
        !           191:                WARNX(sess, "%s: skipping dot-file", ent->fts_path);
        !           192:        } else if (FTS_ERR == ent->fts_info) {
        !           193:                errno = ent->fts_errno;
        !           194:                WARN(sess, "%s", ent->fts_path);
        !           195:        } else if (FTS_DEFAULT == ent->fts_info) {
        !           196:                WARNX(sess, "%s: skipping special", ent->fts_path);
        !           197:        } else if (FTS_NS == ent->fts_info) {
        !           198:                errno = ent->fts_errno;
        !           199:                WARN(sess, "%s: could not stat", ent->fts_path);
        !           200:        }
        !           201:
        !           202:        return 0;
        !           203: }
        !           204:
        !           205: /*
        !           206:  * Copy necessary elements in "st" into the fields of "f".
        !           207:  */
        !           208: static void
        !           209: flist_copy_stat(struct flist *f, const struct stat *st)
        !           210: {
        !           211:
        !           212:        f->st.mode = st->st_mode;
        !           213:        f->st.uid = st->st_uid;
        !           214:        f->st.gid = st->st_gid;
        !           215:        f->st.size = st->st_size;
        !           216:        f->st.mtime = st->st_mtime;
        !           217: }
        !           218:
        !           219: void
        !           220: flist_free(struct flist *f, size_t sz)
        !           221: {
        !           222:        size_t   i;
        !           223:
        !           224:        if (NULL == f)
        !           225:                return;
        !           226:
        !           227:        for (i = 0; i < sz; i++) {
        !           228:                free(f[i].path);
        !           229:                free(f[i].link);
        !           230:        }
        !           231:        free(f);
        !           232: }
        !           233:
        !           234: /*
        !           235:  * Serialise our file list (which may be zero-length) to the wire.
        !           236:  * Makes sure that the receiver isn't going to block on sending us
        !           237:  * return messages on the log channel.
        !           238:  * Return zero on failure, non-zero on success.
        !           239:  */
        !           240: int
        !           241: flist_send(struct sess *sess, int fdin,
        !           242:        int fdout, const struct flist *fl, size_t flsz)
        !           243: {
        !           244:        size_t           i, fnlen;
        !           245:        uint8_t          flag;
        !           246:        const struct flist *f;
        !           247:        const char      *fn;
        !           248:
        !           249:        /* Double-check that we've no pending multiplexed data. */
        !           250:
        !           251:        LOG2(sess, "sending file metadata list: %zu", flsz);
        !           252:
        !           253:        for (i = 0; i < flsz; i++) {
        !           254:                f = &fl[i];
        !           255:                fn = f->wpath;
        !           256:                fnlen = strlen(f->wpath);
        !           257:                assert(fnlen > 0);
        !           258:
        !           259:                /*
        !           260:                 * If applicable, unclog the read buffer.
        !           261:                 * This happens when the receiver has a lot of log
        !           262:                 * messages and all we're doing is sending our file list
        !           263:                 * without checking for messages.
        !           264:                 */
        !           265:
        !           266:                if (sess->mplex_reads &&
        !           267:                    io_read_check(sess, fdin) &&
        !           268:                     ! io_read_flush(sess, fdin)) {
        !           269:                        ERRX1(sess, "io_read_flush");
        !           270:                        return 0;
        !           271:                }
        !           272:
        !           273:                /*
        !           274:                 * For ease, make all of our filenames be "long"
        !           275:                 * regardless their actual length.
        !           276:                 * This also makes sure that we don't transmit a zero
        !           277:                 * byte unintentionally.
        !           278:                 */
        !           279:
        !           280:                flag = FLIST_NAME_LONG;
        !           281:
        !           282:                LOG3(sess, "%s: sending file metadata: "
        !           283:                        "size %jd, mtime %jd, mode %o",
        !           284:                        fn, (intmax_t)f->st.size,
        !           285:                        (intmax_t)f->st.mtime, f->st.mode);
        !           286:
        !           287:                /* Now write to the wire. */
        !           288:                /* FIXME: buffer this. */
        !           289:
        !           290:                if ( ! io_write_byte(sess, fdout, flag)) {
        !           291:                        ERRX1(sess, "io_write_byte");
        !           292:                        return 0;
        !           293:                } else if ( ! io_write_int(sess, fdout, fnlen)) {
        !           294:                        ERRX1(sess, "io_write_int");
        !           295:                        return 0;
        !           296:                } else if ( ! io_write_buf(sess, fdout, fn, fnlen)) {
        !           297:                        ERRX1(sess, "io_write_buf");
        !           298:                        return 0;
        !           299:                } else if ( ! io_write_long(sess, fdout, f->st.size)) {
        !           300:                        ERRX1(sess, "io_write_long");
        !           301:                        return 0;
        !           302:                } else if ( ! io_write_int(sess, fdout, f->st.mtime)) {
        !           303:                        ERRX1(sess, "io_write_int");
        !           304:                        return 0;
        !           305:                } else if ( ! io_write_int(sess, fdout, f->st.mode)) {
        !           306:                        ERRX1(sess, "io_write_int");
        !           307:                        return 0;
        !           308:                }
        !           309:
        !           310:                /* Optional link information. */
        !           311:
        !           312:                if (S_ISLNK(f->st.mode) &&
        !           313:                    sess->opts->preserve_links) {
        !           314:                        fn = f->link;
        !           315:                        fnlen = strlen(f->link);
        !           316:                        if ( ! io_write_int(sess, fdout, fnlen)) {
        !           317:                                ERRX1(sess, "io_write_int");
        !           318:                                return 0;
        !           319:                        }
        !           320:                        if ( ! io_write_buf(sess, fdout, fn, fnlen)) {
        !           321:                                ERRX1(sess, "io_write_int");
        !           322:                                return 0;
        !           323:                        }
        !           324:                }
        !           325:
        !           326:                if (S_ISREG(f->st.mode))
        !           327:                        sess->total_size += f->st.size;
        !           328:        }
        !           329:
        !           330:        if ( ! io_write_byte(sess, fdout, 0)) {
        !           331:                ERRX1(sess, "io_write_byte");
        !           332:                return 0;
        !           333:        }
        !           334:
        !           335:        return 1;
        !           336: }
        !           337:
        !           338: /*
        !           339:  * Read the filename of a file list.
        !           340:  * This is the most expensive part of the file list transfer, so a lot
        !           341:  * of attention has gone into transmitting as little as possible.
        !           342:  * Micro-optimisation, but whatever.
        !           343:  * Fills in "f" with the full path on success.
        !           344:  * Returns zero on failure, non-zero on success.
        !           345:  */
        !           346: static int
        !           347: flist_recv_name(struct sess *sess, int fd,
        !           348:        struct flist *f, uint8_t flags, char last[MAXPATHLEN])
        !           349: {
        !           350:        uint8_t          bval;
        !           351:        size_t           partial = 0;
        !           352:        size_t           pathlen = 0, len;
        !           353:
        !           354:        /*
        !           355:         * Read our filename.
        !           356:         * If we have FLIST_NAME_SAME, we inherit some of the last
        !           357:         * transmitted name.
        !           358:         * If we have FLIST_NAME_LONG, then the string length is greater
        !           359:         * than byte-size.
        !           360:         */
        !           361:
        !           362:        if (FLIST_NAME_SAME & flags) {
        !           363:                if ( ! io_read_byte(sess, fd, &bval)) {
        !           364:                        ERRX1(sess, "io_read_byte");
        !           365:                        return 0;
        !           366:                }
        !           367:                partial = bval;
        !           368:        }
        !           369:
        !           370:        /* Get the (possibly-remaining) filename length. */
        !           371:
        !           372:        if (FLIST_NAME_LONG & flags) {
        !           373:                if ( ! io_read_size(sess, fd, &pathlen)) {
        !           374:                        ERRX1(sess, "io_read_size");
        !           375:                        return 0;
        !           376:                }
        !           377:        } else {
        !           378:                if ( ! io_read_byte(sess, fd, &bval)) {
        !           379:                        ERRX1(sess, "io_read_byte");
        !           380:                        return 0;
        !           381:                }
        !           382:                pathlen = bval;
        !           383:        }
        !           384:
        !           385:        /* Allocate our full filename length. */
        !           386:        /* FIXME: maximum pathname length. */
        !           387:
        !           388:        if (0 == (len = pathlen + partial)) {
        !           389:                ERRX(sess, "security violation: "
        !           390:                        "zero-length pathname");
        !           391:                return 0;
        !           392:        }
        !           393:
        !           394:        if (NULL == (f->path = malloc(len + 1))) {
        !           395:                ERR(sess, "malloc");
        !           396:                return 0;
        !           397:        }
        !           398:        f->path[len] = '\0';
        !           399:
        !           400:        if (FLIST_NAME_SAME & flags)
        !           401:                memcpy(f->path, last, partial);
        !           402:
        !           403:        if ( ! io_read_buf(sess, fd, f->path + partial, pathlen)) {
        !           404:                ERRX1(sess, "io_read_buf");
        !           405:                return 0;
        !           406:        }
        !           407:
        !           408:        if ('/' == f->path[0]) {
        !           409:                ERRX(sess, "security violation: "
        !           410:                        "absolute pathname: %s", f->path);
        !           411:                return 0;
        !           412:        }
        !           413:
        !           414:        if (NULL != strstr(f->path, "/../") ||
        !           415:            (len > 2 && 0 == strcmp(f->path + len - 3, "/..")) ||
        !           416:            (len > 2 && 0 == strncmp(f->path, "../", 3)) ||
        !           417:            0 == strcmp(f->path, "..")) {
        !           418:                ERRX(sess, "%s: security violation: "
        !           419:                        "backtracking pathname", f->path);
        !           420:                return 0;
        !           421:        }
        !           422:
        !           423:        /* Record our last path and construct our filename. */
        !           424:
        !           425:        strlcpy(last, f->path, MAXPATHLEN);
        !           426:        f->wpath = f->path;
        !           427:        return 1;
        !           428: }
        !           429:
        !           430: /*
        !           431:  * Reallocate a file list in chunks of FLIST_CHUNK_SIZE;
        !           432:  * Returns zero on failure, non-zero on success.
        !           433:  */
        !           434: static int
        !           435: flist_realloc(struct sess *sess,
        !           436:        struct flist **fl, size_t *sz, size_t *max)
        !           437: {
        !           438:        void    *pp;
        !           439:
        !           440:        if (*sz + 1 <= *max)  {
        !           441:                (*sz)++;
        !           442:                return 1;
        !           443:        }
        !           444:
        !           445:        pp = recallocarray(*fl, *max,
        !           446:                *max + FLIST_CHUNK_SIZE, sizeof(struct flist));
        !           447:        if (NULL == pp) {
        !           448:                ERR(sess, "recallocarray");
        !           449:                return 0;
        !           450:        }
        !           451:        *fl = pp;
        !           452:        *max += FLIST_CHUNK_SIZE;
        !           453:        (*sz)++;
        !           454:        return 1;
        !           455: }
        !           456:
        !           457: /*
        !           458:  * Copy a regular or symbolic link file "path" into "f".
        !           459:  * This handles the correct path creation and symbolic linking.
        !           460:  * Returns zero on failure, non-zero on success.
        !           461:  */
        !           462: static int
        !           463: flist_append(struct sess *sess, struct flist *f,
        !           464:        struct stat *st, const char *path)
        !           465: {
        !           466:
        !           467:        /*
        !           468:         * Copy the full path for local addressing and transmit
        !           469:         * only the filename part for the receiver.
        !           470:         */
        !           471:
        !           472:        if (NULL == (f->path = strdup(path))) {
        !           473:                ERR(sess, "strdup");
        !           474:                return 0;
        !           475:        }
        !           476:
        !           477:        if (NULL == (f->wpath = strrchr(f->path, '/')))
        !           478:                f->wpath = f->path;
        !           479:        else
        !           480:                f->wpath++;
        !           481:
        !           482:        /*
        !           483:         * On the receiving end, we'll strip out all bits on the
        !           484:         * mode except for the file permissions.
        !           485:         * No need to warn about it here.
        !           486:         */
        !           487:
        !           488:        flist_copy_stat(f, st);
        !           489:
        !           490:        /* Optionally copy link information. */
        !           491:
        !           492:        if (S_ISLNK(st->st_mode)) {
        !           493:                f->link = symlink_read(sess, f->path);
        !           494:                if (NULL == f->link) {
        !           495:                        ERRX1(sess, "symlink_read");
        !           496:                        return 0;
        !           497:                }
        !           498:        }
        !           499:
        !           500:        return 1;
        !           501: }
        !           502:
        !           503: /*
        !           504:  * Receive a file list from the wire, filling in length "sz" (which may
        !           505:  * possibly be zero) and list "flp" on success.
        !           506:  * Return zero on failure, non-zero on success.
        !           507:  */
        !           508: int
        !           509: flist_recv(struct sess *sess, int fd, struct flist **flp, size_t *sz)
        !           510: {
        !           511:        struct flist    *fl = NULL;
        !           512:        struct flist    *ff;
        !           513:        const struct flist *fflast = NULL;
        !           514:        size_t           flsz = 0, flmax = 0, lsz;
        !           515:        uint8_t          flag;
        !           516:        char             last[MAXPATHLEN];
        !           517:        uint64_t         lval; /* temporary values... */
        !           518:        int32_t          ival;
        !           519:
        !           520:        last[0] = '\0';
        !           521:
        !           522:        for (;;) {
        !           523:                if ( ! io_read_byte(sess, fd, &flag)) {
        !           524:                        ERRX1(sess, "io_read_byte");
        !           525:                        goto out;
        !           526:                } else if (0 == flag)
        !           527:                        break;
        !           528:
        !           529:                if ( ! flist_realloc(sess, &fl, &flsz, &flmax)) {
        !           530:                        ERRX1(sess, "flist_realloc");
        !           531:                        goto out;
        !           532:                }
        !           533:
        !           534:                ff = &fl[flsz - 1];
        !           535:                fflast = flsz > 1 ? &fl[flsz - 2] : NULL;
        !           536:
        !           537:                /* Filename first. */
        !           538:
        !           539:                if ( ! flist_recv_name(sess, fd, ff, flag, last)) {
        !           540:                        ERRX1(sess, "flist_recv_name");
        !           541:                        goto out;
        !           542:                }
        !           543:
        !           544:                /* Read the file size. */
        !           545:
        !           546:                if ( ! io_read_ulong(sess, fd, &lval)) {
        !           547:                        ERRX1(sess, "io_read_ulong");
        !           548:                        goto out;
        !           549:                }
        !           550:                ff->st.size = lval;
        !           551:
        !           552:                /* Read the modification time. */
        !           553:
        !           554:                if ( ! (FLIST_TIME_SAME & flag)) {
        !           555:                        if ( ! io_read_int(sess, fd, &ival)) {
        !           556:                                ERRX1(sess, "io_read_int");
        !           557:                                goto out;
        !           558:                        }
        !           559:                        ff->st.mtime = ival;
        !           560:                } else if (NULL == fflast) {
        !           561:                        ERRX(sess, "same time without last entry");
        !           562:                        goto out;
        !           563:                }  else
        !           564:                        ff->st.mtime = fflast->st.mtime;
        !           565:
        !           566:                /* Read the file mode. */
        !           567:
        !           568:                if ( ! (FLIST_MODE_SAME & flag)) {
        !           569:                        if ( ! io_read_int(sess, fd, &ival)) {
        !           570:                                ERRX1(sess, "io_read_int");
        !           571:                                goto out;
        !           572:                        }
        !           573:                        ff->st.mode = ival;
        !           574:                } else if (NULL == fflast) {
        !           575:                        ERRX(sess, "same mode without last entry");
        !           576:                        goto out;
        !           577:                } else
        !           578:                        ff->st.mode = fflast->st.mode;
        !           579:
        !           580:                /* Optionally read the link information. */
        !           581:
        !           582:                if (S_ISLNK(ff->st.mode) &&
        !           583:                    sess->opts->preserve_links) {
        !           584:                        if ( ! io_read_size(sess, fd, &lsz)) {
        !           585:                                ERRX1(sess, "io_read_size");
        !           586:                                goto out;
        !           587:                        } else if (0 == lsz) {
        !           588:                                ERRX(sess, "empty link name");
        !           589:                                goto out;
        !           590:                        }
        !           591:                        ff->link = calloc(lsz + 1, 1);
        !           592:                        if (NULL == ff->link) {
        !           593:                                ERR(sess, "calloc");
        !           594:                                goto out;
        !           595:                        }
        !           596:                        if ( ! io_read_buf(sess, fd, ff->link, lsz)) {
        !           597:                                ERRX1(sess, "io_read_buf");
        !           598:                                goto out;
        !           599:                        }
        !           600:                }
        !           601:
        !           602:                LOG3(sess, "%s: received file metadata: "
        !           603:                        "size %jd, mtime %jd, mode %o",
        !           604:                        ff->path, (intmax_t)ff->st.size,
        !           605:                        (intmax_t)ff->st.mtime, ff->st.mode);
        !           606:
        !           607:                if (S_ISREG(ff->st.mode))
        !           608:                        sess->total_size += ff->st.size;
        !           609:        }
        !           610:
        !           611:        /* Remember to order the received list. */
        !           612:
        !           613:        LOG2(sess, "received file metadata list: %zu", flsz);
        !           614:        qsort(fl, flsz, sizeof(struct flist), flist_cmp);
        !           615:        flist_topdirs(sess, fl, flsz);
        !           616:        *sz = flsz;
        !           617:        *flp = fl;
        !           618:        return 1;
        !           619: out:
        !           620:        flist_free(fl, flsz);
        !           621:        *sz = 0;
        !           622:        *flp = NULL;
        !           623:        return 0;
        !           624: }
        !           625:
        !           626: /*
        !           627:  * Generate a flist possibly-recursively given a file root, which may
        !           628:  * also be a regular file or symlink.
        !           629:  * On success, augments the generated list in "flp" of length "sz".
        !           630:  * Returns zero on failure, non-zero on success.
        !           631:  */
        !           632: static int
        !           633: flist_gen_dirent(struct sess *sess, char *root,
        !           634:        struct flist **fl, size_t *sz, size_t *max)
        !           635: {
        !           636:        char            *cargv[2], *cp;
        !           637:        int              rc = 0;
        !           638:        FTS             *fts;
        !           639:        FTSENT          *ent;
        !           640:        struct flist    *f;
        !           641:        size_t           flsz = 0, stripdir;
        !           642:        struct stat      st;
        !           643:
        !           644:        cargv[0] = root;
        !           645:        cargv[1] = NULL;
        !           646:
        !           647:        /*
        !           648:         * If we're a file, then revert to the same actions we use for
        !           649:         * the non-recursive scan.
        !           650:         */
        !           651:
        !           652:        if (-1 == lstat(root, &st)) {
        !           653:                ERR(sess, "%s: lstat", root);
        !           654:                return 0;
        !           655:        } else if (S_ISREG(st.st_mode)) {
        !           656:                if ( ! flist_realloc(sess, fl, sz, max)) {
        !           657:                        ERRX1(sess, "flist_realloc");
        !           658:                        return 0;
        !           659:                }
        !           660:                f = &(*fl)[(*sz) - 1];
        !           661:                assert(NULL != f);
        !           662:
        !           663:                if ( ! flist_append(sess, f, &st, root)) {
        !           664:                        ERRX1(sess, "flist_append");
        !           665:                        return 0;
        !           666:                } else if (-1 == unveil(root, "r")) {
        !           667:                        ERR(sess, "%s: unveil", root);
        !           668:                        return 0;
        !           669:                }
        !           670:                return 1;
        !           671:        } else if (S_ISLNK(st.st_mode)) {
        !           672:                if ( ! sess->opts->preserve_links) {
        !           673:                        WARNX(sess, "%s: skipping symlink", root);
        !           674:                        return 1;
        !           675:                } else if ( ! flist_realloc(sess, fl, sz, max)) {
        !           676:                        ERRX1(sess, "flist_realloc");
        !           677:                        return 0;
        !           678:                }
        !           679:                f = &(*fl)[(*sz) - 1];
        !           680:                assert(NULL != f);
        !           681:
        !           682:                if ( ! flist_append(sess, f, &st, root)) {
        !           683:                        ERRX1(sess, "flist_append");
        !           684:                        return 0;
        !           685:                } else if (-1 == unveil(root, "r")) {
        !           686:                        ERR(sess, "%s: unveil", root);
        !           687:                        return 0;
        !           688:                }
        !           689:                return 1;
        !           690:        } else if ( ! S_ISDIR(st.st_mode)) {
        !           691:                WARNX(sess, "%s: skipping special", root);
        !           692:                return 1;
        !           693:        }
        !           694:
        !           695:        /*
        !           696:         * If we end with a slash, it means that we're not supposed to
        !           697:         * copy the directory part itself---only the contents.
        !           698:         * So set "stripdir" to be what we take out.
        !           699:         */
        !           700:
        !           701:        stripdir = strlen(root);
        !           702:        assert(stripdir > 0);
        !           703:        if ('/' != root[stripdir - 1])
        !           704:                stripdir = 0;
        !           705:
        !           706:        /*
        !           707:         * If we're not stripping anything, then see if we need to strip
        !           708:         * out the leading material in the path up to and including the
        !           709:         * last directory component.
        !           710:         */
        !           711:
        !           712:        if (0 == stripdir)
        !           713:                if (NULL != (cp = strrchr(root, '/')))
        !           714:                        stripdir = cp - root + 1;
        !           715:
        !           716:        /*
        !           717:         * If we're recursive, then we need to take down all of the
        !           718:         * files and directory components, so use fts(3).
        !           719:         * Copying the information file-by-file into the flstat.
        !           720:         * We'll make sense of it in flist_send.
        !           721:         */
        !           722:
        !           723:        if (NULL == (fts = fts_open(cargv, FTS_PHYSICAL, NULL))) {
        !           724:                ERR(sess, "fts_open");
        !           725:                return 0;
        !           726:        }
        !           727:
        !           728:        errno = 0;
        !           729:        while (NULL != (ent = fts_read(fts))) {
        !           730:                if ( ! flist_fts_check(sess, ent)) {
        !           731:                        errno = 0;
        !           732:                        continue;
        !           733:                }
        !           734:
        !           735:                /* We don't allow symlinks without -l. */
        !           736:
        !           737:                assert(NULL != ent->fts_statp);
        !           738:                if (S_ISLNK(ent->fts_statp->st_mode) &&
        !           739:                    ! sess->opts->preserve_links) {
        !           740:                        WARNX(sess, "%s: skipping "
        !           741:                                "symlink", ent->fts_path);
        !           742:                        continue;
        !           743:                }
        !           744:
        !           745:                /* Allocate a new file entry. */
        !           746:
        !           747:                if ( ! flist_realloc(sess, fl, sz, max)) {
        !           748:                        ERRX1(sess, "flist_realloc");
        !           749:                        goto out;
        !           750:                }
        !           751:                flsz++;
        !           752:                f = &(*fl)[*sz - 1];
        !           753:
        !           754:                /* Our path defaults to "." for the root. */
        !           755:
        !           756:                if ('\0' == ent->fts_path[stripdir]) {
        !           757:                        if (asprintf(&f->path, "%s.", ent->fts_path) < 0) {
        !           758:                                ERR(sess, "asprintf");
        !           759:                                f->path = NULL;
        !           760:                                goto out;
        !           761:                        }
        !           762:                } else {
        !           763:                        if (NULL == (f->path = strdup(ent->fts_path))) {
        !           764:                                ERR(sess, "strdup");
        !           765:                                goto out;
        !           766:                        }
        !           767:                }
        !           768:
        !           769:                f->wpath = f->path + stripdir;
        !           770:                flist_copy_stat(f, ent->fts_statp);
        !           771:
        !           772:                /* Optionally copy link information. */
        !           773:
        !           774:                if (S_ISLNK(ent->fts_statp->st_mode)) {
        !           775:                        f->link = symlink_read(sess, f->path);
        !           776:                        if (NULL == f->link) {
        !           777:                                ERRX1(sess, "symlink_read");
        !           778:                                goto out;
        !           779:                        }
        !           780:                }
        !           781:
        !           782:                /* Reset errno for next fts_read() call. */
        !           783:                errno = 0;
        !           784:        }
        !           785:        if (errno) {
        !           786:                ERR(sess, "fts_read");
        !           787:                goto out;
        !           788:        } else if (-1 == unveil(root, "r")) {
        !           789:                ERR(sess, "%s: unveil", root);
        !           790:                goto out;
        !           791:        }
        !           792:
        !           793:        LOG3(sess, "generated %zu filenames: %s", flsz, root);
        !           794:        rc = 1;
        !           795: out:
        !           796:        fts_close(fts);
        !           797:        return rc;
        !           798: }
        !           799:
        !           800: /*
        !           801:  * Generate a flist recursively given the array of directories (or
        !           802:  * files, symlinks, doesn't matter) specified in argv (argc >0).
        !           803:  * On success, stores the generated list in "flp" with length "sz",
        !           804:  * which may be zero.
        !           805:  * Returns zero on failure, non-zero on success.
        !           806:  */
        !           807: static int
        !           808: flist_gen_dirs(struct sess *sess, size_t argc,
        !           809:        char **argv, struct flist **flp, size_t *sz)
        !           810: {
        !           811:        size_t           i, max = 0;
        !           812:
        !           813:        for (i = 0; i < argc; i++)
        !           814:                if ( ! flist_gen_dirent(sess, argv[i], flp, sz, &max))
        !           815:                        break;
        !           816:
        !           817:        if (i == argc) {
        !           818:                LOG2(sess, "recursively generated %zu filenames", *sz);
        !           819:                return 1;
        !           820:        }
        !           821:
        !           822:        ERRX1(sess, "flist_gen_dirent");
        !           823:        flist_free(*flp, max);
        !           824:        *flp = NULL;
        !           825:        *sz = 0;
        !           826:        return 0;
        !           827: }
        !           828:
        !           829: /*
        !           830:  * Generate list of files from the command-line argc (>0) and argv.
        !           831:  * On success, stores the generated list in "flp" with length "sz",
        !           832:  * which may be zero.
        !           833:  * Returns zero on failure, non-zero on success.
        !           834:  */
        !           835: static int
        !           836: flist_gen_files(struct sess *sess, size_t argc,
        !           837:        char **argv, struct flist **flp, size_t *sz)
        !           838: {
        !           839:        struct flist    *fl = NULL, *f;
        !           840:        size_t           i, flsz = 0;
        !           841:        struct stat      st;
        !           842:
        !           843:        assert(argc);
        !           844:
        !           845:        if (NULL == (fl = calloc(argc, sizeof(struct flist)))) {
        !           846:                ERR(sess, "calloc");
        !           847:                return 0;
        !           848:        }
        !           849:
        !           850:        for (i = 0; i < argc; i++) {
        !           851:                if ('\0' == argv[i][0])
        !           852:                        continue;
        !           853:                if (-1 == lstat(argv[i], &st)) {
        !           854:                        ERR(sess, "%s: lstat", argv[i]);
        !           855:                        goto out;
        !           856:                }
        !           857:
        !           858:                /*
        !           859:                 * File type checks.
        !           860:                 * In non-recursive mode, we don't accept directories.
        !           861:                 * We also skip symbolic links without -l.
        !           862:                 * Beyond that, we only accept regular files.
        !           863:                 */
        !           864:
        !           865:                if (S_ISDIR(st.st_mode)) {
        !           866:                        WARNX(sess, "%s: skipping directory", argv[i]);
        !           867:                        continue;
        !           868:                } else if (S_ISLNK(st.st_mode)) {
        !           869:                        if ( ! sess->opts->preserve_links) {
        !           870:                                WARNX(sess, "%s: skipping "
        !           871:                                        "symlink", argv[i]);
        !           872:                                continue;
        !           873:                        }
        !           874:                } else if ( ! S_ISREG(st.st_mode)) {
        !           875:                        WARNX(sess, "%s: skipping special", argv[i]);
        !           876:                        continue;
        !           877:                }
        !           878:
        !           879:                f = &fl[flsz++];
        !           880:                assert(NULL != f);
        !           881:
        !           882:                /* Add this file to our file-system worldview. */
        !           883:
        !           884:                if (-1 == unveil(argv[i], "r")) {
        !           885:                        ERR(sess, "%s: unveil", argv[i]);
        !           886:                        goto out;
        !           887:                } else if ( ! flist_append(sess, f, &st, argv[i])) {
        !           888:                        ERRX1(sess, "flist_append");
        !           889:                        goto out;
        !           890:                }
        !           891:        }
        !           892:
        !           893:        LOG2(sess, "non-recursively generated %zu filenames", flsz);
        !           894:        *sz = flsz;
        !           895:        *flp = fl;
        !           896:        return 1;
        !           897: out:
        !           898:        flist_free(fl, argc);
        !           899:        *sz = 0;
        !           900:        *flp = NULL;
        !           901:        return 0;
        !           902: }
        !           903:
        !           904: /*
        !           905:  * Generate a sorted, de-duplicated list of file metadata.
        !           906:  * In non-recursive mode (the default), we use only the files we're
        !           907:  * given.
        !           908:  * Otherwise, directories are recursively examined.
        !           909:  * Returns zero on failure, non-zero on success.
        !           910:  * On success, "fl" will need to be freed with flist_free().
        !           911:  */
        !           912: int
        !           913: flist_gen(struct sess *sess, size_t argc,
        !           914:        char **argv, struct flist **flp, size_t *sz)
        !           915: {
        !           916:        int      rc;
        !           917:
        !           918:        assert(argc > 0);
        !           919:        rc = sess->opts->recursive ?
        !           920:                flist_gen_dirs(sess, argc, argv, flp, sz) :
        !           921:                flist_gen_files(sess, argc, argv, flp, sz);
        !           922:
        !           923:        /* After scanning, lock our file-system view. */
        !           924:
        !           925:        if (-1 == unveil(NULL, NULL)) {
        !           926:                ERR(sess, "unveil");
        !           927:                return 0;
        !           928:        } else if ( ! rc)
        !           929:                return 0;
        !           930:
        !           931:        qsort(*flp, *sz, sizeof(struct flist), flist_cmp);
        !           932:
        !           933:        if (flist_dedupe(sess, flp, sz)) {
        !           934:                flist_topdirs(sess, *flp, *sz);
        !           935:                return 1;
        !           936:        }
        !           937:
        !           938:        ERRX1(sess, "flist_dedupe");
        !           939:        flist_free(*flp, *sz);
        !           940:        *flp = NULL;
        !           941:        *sz = 0;
        !           942:        return 0;
        !           943: }
        !           944:
        !           945: /*
        !           946:  * Generate a list of files in root to delete that are within the
        !           947:  * top-level directories stipulated by "wfl".
        !           948:  * Only handles symbolic links, directories, and regular files.
        !           949:  * Returns zero on failure (fl and flsz will be NULL and zero), non-zero
        !           950:  * on success.
        !           951:  * On success, "fl" will need to be freed with flist_free().
        !           952:  */
        !           953: int
        !           954: flist_gen_dels(struct sess *sess, const char *root,
        !           955:        struct flist **fl, size_t *sz,
        !           956:        const struct flist *wfl, size_t wflsz)
        !           957: {
        !           958:        char            **cargv = NULL;
        !           959:        int               rc = 0, c;
        !           960:        FTS              *fts = NULL;
        !           961:        FTSENT           *ent;
        !           962:        struct flist     *f;
        !           963:        size_t            cargvs = 0, i, j, max = 0, stripdir;
        !           964:        ENTRY             hent;
        !           965:        ENTRY            *hentp;
        !           966:
        !           967:        *fl = NULL;
        !           968:        *sz = 0;
        !           969:
        !           970:        /* Only run this code when we're recursive. */
        !           971:
        !           972:        if ( ! sess->opts->recursive)
        !           973:                return 1;
        !           974:
        !           975:        /*
        !           976:         * Gather up all top-level directories for scanning.
        !           977:         * This is stipulated by rsync's --delete behaviour, where we
        !           978:         * only delete things in the top-level directories given on the
        !           979:         * command line.
        !           980:         */
        !           981:
        !           982:        assert(wflsz > 0);
        !           983:        for (i = 0; i < wflsz; i++)
        !           984:                if (FLSTAT_TOP_DIR & wfl[i].st.flags)
        !           985:                        cargvs++;
        !           986:        if (0 == cargvs)
        !           987:                return 1;
        !           988:
        !           989:        if (NULL == (cargv = calloc(cargvs + 1, sizeof(char *)))) {
        !           990:                ERR(sess, "calloc");
        !           991:                return 0;
        !           992:        }
        !           993:
        !           994:        /*
        !           995:         * If we're given just a "." as the first entry, that means
        !           996:         * we're doing a relative copy with a trailing slash.
        !           997:         * Special-case this just for the sake of simplicity.
        !           998:         * Otherwise, look through all top-levels.
        !           999:         */
        !          1000:
        !          1001:        if (wflsz && 0 == strcmp(wfl[0].wpath, ".")) {
        !          1002:                assert(1 == cargvs);
        !          1003:                assert(S_ISDIR(wfl[0].st.mode));
        !          1004:                if (asprintf(&cargv[0], "%s/", root) < 0) {
        !          1005:                        ERR(sess, "asprintf");
        !          1006:                        cargv[0] = NULL;
        !          1007:                        goto out;
        !          1008:                }
        !          1009:                cargv[1] = NULL;
        !          1010:        } else {
        !          1011:                for (i = j = 0; i < wflsz; i++) {
        !          1012:                        if ( ! (FLSTAT_TOP_DIR & wfl[i].st.flags))
        !          1013:                                continue;
        !          1014:                        assert(S_ISDIR(wfl[i].st.mode));
        !          1015:                        assert(strcmp(wfl[i].wpath, "."));
        !          1016:                        c = asprintf(&cargv[j],
        !          1017:                                "%s/%s", root, wfl[i].wpath);
        !          1018:                        if (c < 0) {
        !          1019:                                ERR(sess, "asprintf");
        !          1020:                                cargv[j] = NULL;
        !          1021:                                goto out;
        !          1022:                        }
        !          1023:                        LOG4(sess, "%s: will scan "
        !          1024:                                "for deletions", cargv[j]);
        !          1025:                        j++;
        !          1026:                }
        !          1027:                assert(j == cargvs);
        !          1028:                cargv[j] = NULL;
        !          1029:        }
        !          1030:
        !          1031:        LOG2(sess, "delete from %zu directories", cargvs);
        !          1032:
        !          1033:        /*
        !          1034:         * Next, use the standard hcreate(3) hashtable interface to hash
        !          1035:         * all of the files that we want to synchronise.
        !          1036:         * This way, we'll be able to determine which files we want to
        !          1037:         * delete in O(n) time instead of O(n * search) time.
        !          1038:         * Plus, we can do the scan in-band and only allocate the files
        !          1039:         * we want to delete.
        !          1040:         */
        !          1041:
        !          1042:        if ( ! hcreate(wflsz)) {
        !          1043:                ERR(sess, "hcreate");
        !          1044:                goto out;
        !          1045:        }
        !          1046:
        !          1047:        for (i = 0; i < wflsz; i++) {
        !          1048:                memset(&hent, 0, sizeof(ENTRY));
        !          1049:                if (NULL == (hent.key = strdup(wfl[i].wpath))) {
        !          1050:                        ERR(sess, "strdup");
        !          1051:                        goto out;
        !          1052:                }
        !          1053:                if (NULL == (hentp = hsearch(hent, ENTER))) {
        !          1054:                        ERR(sess, "hsearch");
        !          1055:                        goto out;
        !          1056:                } else if (hentp->key != hent.key) {
        !          1057:                        ERRX(sess, "%s: duplicate", wfl[i].wpath);
        !          1058:                        free(hent.key);
        !          1059:                        goto out;
        !          1060:                }
        !          1061:        }
        !          1062:
        !          1063:        /*
        !          1064:         * Now we're going to try to descend into all of the top-level
        !          1065:         * directories stipulated by the file list.
        !          1066:         * If the directories don't exist, it's ok.
        !          1067:         */
        !          1068:
        !          1069:        if (NULL == (fts = fts_open(cargv, FTS_PHYSICAL, NULL))) {
        !          1070:                ERR(sess, "fts_open");
        !          1071:                goto out;
        !          1072:        }
        !          1073:
        !          1074:        stripdir = strlen(root) + 1;
        !          1075:        errno = 0;
        !          1076:        while (NULL != (ent = fts_read(fts))) {
        !          1077:                if (FTS_NS == ent->fts_info)
        !          1078:                        continue;
        !          1079:                if ( ! flist_fts_check(sess, ent)) {
        !          1080:                        errno = 0;
        !          1081:                        continue;
        !          1082:                } else if (stripdir >= ent->fts_pathlen)
        !          1083:                        continue;
        !          1084:
        !          1085:                /* Look up in hashtable. */
        !          1086:
        !          1087:                memset(&hent, 0, sizeof(ENTRY));
        !          1088:                hent.key = ent->fts_path + stripdir;
        !          1089:                if (NULL != hsearch(hent, FIND))
        !          1090:                        continue;
        !          1091:
        !          1092:                /* Not found: we'll delete it. */
        !          1093:
        !          1094:                if ( ! flist_realloc(sess, fl, sz, &max)) {
        !          1095:                        ERRX1(sess, "flist_realloc");
        !          1096:                        goto out;
        !          1097:                }
        !          1098:                f = &(*fl)[*sz - 1];
        !          1099:
        !          1100:                if (NULL == (f->path = strdup(ent->fts_path))) {
        !          1101:                        ERR(sess, "strdup");
        !          1102:                        goto out;
        !          1103:                }
        !          1104:                f->wpath = f->path + stripdir;
        !          1105:                assert(NULL != ent->fts_statp);
        !          1106:                flist_copy_stat(f, ent->fts_statp);
        !          1107:                errno = 0;
        !          1108:        }
        !          1109:
        !          1110:        if (errno) {
        !          1111:                ERR(sess, "fts_read");
        !          1112:                goto out;
        !          1113:        }
        !          1114:
        !          1115:        qsort(*fl, *sz, sizeof(struct flist), flist_cmp);
        !          1116:        rc = 1;
        !          1117: out:
        !          1118:        if (NULL != fts)
        !          1119:                fts_close(fts);
        !          1120:        for (i = 0; i < cargvs; i++)
        !          1121:                free(cargv[i]);
        !          1122:        free(cargv);
        !          1123:        hdestroy();
        !          1124:        return rc;
        !          1125: }
        !          1126:
        !          1127: /*
        !          1128:  * Delete all files and directories in "fl".
        !          1129:  * If called with a zero-length "fl", does nothing.
        !          1130:  * If dry_run is specified, simply write what would be done.
        !          1131:  * Return zero on failure, non-zero on success.
        !          1132:  */
        !          1133: int
        !          1134: flist_del(struct sess *sess, int root,
        !          1135:        const struct flist *fl, size_t flsz)
        !          1136: {
        !          1137:        ssize_t  i;
        !          1138:        int      flag;
        !          1139:
        !          1140:        if (0 == flsz)
        !          1141:                return 1;
        !          1142:
        !          1143:        assert(sess->opts->del);
        !          1144:        assert(sess->opts->recursive);
        !          1145:
        !          1146:        for (i = flsz - 1; i >= 0; i--) {
        !          1147:                LOG1(sess, "%s: deleting", fl[i].wpath);
        !          1148:                if (sess->opts->dry_run)
        !          1149:                        continue;
        !          1150:                assert(-1 != root);
        !          1151:                flag = S_ISDIR(fl[i].st.mode) ? AT_REMOVEDIR : 0;
        !          1152:                if (-1 == unlinkat(root, fl[i].wpath, flag) &&
        !          1153:                    ENOENT != errno) {
        !          1154:                        ERR(sess, "%s: unlinkat", fl[i].wpath);
        !          1155:                        return 0;
        !          1156:                }
        !          1157:        }
        !          1158:
        !          1159:        return 1;
        !          1160: }