On Wed, Jun 30, 2021 at 05:47:16PM +0200, Claudio Jeker wrote:
> 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.
 
There is some unveil issues with this diff that need to be worked out.
Until then it people can test by commenting the unveil calls in receiver.c

> -- 
> :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));
> 

-- 
:wq Claudio

Reply via email to