Thge compare-dest option of rsync is something I would like to use in
rpki-client. This implements just that and I think after that adding
copy-dest and link-dest options should be somewhat easy to add as well.
Lightly tested with my particular need.

-- 
:wq Claudio

Index: Makefile
===================================================================
RCS file: /cvs/src/usr.bin/rsync/Makefile,v
retrieving revision 1.10
diff -u -p -r1.10 Makefile
--- Makefile    8 May 2019 21:30:11 -0000       1.10
+++ Makefile    30 Jun 2021 15:40:19 -0000
@@ -1,14 +1,18 @@
 #      $OpenBSD: Makefile,v 1.10 2019/05/08 21:30:11 benno Exp $
 
 PROG=  openrsync
-SRCS=  blocks.c client.c downloader.c fargs.c flist.c hash.c ids.c \
+SRCS=  blocks.c client.c copy.c downloader.c fargs.c flist.c hash.c ids.c \
        io.c log.c mkpath.c mktemp.c receiver.c sender.c server.c session.c \
        socket.c symlinks.c uploader.c main.c misc.c
 LDADD+= -lcrypto -lm
 DPADD+= ${LIBCRYPTO} ${LIBM}
 MAN=   openrsync.1
 
-CFLAGS+=-g -W -Wall -Wextra
+CFLAGS+= -Wall -Wextra
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wmissing-declarations
+CFLAGS+= -Wshadow
+
 
 openrsync.1: rsync.1
        ln -sf ${.CURDIR}/rsync.1 openrsync.1
Index: copy.c
===================================================================
RCS file: copy.c
diff -N copy.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ copy.c      30 Jun 2021 15:40:19 -0000
@@ -0,0 +1,90 @@
+/*     $OpenBSD$ */
+/*
+ * Copyright (c) 2021 Claudio Jeker <clau...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h> /* for MAXBSIZE */
+
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+/*
+ * Return true if all bytes in buffer are zero.
+ * A buffer of zero lenght is also considered a zero buffer.
+ */
+static int
+iszero(const void *b, size_t len)
+{
+       const unsigned char *c = b;
+
+       for (; len > 0; len--) {
+               if (*c++ != '\0')
+                       return 0;
+       }
+       return 1;
+}
+
+static int
+copy_internal(int fromfd, int tofd)
+{
+       char buf[MAXBSIZE];
+       ssize_t r, w;
+
+       while ((r = read(fromfd, buf, sizeof(buf))) > 0) {
+               if (iszero(buf, sizeof(buf))) {
+                       if (lseek(tofd, r, SEEK_CUR) == -1)
+                               return -1;
+               } else {
+                       w = write(tofd, buf, r);
+                       if (r != w || w == -1)
+                               return -1;
+               }
+       }
+       if (r == -1)
+               return -1;
+       if (ftruncate(tofd, lseek(tofd, 0, SEEK_CUR)) == -1)
+               return -1;
+       return 0;
+}
+
+void
+copy_file(int rootfd, const char *basedir, const struct flist *f)
+{
+       int fromfd, tofd, dfd;
+
+       dfd = openat(rootfd, basedir, O_RDONLY | O_DIRECTORY, 0);
+       if (dfd == -1)
+               err(ERR_FILE_IO, "%s: openat", basedir);
+
+       fromfd = openat(dfd, f->path, O_RDONLY | O_NOFOLLOW, 0);
+       if (fromfd == -1)
+               err(ERR_FILE_IO, "%s/%s: openat", basedir, f->path);
+       close(dfd);
+
+       tofd = openat(rootfd, f->path,
+           O_WRONLY | O_NOFOLLOW | O_TRUNC | O_CREAT | O_EXCL,
+           0600);
+       if (tofd == -1)
+               err(ERR_FILE_IO, "%s: openat", f->path);
+
+       if (copy_internal(fromfd, tofd) == -1)
+               err(ERR_FILE_IO, "%s: copy file", f->path);
+
+       close(fromfd);
+       close(tofd);
+}
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/rsync/extern.h,v
retrieving revision 1.39
diff -u -p -r1.39 extern.h
--- extern.h    30 Jun 2021 15:24:10 -0000      1.39
+++ extern.h    30 Jun 2021 15:40:19 -0000
@@ -34,6 +34,15 @@
 #define        BLOCK_SIZE_MIN  (700)
 
 /*
+ * Maximum number of base directories that can be used.
+ */
+#define MAX_BASEDIR    20
+
+#define BASE_MODE_COMPARE      1
+#define BASE_MODE_COPY         2
+#define BASE_MODE_LINK         3
+
+/*
  * The sender and receiver use a two-phase synchronisation process.
  * The first uses two-byte hashes; the second, 16-byte.
  * (The second must hold a full MD4 digest.)
@@ -131,10 +140,12 @@ struct    opts {
        int              no_motd;               /* --no-motd */
        int              numeric_ids;           /* --numeric-ids */
        int              one_file_system;       /* -x */
