summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.bin/rsync/Makefile10
-rw-r--r--usr.bin/rsync/copy.c90
-rw-r--r--usr.bin/rsync/extern.h18
-rw-r--r--usr.bin/rsync/fargs.c29
-rw-r--r--usr.bin/rsync/main.c61
-rw-r--r--usr.bin/rsync/receiver.c67
-rw-r--r--usr.bin/rsync/rsync.117
-rw-r--r--usr.bin/rsync/uploader.c126
8 files changed, 345 insertions, 73 deletions
diff --git a/usr.bin/rsync/Makefile b/usr.bin/rsync/Makefile
index f2e4d460c57..74d15d29a64 100644
--- a/usr.bin/rsync/Makefile
+++ b/usr.bin/rsync/Makefile
@@ -1,14 +1,18 @@
-# $OpenBSD: Makefile,v 1.11 2021/08/29 13:43:46 claudio Exp $
+# $OpenBSD: Makefile,v 1.12 2021/10/22 11:10:34 claudio 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 main.c misc.c mkpath.c mktemp.c receiver.c rmatch.c \
rules.c sender.c server.c session.c socket.c symlinks.c uploader.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
diff --git a/usr.bin/rsync/copy.c b/usr.bin/rsync/copy.c
new file mode 100644
index 00000000000..737234d2269
--- /dev/null
+++ b/usr.bin/rsync/copy.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: copy.c,v 1.1 2021/10/22 11:10:34 claudio Exp $ */
+/*
+ * Copyright (c) 2021 Claudio Jeker <claudio@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> /* 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);
+}
diff --git a/usr.bin/rsync/extern.h b/usr.bin/rsync/extern.h
index 2815f82cf89..cd7006a1737 100644
--- a/usr.bin/rsync/extern.h
+++ b/usr.bin/rsync/extern.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: extern.h,v 1.41 2021/09/01 09:48:08 claudio Exp $ */
+/* $OpenBSD: extern.h,v 1.42 2021/10/22 11:10:34 claudio Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -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];
};
enum rule_type {
@@ -298,7 +309,8 @@ int flist_send(struct sess *, int, int, const struct flist *, size_t);
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 *);
@@ -368,6 +380,8 @@ void hash_slow(const void *, size_t, unsigned char *,
void hash_file(const void *, size_t, unsigned char *,
const struct sess *);
+void copy_file(int, const char *, const struct flist *);
+
int mkpath(char *);
int mkstempat(int, char *);
diff --git a/usr.bin/rsync/fargs.c b/usr.bin/rsync/fargs.c
index 4047416854b..a69955c4332 100644
--- a/usr.bin/rsync/fargs.c
+++ b/usr.bin/rsync/fargs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fargs.c,v 1.19 2021/06/30 13:10:04 claudio Exp $ */
+/* $OpenBSD: fargs.c,v 1.20 2021/10/22 11:10:34 claudio Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -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)
{
@@ -117,6 +132,18 @@ fargs_cmdline(struct sess *sess, const struct fargs *f, size_t *skip)
/* --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. */
addargs(&args, ".");
diff --git a/usr.bin/rsync/main.c b/usr.bin/rsync/main.c
index 06606c07674..70ae1e3ce30 100644
--- a/usr.bin/rsync/main.c
+++ b/usr.bin/rsync/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.59 2021/09/01 09:48:08 claudio Exp $ */
+/* $OpenBSD: main.c,v 1.60 2021/10/22 11:10:34 claudio Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -280,10 +280,18 @@ static struct opts opts;
#define OP_INCLUDE 1006
#define OP_EXCLUDE_FROM 1007
#define OP_INCLUDE_FROM 1008
+#define OP_COMP_DEST 1009
+#define OP_COPY_DEST 1010
+#define OP_LINK_DEST 1011
const struct option lopts[] = {
{ "address", required_argument, NULL, OP_ADDRESS },
{ "archive", no_argument, NULL, 'a' },
+ { "compare-dest", required_argument, NULL, OP_COMP_DEST },
+#if 0
+ { "copy-dest", required_argument, NULL, OP_COPY_DEST },
+ { "link-dest", required_argument, NULL, OP_LINK_DEST },
+#endif
{ "compress", no_argument, NULL, 'z' },
{ "del", no_argument, &opts.del, 1 },
{ "delete", no_argument, &opts.del, 1 },
@@ -327,7 +335,8 @@ int
main(int argc, char *argv[])
{
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;
@@ -339,7 +348,7 @@ main(int argc, char *argv[])
NULL) == -1)
err(ERR_IPC, "pledge");
- 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':
@@ -423,6 +432,41 @@ main(int argc, char *argv[])
case OP_INCLUDE_FROM:
parse_file(optarg, RULE_INCLUDE);
break;
+ case OP_COMP_DEST:
+ 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 OP_COPY_DEST:
+ 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 OP_LINK_DEST:
+ 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 OP_VERSION:
fprintf(stderr, "openrsync: protocol version %u\n",
RSYNC_PROTOCOL);
@@ -554,12 +598,11 @@ main(int argc, char *argv[])
exit(rc);
usage:
fprintf(stderr, "usage: %s"
- " [-aDglnoprtvx] [-e program] [--address=sourceaddr] [--del]\n"
- "\t[--exclude] [--exclude-from=file] [--include] "
- "[--include-from=file]\n"
- "\t[--no-motd] [--numeric-ids] [--port=portnumber] "
- "[--rsync-path=program]\n\t[--timeout=seconds] [--version] "
- "source ... directory\n",
+ " [-aDglnoprtvx] [-e program] [--address=sourceaddr]\n"
+ "\t[--compare-dest=dir] [--del] [--exclude] [--exclude-from=file]\n"
+ "\t[--include] [--include-from=file] [--no-motd] [--numeric-ids]\n"
+ "\t[--port=portnumber] [--rsync-path=program] [--timeout=seconds]\n"
+ "\t[--version] source ... directory\n",
getprogname());
exit(ERR_SYNTAX);
}
diff --git a/usr.bin/rsync/receiver.c b/usr.bin/rsync/receiver.c
index 6e5b01670cd..02e1a58d7c4 100644
--- a/usr.bin/rsync/receiver.c
+++ b/usr.bin/rsync/receiver.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: receiver.c,v 1.29 2021/08/29 13:43:46 claudio Exp $ */
+/* $OpenBSD: receiver.c,v 1.30 2021/10/22 11:10:34 claudio Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -184,6 +184,44 @@ rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
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 exclusions. */
if (!sess->opts->server)
send_rules(sess, fdout);
@@ -222,21 +260,6 @@ rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
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.
*/
@@ -261,18 +284,6 @@ rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root)
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. */
if (!flist_del(sess, dfd, dfl, dflsz)) {
diff --git a/usr.bin/rsync/rsync.1 b/usr.bin/rsync/rsync.1
index 06d4ec606d6..9fcee4bdfbc 100644
--- a/usr.bin/rsync/rsync.1
+++ b/usr.bin/rsync/rsync.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: rsync.1,v 1.25 2021/08/30 20:25:24 job Exp $
+.\" $OpenBSD: rsync.1,v 1.26 2021/10/22 11:10:34 claudio Exp $
.\"
.\" Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: August 30 2021 $
+.Dd $Mdocdate: October 22 2021 $
.Dt OPENRSYNC 1
.Os
.Sh NAME
@@ -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 -exclude Ar pattern
.Op Fl -exclude-from Ns = Ns Ar file
@@ -62,6 +63,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
diff --git a/usr.bin/rsync/uploader.c b/usr.bin/rsync/uploader.c
index e57647c38e5..254e1be0791 100644
--- a/usr.bin/rsync/uploader.c
+++ b/usr.bin/rsync/uploader.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uploader.c,v 1.29 2021/06/30 13:10:04 claudio Exp $ */
+/* $OpenBSD: uploader.c,v 1.30 2021/10/22 11:10:34 claudio Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2019 Florian Obser <florian@openbsd.org>
@@ -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 blkset *set, off_t offs,
* 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 *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 *sess)
}
/*
- * 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 *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 upload *u, size_t idx)
}
/*
+ * 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 *filefd, struct stat *st,
* 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 *fileinfd,
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 *fileinfd,
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 *fileinfd,
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));