+       int              alt_base_mode;
        char            *rsync_path;            /* --rsync-path */
        char            *ssh_prog;              /* --rsh or -e */
        char            *port;                  /* --port */
        char            *address;               /* --address */
+       char            *basedir[MAX_BASEDIR];
 };
 
 /*
@@ -283,7 +294,8 @@ int flist_send(struct sess *, int, int, 
 int    flist_gen_dels(struct sess *, const char *, struct flist **, size_t *,
            const struct flist *, size_t);
 
-char   **fargs_cmdline(struct sess *, const struct fargs *, size_t *);
+const char      *alt_base_mode(int);
+char           **fargs_cmdline(struct sess *, const struct fargs *, size_t *);
 
 int    io_read_buf(struct sess *, int, void *, size_t);
 int    io_read_byte(struct sess *, int, uint8_t *);
@@ -352,6 +364,8 @@ void                 hash_slow(const void *, size_t, u
                    const struct sess *);
 void            hash_file(const void *, size_t, unsigned char *,
                    const struct sess *);
+
+void            copy_file(int, const char *, const struct flist *);
 
 int             mkpath(char *);
 
Index: fargs.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/fargs.c,v
retrieving revision 1.19
diff -u -p -r1.19 fargs.c
--- fargs.c     30 Jun 2021 13:10:04 -0000      1.19
+++ fargs.c     30 Jun 2021 15:40:19 -0000
@@ -26,6 +26,21 @@
 
 #define        RSYNC_PATH      "rsync"
 
+const char *
+alt_base_mode(int mode)
+{
+       switch(mode) {
+       case BASE_MODE_COMPARE:
+               return "--compare-dest";
+       case BASE_MODE_COPY:
+               return "--copy-dest";
+       case BASE_MODE_LINK:
+               return "--link-dest";
+       default:
+               errx(1, "unknown base mode %d", mode);
+       }
+}
+
 char **
 fargs_cmdline(struct sess *sess, const struct fargs *f, size_t *skip)
 {
@@ -116,6 +131,18 @@ fargs_cmdline(struct sess *sess, const s
        if (!sess->opts->specials && sess->opts->devices)
                /* --devices is sent as -D --no-specials */
                addargs(&args, "--no-specials");
+
+       /* only add --compare-dest, etc if this is the sender */
+       if (sess->opts->alt_base_mode != 0 && 
+           f->mode == FARGS_SENDER) {
+               for (j = 0; j < MAX_BASEDIR; j++) {
+                       if (sess->opts->basedir[j] == NULL)
+                               break;
+                       addargs(&args, "%s=%s",
+                           alt_base_mode(sess->opts->alt_base_mode),
+                           sess->opts->basedir[j]);
+               }
+       }
 
        /* Terminate with a full-stop for reasons unknown. */
 
Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/main.c,v
retrieving revision 1.55
diff -u -p -r1.55 main.c
--- main.c      30 Jun 2021 13:10:04 -0000      1.55
+++ main.c      30 Jun 2021 15:40:19 -0000
@@ -274,7 +274,8 @@ main(int argc, char *argv[])
 {
        struct opts      opts;
        pid_t            child;
-       int              fds[2], sd = -1, rc, c, st, i;
+       int              fds[2], sd = -1, rc, c, st, i, lidx;
+       size_t           basedir_cnt = 0;
        struct sess       sess;
        struct fargs    *fargs;
        char            **args;
@@ -313,7 +314,12 @@ main(int argc, char *argv[])
                { "verbose",    no_argument,    &verbose,               1 },
                { "no-verbose", no_argument,    &verbose,               0 },
                { "address",    required_argument, NULL,                4 },
-               { "no-motd",    no_argument,    NULL,                   6 },
+               { "no-motd",    no_argument,    &opts.no_motd,          1 },
+               { "compare-dest", required_argument, NULL,              6 },
+#if 0
+               { "copy-dest",  required_argument, NULL,                7 },
+               { "link-dest",  required_argument, NULL,                8 },
+#endif
                { NULL,         0,              NULL,                   0 }};
 
        /* Global pledge. */
@@ -324,7 +330,7 @@ main(int argc, char *argv[])
 
        memset(&opts, 0, sizeof(struct opts));
 
-       while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvxz", lopts, NULL))
+       while ((c = getopt_long(argc, argv, "Dae:ghlnoprtvxz", lopts, &lidx))
            != -1) {
                switch (c) {
                case 'D':
@@ -397,7 +403,39 @@ main(int argc, char *argv[])
                                    errstr, optarg);
                        break;
                case 6:
-                       opts.no_motd = 1;
+                       if (opts.alt_base_mode !=0 &&
+                           opts.alt_base_mode != BASE_MODE_COMPARE) {
+                               errx(1, "option --%s conflicts with %s",
+                                   lopts[lidx].name,
+                                   alt_base_mode(opts.alt_base_mode));
+                       }
+                       opts.alt_base_mode = BASE_MODE_COMPARE;
+#if 0
+                       goto basedir;
+               case 7:
+                       if (opts.alt_base_mode !=0 &&
+                           opts.alt_base_mode != BASE_MODE_COPY) {
+                               errx(1, "option --%s conflicts with %s",
+                                   lopts[lidx].name,
+                                   alt_base_mode(opts.alt_base_mode));
+                       }
+                       opts.alt_base_mode = BASE_MODE_COPY;
+                       goto basedir;
+               case 8:
+                       if (opts.alt_base_mode !=0 &&
+                           opts.alt_base_mode != BASE_MODE_LINK) {
+                               errx(1, "option --%s conflicts with %s",
+                                   lopts[lidx].name,
+                                   alt_base_mode(opts.alt_base_mode));
+                       }
+                       opts.alt_base_mode = BASE_MODE_LINK;
+
+basedir:
+#endif
+                       if (basedir_cnt >= MAX_BASEDIR)
+                               errx(1, "too many --%s directories specified",
+                                   lopts[lidx].name);
+                       opts.basedir[basedir_cnt++] = optarg;
                        break;
                case 'h':
                default:
@@ -526,11 +564,11 @@ main(int argc, char *argv[])
 
        exit(rc);
 usage:
-       fprintf(stderr, "usage: %s"
-           " [-aDglnoprtvx] [-e program] [--address=sourceaddr] [--del]\n"
-           "\t[--no-motd] [--numeric-ids] [--port=portnumber] "
-           "[--rsync-path=program]\n\t[--timeout=seconds] [--version] "
-            "source ... directory\n",
+       fprintf(stderr, "usage: %s "
+           "[-aDglnoprtvx] [-e program] [--address=sourceaddr]\n\t"
+           "[--compare-dest=directory] [--del] [--no-motd] [--numeric-ids]\n\t"
+           "[--port=portnumber] [--rsync-path=program] [--timeout=seconds]\n\t"
+           "[--version] source ... directory\n",
            getprogname());
        exit(ERR_SYNTAX);
 }
Index: receiver.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/receiver.c,v
retrieving revision 1.28
diff -u -p -r1.28 receiver.c
--- receiver.c  30 Jun 2021 13:10:04 -0000      1.28
+++ receiver.c  30 Jun 2021 15:40:19 -0000
@@ -1,5 +1,4 @@
 /*     $OpenBSD: receiver.c,v 1.28 2021/06/30 13:10:04 claudio Exp $ */
-
 /*
  * Copyright (c) 2019 Kristaps Dzonsons <krist...@bsd.lv>
  * Copyright (c) 2019 Florian Obser <flor...@openbsd.org>
@@ -184,6 +183,44 @@ rsync_receiver(struct sess *sess, int fd
        if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw 
unveil", NULL) == -1)
                err(ERR_IPC, "pledge");
 
+       /*
+        * Create the path for our destination directory, if we're not
+        * in dry-run mode (which would otherwise crash w/the pledge).
+        * This uses our current umask: we might set the permissions on
+        * this directory in post_dir().
+        */
+
+       if (!sess->opts->dry_run) {
+               if ((tofree = strdup(root)) == NULL)
+                       err(ERR_NOMEM, NULL);
+               if (mkpath(tofree) < 0)
+                       err(ERR_FILE_IO, "%s: mkpath", tofree);
+               free(tofree);
+       }
+
+       /*
+        * Make our entire view of the file-system be limited to what's
+        * in the root directory.
+        * This prevents us from accidentally (or "under the influence")
+        * writing into other parts of the file-system.
+        */
+       if (sess->opts->basedir[0]) {
+               /*
+                * XXX just unveil everything for read
+                * Could unveil each basedir or maybe a common path
+                * also the fact that relative path are relative to the
+                * root does not help.
+                */
+               if (unveil("/", "r") == -1)
+                       err(ERR_IPC, "%s: unveil", root);
+       }
+
+       if (unveil(root, "rwc") == -1)
+               err(ERR_IPC, "%s: unveil", root);
+
+       if (unveil(NULL, NULL) == -1)
+               err(ERR_IPC, "unveil");
+
        /* Client sends zero-length exclusions. */
 
        if (!sess->opts->server && !io_write_int(sess, fdout, 0)) {
@@ -231,21 +268,6 @@ rsync_receiver(struct sess *sess, int fd
        LOG2("%s: receiver destination", root);
 
        /*
-        * Create the path for our destination directory, if we're not
-        * in dry-run mode (which would otherwise crash w/the pledge).
-        * This uses our current umask: we might set the permissions on
-        * this directory in post_dir().
-        */
-
-       if (!sess->opts->dry_run) {
-               if ((tofree = strdup(root)) == NULL)
-                       err(ERR_NOMEM, NULL);
-               if (mkpath(tofree) < 0)
-                       err(ERR_FILE_IO, "%s: mkpath", tofree);
-               free(tofree);
-       }
-
-       /*
         * Disable umask() so we can set permissions fully.
         * Then open the directory iff we're not in dry_run.
         */
@@ -269,18 +291,6 @@ rsync_receiver(struct sess *sess, int fd
                ERRX1("flist_gen_local");
                goto out;
        }
-
-       /*
-        * Make our entire view of the file-system be limited to what's
-        * in the root directory.
-        * This prevents us from accidentally (or "under the influence")
-        * writing into other parts of the file-system.
-        */
-
-       if (unveil(root, "rwc") == -1)
-               err(ERR_IPC, "%s: unveil", root);
-       if (unveil(NULL, NULL) == -1)
-               err(ERR_IPC, "unveil");
 
        /* If we have a local set, go for the deletion. */
 
Index: rsync.1
===================================================================
RCS file: /cvs/src/usr.bin/rsync/rsync.1,v
retrieving revision 1.24
diff -u -p -r1.24 rsync.1
--- rsync.1     31 Mar 2021 20:36:05 -0000      1.24
+++ rsync.1     30 Jun 2021 15:40:19 -0000
@@ -25,6 +25,7 @@
 .Op Fl aDglnoprtvx
 .Op Fl e Ar program
 .Op Fl -address Ns = Ns Ar sourceaddr
+.Op Fl -compare-dest Ns = Ns Ar directory
 .Op Fl -del
 .Op Fl -no-motd
 .Op Fl -numeric-ids
@@ -58,6 +59,18 @@ When connecting to an rsync daemon, use
 .Ar sourceaddr
 as the source address for connections, which is useful on machines with
 multiple interfaces.
+.It Fl -compare-dest Ns = Ns Ar directory
+Use directory as an alternate base directory to compare files against on the
+destination machine.
+If file in
+.Ar directory
+is found and identical to the senders file the file will not be transferred.
+Multiple
+.Fl -compare-dest
+directories may be provided.
+If
+.Ar directory
+is a relative path, it is relative to the destination directory.
 .It Fl D
 Also transfer device and special files.
 Shorthand for
Index: uploader.c
===================================================================
RCS file: /cvs/src/usr.bin/rsync/uploader.c,v
retrieving revision 1.29
diff -u -p -r1.29 uploader.c
--- uploader.c  30 Jun 2021 13:10:04 -0000      1.29
+++ uploader.c  30 Jun 2021 15:40:19 -0000
@@ -19,6 +19,7 @@
 #include <sys/stat.h>
 
 #include <assert.h>
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -166,7 +167,7 @@ init_blk(struct blk *p, const struct blk
  * Return <0 on failure 0 on success.
  */
 static int
-pre_link(struct upload *p, struct sess *sess)
+pre_symlink(struct upload *p, struct sess *sess)
 {
        struct stat              st;
        const struct flist      *f;
@@ -266,7 +267,7 @@ pre_link(struct upload *p, struct sess *
 }
 
 /*
- * See pre_link(), but for devices.
+ * See pre_symlink(), but for devices.
  * FIXME: this is very similar to the other pre_xxx() functions.
  * Return <0 on failure 0 on success.
  */
@@ -355,7 +356,7 @@ pre_dev(struct upload *p, struct sess *s
 }
 
 /*
- * See pre_link(), but for FIFOs.
+ * See pre_symlink(), but for FIFOs.
  * FIXME: this is very similar to the other pre_xxx() functions.
  * Return <0 on failure 0 on success.
  */
@@ -432,7 +433,7 @@ pre_fifo(struct upload *p, struct sess *
 }
 
 /*
- * See pre_link(), but for socket files.
+ * See pre_symlink(), but for socket files.
  * FIXME: this is very similar to the other pre_xxx() functions.
  * Return <0 on failure 0 on success.
  */
@@ -641,17 +642,55 @@ post_dir(struct sess *sess, const struct
 }
 
 /*
+ * Check if file exists in the specified root directory.
+ * Returns:
+ *    -1 on error
+ *     0 if file is considered the same
+ *     1 if file exists and is possible match
+ *     2 if file exists but quick check failed
+ *     3 if file does not exist
+ * The stat pointer st is only valid for 0, 1, and 2 returns.
+ */
+static int
+check_file(int rootfd, const struct flist *f, struct stat *st)
+{ 
+       if (fstatat(rootfd, f->path, st, AT_SYMLINK_NOFOLLOW) == -1) {
+               if (errno == ENOENT)
+                       return 3;
+
+               ERR("%s: fstatat", f->path);
+               return -1;
+       }
+
+       /* non-regular file needs attention */
+       if (!S_ISREG(st->st_mode))
+               return 2;
+
+       /* quick check if file is the same */
+       /* TODO: add support for --checksum, --size-only and --ignore-times */
+       if (st->st_size == f->st.size) {
+               if (st->st_mtime == f->st.mtime)
+                       return 0;
+               return 1;
+       }
+
+       /* file needs attention */
+       return 2;
+}
+
+/*
  * Try to open the file at the current index.
  * If the file does not exist, returns with >0.
  * Return <0 on failure, 0 on success w/nothing to be done, >0 on
  * success and the file needs attention.
  */
 static int
-pre_file(const struct upload *p, int *filefd, struct stat *st,
+pre_file(const struct upload *p, int *filefd, off_t *size,
     struct sess *sess)
 {
        const struct flist *f;
-       int rc;
+       struct stat st;
+       int i, rc, match = -1;
 
        f = &p->fl[p->idx];
        assert(S_ISREG(f->st.mode));
@@ -670,36 +709,68 @@ pre_file(const struct upload *p, int *fi
         * in the rsync_uploader() function.
         */
 
+       *size = 0;
        *filefd = -1;
-       rc = fstatat(p->rootfd, f->path, st, AT_SYMLINK_NOFOLLOW);
 
-       if (rc == -1) {
-               if (errno == ENOENT)
-                       return 1;
-
-               ERR("%s: fstatat", f->path);
+       rc = check_file(p->rootfd, f, &st);
+       if (rc == -1)
                return -1;
-       }
-       if (!S_ISREG(st->st_mode)) {
-               if (S_ISDIR(st->st_mode) &&
+       if (rc == 2 && !S_ISREG(st.st_mode)) {
+               if (S_ISDIR(st.st_mode) &&
                    unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) {
                        ERR("%s: unlinkat", f->path);
                        return -1;
                }
-               return 1;
        }
-
-       /* quick check if file is the same */
-       if (st->st_size == f->st.size &&
-           st->st_mtime == f->st.mtime) {
-               LOG3("%s: skipping: up to date", f->path);
+       if (rc == 0) {
                if (!rsync_set_metadata_at(sess, 0, p->rootfd, f, f->path)) {
                        ERRX1("rsync_set_metadata");
                        return -1;
                }
+               LOG3("%s: skipping: up to date", f->path);
                return 0;
        }
 
+       /* check alternative locations for better match */
+       for (i = 0; sess->opts->basedir[i] != NULL; i++) {
+               const char *root = sess->opts->basedir[i];
+               int dfd, x;
+
+               dfd = openat(p->rootfd, root, O_RDONLY | O_DIRECTORY, 0);
+               if (dfd == -1)
+                       err(ERR_FILE_IO, "%s: openat", root);
+               x = check_file(dfd, f, &st);
+               /* found a match */
+               if (x == 0) {
+                       if (rc >= 0) {
+                               /* found better match, delete file in rootfd */
+                               if (unlinkat(p->rootfd, f->path, 0) == -1 &&
+                                   errno != ENOENT) {
+                                       ERR("%s: unlinkat", f->path);
+                                       return -1;
+                               }
+                       }
+                       LOG3("%s: skipping: up to date in %s", f->path, root);
+                       /* TODO: depending on mode link or copy file */
+                       close(dfd);
+                       return 0;
+               } else if (x == 1 && match == -1) {
+                       /* found a local file that is a close match */
+                       match = i;
+               }
+               close(dfd);
+       }
+       if (match != -1) {
+               /* copy match from basedir into root as a start point */
+               copy_file(p->rootfd, sess->opts->basedir[match], f);
+               if (fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW) ==
+                   -1) {
+                       ERR("%s: fstatat", f->path);
+                       return -1;
+               }
+       }
+
+       *size = st.st_size;
        *filefd = openat(p->rootfd, f->path, O_RDONLY | O_NOFOLLOW, 0);
        if (*filefd == -1 && errno != ENOENT) {
                ERR("%s: openat", f->path);
@@ -778,11 +849,10 @@ rsync_uploader(struct upload *u, int *fi
        struct sess *sess, int *fileoutfd)
 {
        struct blkset       blk;
-       struct stat         st;
        void               *mbuf, *bufp;
        ssize_t             msz;
        size_t              i, pos, sz;
-       off_t               offs;
+       off_t               offs, filesize;
        int                 c;
 
        /* Once finished this should never get called again. */
@@ -849,9 +919,9 @@ rsync_uploader(struct upload *u, int *fi
                        if (S_ISDIR(u->fl[u->idx].st.mode))
                                c = pre_dir(u, sess);
                        else if (S_ISLNK(u->fl[u->idx].st.mode))
-                               c = pre_link(u, sess);
+                               c = pre_symlink(u, sess);
                        else if (S_ISREG(u->fl[u->idx].st.mode))
-                               c = pre_file(u, fileinfd, &st, sess);
+                               c = pre_file(u, fileinfd, &filesize, sess);
                        else if (S_ISBLK(u->fl[u->idx].st.mode) ||
                            S_ISCHR(u->fl[u->idx].st.mode))
                                c = pre_dev(u, sess);
@@ -896,8 +966,8 @@ rsync_uploader(struct upload *u, int *fi
        memset(&blk, 0, sizeof(struct blkset));
        blk.csum = u->csumlen;
 
-       if (*fileinfd != -1 && st.st_size > 0) {
-               init_blkset(&blk, st.st_size);
+       if (*fileinfd != -1 && filesize > 0) {
+               init_blkset(&blk, filesize);
                assert(blk.blksz);
 
                blk.blks = calloc(blk.blksz, sizeof(struct blk));

Reply via email to