diff options
author | Sebastian Benoit <benno@cvs.openbsd.org> | 2019-02-18 21:34:55 +0000 |
---|---|---|
committer | Sebastian Benoit <benno@cvs.openbsd.org> | 2019-02-18 21:34:55 +0000 |
commit | bde034a382776f3c396eef446206fe579292673e (patch) | |
tree | cc19160b5f11f9dcb25f390e1b1f89f36bb5b3f2 /usr.bin/rsync | |
parent | 574102a6ff9a74c28f2f7825a5f76cbd68781424 (diff) |
sync with kristaps up to Sun Feb 17 2019
339cf5998c0c022623cd68de50722b6c14543952 Push "error trail" further into code.
baf58ce5fe1bc6ce431b3b0ac8264b83ae8c7d02 Document all arguments. Add
common -av usage. Remove bits about not supporting anything but
files/dirs.
821a811a8c80e52fb56b241fc65a16cae1b4fb2c Disambiguate as prodded by deraadt@
6c4475b8f226e9031ec0ec1b3f14f7d347132c87 Add -h to usage string
4d344ae6156873b44c95de0c1ed629e637c2d7ab Clarify error message
language, use service name instead of port, specify that the socket is
SOCK_STREAM. From deraadt@. Tweaked for lowercase messages.
f3ec049e76257fc96bcdc872f1d3b967b98f3eb6 In consideration to benno@'s
comments, let the mktemp functions propogate an errno handled by the
caller. Also keep the original line lengths. While in mktemp.c, make
some defines into an enum.
e116c2bd00e634b56e4276120135915ceaa31cf2 Put the FSM of the sender
into its own function. Put dry_run ack and end of phase ack into the
send buffer too, further reducing the possibility of deadlock.
c7745aa4c7394ca89d841f8ee76782256d694340 Make the sender write loop be
fully non-blocking. This frees us of deadlocking the protocol because
the sender will always be able to pull down data.
93c7b4843e80aeac2ec6ae6ffc395df4deaf4a31 Remove "yoda" notation to be
more in tune with OpenBSD. Most found by deraadt@.
Diffstat (limited to 'usr.bin/rsync')
-rw-r--r-- | usr.bin/rsync/TODO.md | 6 | ||||
-rw-r--r-- | usr.bin/rsync/blocks.c | 23 | ||||
-rw-r--r-- | usr.bin/rsync/child.c | 10 | ||||
-rw-r--r-- | usr.bin/rsync/client.c | 10 | ||||
-rw-r--r-- | usr.bin/rsync/downloader.c | 10 | ||||
-rw-r--r-- | usr.bin/rsync/extern.h | 18 | ||||
-rw-r--r-- | usr.bin/rsync/io.c | 5 | ||||
-rw-r--r-- | usr.bin/rsync/log.c | 22 | ||||
-rw-r--r-- | usr.bin/rsync/main.c | 100 | ||||
-rw-r--r-- | usr.bin/rsync/mktemp.c | 125 | ||||
-rw-r--r-- | usr.bin/rsync/receiver.c | 6 | ||||
-rw-r--r-- | usr.bin/rsync/rsync.5 | 6 | ||||
-rw-r--r-- | usr.bin/rsync/rsyncd.5 | 4 | ||||
-rw-r--r-- | usr.bin/rsync/sender.c | 376 | ||||
-rw-r--r-- | usr.bin/rsync/server.c | 16 | ||||
-rw-r--r-- | usr.bin/rsync/socket.c | 16 | ||||
-rw-r--r-- | usr.bin/rsync/uploader.c | 176 |
17 files changed, 512 insertions, 417 deletions
diff --git a/usr.bin/rsync/TODO.md b/usr.bin/rsync/TODO.md index b98c674b4e5..4ba1dca3403 100644 --- a/usr.bin/rsync/TODO.md +++ b/usr.bin/rsync/TODO.md @@ -47,12 +47,6 @@ I would rate this as easy/medium. - Hard: the same, but for Linux. -- Hard: make the sender loop use an event handler on incoming and - outgoing I/O. Right now it moves in lockstep and can be considerably - more responsive to requests by reading them in immediately instead of - having them sit in the receiver queue while it waits for disc IO. - This isn't *that* hard. - - Hard: once the sender loop is optimised, the uploader can also queue up block metadata to send on-demand instead of reading in the file then sending, then reading again, then sending. diff --git a/usr.bin/rsync/blocks.c b/usr.bin/rsync/blocks.c index bdd880fc11c..a65ea84da69 100644 --- a/usr.bin/rsync/blocks.c +++ b/usr.bin/rsync/blocks.c @@ -1,4 +1,4 @@ -/* $Id: blocks.c,v 1.10 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: blocks.c,v 1.11 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -201,16 +201,14 @@ blk_match(struct sess *sess, const struct blkset *blks, } /* - * Sent from the sender to the receiver to indicate that the block set - * has been received. + * Buffer the message from sender to the receiver indicating that the + * block set has been received. * Symmetrises blk_send_ack(). - * Returns zero on failure, non-zero on success. */ -int -blk_recv_ack(struct sess *sess, - int fd, const struct blkset *blocks, int32_t idx) +void +blk_recv_ack(struct sess *sess, char buf[20], + const struct blkset *blocks, int32_t idx) { - char buf[20]; size_t pos = 0, sz; sz = sizeof(int32_t) + /* index */ @@ -218,7 +216,7 @@ blk_recv_ack(struct sess *sess, sizeof(int32_t) + /* block length */ sizeof(int32_t) + /* checksum length */ sizeof(int32_t); /* block remainder */ - assert(sz <= sizeof(buf)); + assert(sz == 20); io_buffer_int(sess, buf, &pos, sz, idx); io_buffer_int(sess, buf, &pos, sz, blocks->blksz); @@ -226,13 +224,6 @@ blk_recv_ack(struct sess *sess, io_buffer_int(sess, buf, &pos, sz, blocks->csum); io_buffer_int(sess, buf, &pos, sz, blocks->rem); assert(pos == sz); - - if (!io_write_buf(sess, fd, buf, sz)) { - ERRX1(sess, "io_write_buf"); - return 0; - } - - return 1; } /* diff --git a/usr.bin/rsync/child.c b/usr.bin/rsync/child.c index 3045d3926ee..2b9427b7cbe 100644 --- a/usr.bin/rsync/child.c +++ b/usr.bin/rsync/child.c @@ -1,4 +1,4 @@ -/* $Id: child.c,v 1.5 2019/02/17 17:19:05 deraadt Exp $ */ +/* $Id: child.c,v 1.6 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -46,7 +46,7 @@ rsync_child(const struct opts *opts, int fd, const struct fargs *f) if ((args = fargs_cmdline(&sess, f)) == NULL) { ERRX1(&sess, "fargs_cmdline"); - exit(1); + exit(EXIT_FAILURE); } for (i = 0; args[i] != NULL; i++) @@ -56,10 +56,10 @@ rsync_child(const struct opts *opts, int fd, const struct fargs *f) if (dup2(fd, STDIN_FILENO) == -1) { ERR(&sess, "dup2"); - exit(1); + exit(EXIT_FAILURE); } if (dup2(fd, STDOUT_FILENO) == -1) { ERR(&sess, "dup2"); - exit(1); + exit(EXIT_FAILURE); } /* Here we go... */ @@ -67,6 +67,6 @@ rsync_child(const struct opts *opts, int fd, const struct fargs *f) execvp(args[0], args); ERR(&sess, "%s: execvp", args[0]); - exit(1); + exit(EXIT_FAILURE); /* NOTREACHED */ } diff --git a/usr.bin/rsync/client.c b/usr.bin/rsync/client.c index a184894ef86..fd1deea624c 100644 --- a/usr.bin/rsync/client.c +++ b/usr.bin/rsync/client.c @@ -1,4 +1,4 @@ -/* $Id: client.c,v 1.7 2019/02/17 15:59:09 deraadt Exp $ */ +/* $Id: client.c,v 1.8 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -59,10 +59,10 @@ rsync_client(const struct opts *opts, int fd, const struct fargs *f) } if (sess.rver < sess.lver) { - ERRX(&sess, - "remote protocol %d is older than own %d: unsupported\n", - sess.rver, sess.lver); - rc = 2; /* Protocol incompatibility*/ + ERRX(&sess, "remote protocol is older " + "than our own (%" PRId32 " < %" PRId32 "): " + "this is not supported", + sess.rver, sess.lver); goto out; } diff --git a/usr.bin/rsync/downloader.c b/usr.bin/rsync/downloader.c index 0ddc1b488d0..3c5652e29f3 100644 --- a/usr.bin/rsync/downloader.c +++ b/usr.bin/rsync/downloader.c @@ -1,4 +1,4 @@ -/* $Id: downloader.c,v 1.14 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: downloader.c,v 1.15 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -414,14 +414,14 @@ rsync_downloader(struct download *p, struct sess *sess, int *ofd) /* Create the temporary file. */ - if (mktemplate(&p->fname, f->path, sess->opts->recursive) - == -1) { - ERR(sess, "asprintf"); + if (mktemplate(sess, &p->fname, + f->path, sess->opts->recursive) == -1) { + ERRX1(sess, "mktemplate"); goto out; } if ((p->fd = mkstempat(p->rootfd, p->fname)) == -1) { - ERR(sess, "%s: openat", p->fname); + ERR(sess, "mkstempat"); goto out; } diff --git a/usr.bin/rsync/extern.h b/usr.bin/rsync/extern.h index 8137faba946..a395a4e7cf5 100644 --- a/usr.bin/rsync/extern.h +++ b/usr.bin/rsync/extern.h @@ -1,4 +1,4 @@ -/* $Id: extern.h,v 1.18 2019/02/17 18:11:50 deraadt Exp $ */ +/* $Id: extern.h,v 1.19 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -23,6 +23,11 @@ #define RSYNC_PROTOCOL (27) /* + * The default service (see services(5)) for rsync. + */ +#define RSYNC_SERVICE "rsync" + +/* * Maximum amount of file data sent over the wire at once. */ #define MAX_CHUNK (32 * 1024) @@ -135,11 +140,12 @@ struct blk { enum blkstatst { BLKSTAT_NONE = 0, - BLKSTAT_DATASZ, + BLKSTAT_NEXT, BLKSTAT_DATA, BLKSTAT_TOK, BLKSTAT_HASH, - BLKSTAT_DONE + BLKSTAT_DONE, + BLKSTAT_PHASE, }; /* @@ -325,8 +331,8 @@ struct upload *upload_alloc(struct sess *, const char *, int, int, size_t, void upload_free(struct upload *); struct blkset *blk_recv(struct sess *, int, const char *); -int blk_recv_ack(struct sess *, - int, const struct blkset *, int32_t); +void blk_recv_ack(struct sess *, + char [20], const struct blkset *, int32_t); void blk_match(struct sess *, const struct blkset *, const char *, struct blkstat *); int blk_send(struct sess *, int, size_t, @@ -346,7 +352,7 @@ char *mkstemplinkat(char*, int, char *); char *mkstempfifoat(int, char *); char *mkstempnodat(int, char *, mode_t, dev_t); char *mkstempsock(const char *, char *); -int mktemplate(char **, const char *, int); +int mktemplate(struct sess *, char **, const char *, int); char *symlink_read(struct sess *, const char *); char *symlinkat_read(struct sess *, int, const char *); diff --git a/usr.bin/rsync/io.c b/usr.bin/rsync/io.c index 018798bed83..edd413ab2c4 100644 --- a/usr.bin/rsync/io.c +++ b/usr.bin/rsync/io.c @@ -1,4 +1,4 @@ -/* $Id: io.c,v 1.9 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: io.c,v 1.10 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -304,8 +304,7 @@ io_read_flush(struct sess *sess, int fd) } else if (sess->mplex_read_remain == 0) return 1; - if (!io_read_blocking(sess, fd, - mpbuf, sess->mplex_read_remain)) { + if (!io_read_blocking(sess, fd, mpbuf, sess->mplex_read_remain)) { ERRX1(sess, "io_read_blocking"); return 0; } diff --git a/usr.bin/rsync/log.c b/usr.bin/rsync/log.c index 606996b8c23..77c537a473b 100644 --- a/usr.bin/rsync/log.c +++ b/usr.bin/rsync/log.c @@ -1,4 +1,4 @@ -/* $Id: log.c,v 1.3 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: log.c,v 1.4 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -77,8 +77,8 @@ rsync_errx(struct sess *sess, const char *fname, } fprintf(stderr, "%s:%zu: error%s%s\n", fname, line, - buf != NULL ? ": " : "", - buf != NULL ? buf : ""); + (buf != NULL) ? ": " : "", + (buf != NULL) ? buf : ""); free(buf); } @@ -104,8 +104,8 @@ rsync_err(struct sess *sess, const char *fname, } fprintf(stderr, "%s:%zu: error%s%s: %s\n", fname, line, - buf != NULL ? ": " : "", - buf != NULL ? buf : "", strerror(er)); + (buf != NULL) ? ": " : "", + (buf != NULL) ? buf : "", strerror(er)); free(buf); } @@ -133,8 +133,8 @@ rsync_errx1(struct sess *sess, const char *fname, } fprintf(stderr, "%s:%zu: error%s%s\n", fname, line, - buf != NULL ? ": " : "", - buf != NULL ? buf : ""); + (buf != NULL) ? ": " : "", + (buf != NULL) ? buf : ""); free(buf); } @@ -158,8 +158,8 @@ rsync_warnx(struct sess *sess, const char *fname, } fprintf(stderr, "%s:%zu: warning%s%s\n", fname, line, - buf != NULL ? ": " : "", - buf != NULL ? buf : ""); + (buf != NULL) ? ": " : "", + (buf != NULL) ? buf : ""); free(buf); } @@ -188,7 +188,7 @@ rsync_warn(struct sess *sess, int level, } fprintf(stderr, "%s:%zu: warning%s%s: %s\n", fname, line, - buf != NULL ? ": " : "", - buf != NULL ? buf : "", strerror(er)); + (buf != NULL) ? ": " : "", + (buf != NULL) ? buf : "", strerror(er)); free(buf); } diff --git a/usr.bin/rsync/main.c b/usr.bin/rsync/main.c index 358dbe2d9a8..f72bbcde936 100644 --- a/usr.bin/rsync/main.c +++ b/usr.bin/rsync/main.c @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.27 2019/02/17 20:11:42 deraadt Exp $ */ +/* $Id: main.c,v 1.28 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -98,18 +98,18 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) /* Allocations. */ if ((f = calloc(1, sizeof(struct fargs))) == NULL) - err(1, "calloc"); + err(EXIT_FAILURE, "calloc"); f->sourcesz = argc - 1; if ((f->sources = calloc(f->sourcesz, sizeof(char *))) == NULL) - err(1, "calloc"); + err(EXIT_FAILURE, "calloc"); for (i = 0; i < argc - 1; i++) if ((f->sources[i] = strdup(argv[i])) == NULL) - err(1, "strdup"); + err(EXIT_FAILURE, "strdup"); if ((f->sink = strdup(argv[i])) == NULL) - err(1, "strdup"); + err(EXIT_FAILURE, "strdup"); /* * Test files for its locality. @@ -124,32 +124,32 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) if (fargs_is_remote(f->sink)) { f->mode = FARGS_SENDER; if ((f->host = strdup(f->sink)) == NULL) - err(1, "strdup"); + err(EXIT_FAILURE, "strdup"); } if (fargs_is_remote(f->sources[0])) { if (f->host != NULL) - errx(1, "both source and " + errx(EXIT_FAILURE, "both source and " "destination cannot be remote files"); f->mode = FARGS_RECEIVER; if ((f->host = strdup(f->sources[0])) == NULL) - err(1, "strdup"); + err(EXIT_FAILURE, "strdup"); } if (f->host != NULL) { if (strncasecmp(f->host, "rsync://", 8) == 0) { - /* rsync://host/module[/path] */ + /* rsync://host[:port]/module[/path] */ f->remote = 1; len = strlen(f->host) - 8 + 1; memmove(f->host, f->host + 8, len); if ((cp = strchr(f->host, '/')) == NULL) - errx(1, "rsync protocol " + errx(EXIT_FAILURE, "rsync protocol " "requires a module name"); *cp++ = '\0'; f->module = cp; if ((cp = strchr(f->module, '/')) != NULL) *cp = '\0'; - if ((cp = strchr(f->host, ':'))) { + if ((cp = strchr(f->host, ':')) != NULL) { /* host:port --> extract port */ *cp++ = '\0'; opts->port = cp; @@ -169,9 +169,9 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) } } if ((len = strlen(f->host)) == 0) - errx(1, "empty remote host"); + errx(EXIT_FAILURE, "empty remote host"); if (f->remote && strlen(f->module) == 0) - errx(1, "empty remote module"); + errx(EXIT_FAILURE, "empty remote module"); } /* Make sure we have the same "hostspec" for all files. */ @@ -181,7 +181,7 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) for (i = 0; i < f->sourcesz; i++) { if (!fargs_is_remote(f->sources[i])) continue; - errx(1, "remote file in " + errx(EXIT_FAILURE, "remote file in " "list of local sources: %s", f->sources[i]); } @@ -191,22 +191,22 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) !fargs_is_daemon(f->sources[i])) continue; if (fargs_is_daemon(f->sources[i])) - errx(1, "remote " + errx(EXIT_FAILURE, "remote " "daemon in list of " "remote sources: %s", f->sources[i]); - errx(1, "local file in " + errx(EXIT_FAILURE, "local file in " "list of remote sources: %s", f->sources[i]); } } else { if (f->mode != FARGS_RECEIVER) - errx(1, "sender mode for remote " + errx(EXIT_FAILURE, "sender mode for remote " "daemon receivers not yet supported"); for (i = 0; i < f->sourcesz; i++) { if (fargs_is_daemon(f->sources[i])) continue; - errx(1, "non-remote daemon file " + errx(EXIT_FAILURE, "non-remote daemon file " "in list of remote daemon sources: " "%s", f->sources[i]); } @@ -248,11 +248,18 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) strncasecmp(cp, "rsync://", 8) == 0) { /* rsync://path */ cp += 8; - if ((ccp = strchr(cp, ':'))) /* skip :port */ + + /* + * FIXME: broken. + * URIs can allow colons too. + * Fix this after merge. + */ + + if ((ccp = strchr(cp, ':')) != NULL) /* skip :port */ *ccp = '\0'; if (strncmp(cp, f->host, len) || (cp[len] != '/' && cp[len] != '\0')) - errx(1, "different remote " + errx(EXIT_FAILURE, "different remote " "host: %s", f->sources[i]); memmove(f->sources[i], f->sources[i] + len + 8 + 1, @@ -265,7 +272,7 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) /* host::path */ if (strncmp(cp, f->host, len) || (cp[len] != ':' && cp[len] != '\0')) - errx(1, "different remote " + errx(EXIT_FAILURE, "different remote " "host: %s", f->sources[i]); memmove(f->sources[i], f->sources[i] + len + 2, j - len - 1); @@ -276,7 +283,7 @@ fargs_parse(size_t argc, char *argv[], struct opts *opts) /* host:path */ if (strncmp(cp, f->host, len) || (cp[len] != ':' && cp[len] != '\0')) - errx(1, "different remote " + errx(EXIT_FAILURE, "different remote " "host: %s", f->sources[i]); memmove(f->sources[i], f->sources[i] + len + 1, j - len); @@ -291,7 +298,7 @@ main(int argc, char *argv[]) { struct opts opts; pid_t child; - int fds[2], rc = 0, c, st; + int fds[2], c, st; struct fargs *fargs; struct option lopts[] = { { "port", required_argument, NULL, 3 }, @@ -329,7 +336,7 @@ main(int argc, char *argv[]) if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw proc exec unveil", NULL) == -1) - err(1, "pledge"); + err(EXIT_FAILURE, "pledge"); memset(&opts, 0, sizeof(struct opts)); @@ -351,6 +358,7 @@ main(int argc, char *argv[]) break; case 'e': opts.ssh_prog = optarg; + /* Ignore. */ break; case 'g': opts.preserve_gids = 1; @@ -404,7 +412,7 @@ main(int argc, char *argv[]) goto usage; if (opts.port == NULL) - opts.port = "rsync"; + opts.port = RSYNC_SERVICE; /* * This is what happens when we're started with the "hidden" @@ -414,8 +422,9 @@ main(int argc, char *argv[]) if (opts.server) { if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) - err(1, "pledge"); - return rsync_server(&opts, (size_t)argc, argv); + err(EXIT_FAILURE, "pledge"); + c = rsync_server(&opts, (size_t)argc, argv); + return c ? EXIT_SUCCESS : EXIT_FAILURE; } /* @@ -441,39 +450,39 @@ main(int argc, char *argv[]) assert(fargs->mode == FARGS_RECEIVER); if (pledge("stdio unix rpath wpath cpath dpath inet fattr chown dns getpw unveil", NULL) == -1) - err(1, "pledge"); - rc = rsync_socket(&opts, fargs); + err(EXIT_FAILURE, "pledge"); + c = rsync_socket(&opts, fargs); fargs_free(fargs); - return rc; + return c ? EXIT_SUCCESS : EXIT_FAILURE; } /* Drop the dns/inet possibility. */ if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw proc exec unveil", NULL) == -1) - err(1, "pledge"); + err(EXIT_FAILURE, "pledge"); /* Create a bidirectional socket and start our child. */ if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds) == -1) - err(1, "socketpair"); + err(EXIT_FAILURE, "socketpair"); if ((child = fork()) == -1) { close(fds[0]); close(fds[1]); - err(1, "fork"); + err(EXIT_FAILURE, "fork"); } /* Drop the fork possibility. */ if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw exec unveil", NULL) == -1) - err(1, "pledge"); + err(EXIT_FAILURE, "pledge"); if (child == 0) { close(fds[0]); fds[0] = -1; if (pledge("stdio exec", NULL) == -1) - err(1, "pledge"); + err(EXIT_FAILURE, "pledge"); rsync_child(&opts, fds[1], fargs); /* NOTREACHED */ } @@ -481,8 +490,8 @@ main(int argc, char *argv[]) close(fds[1]); fds[1] = -1; if (pledge("stdio unix rpath wpath cpath dpath fattr chown getpw unveil", NULL) == -1) - err(1, "pledge"); - rc = rsync_client(&opts, fds[0], fargs); + err(EXIT_FAILURE, "pledge"); + c = rsync_client(&opts, fds[0], fargs); fargs_free(fargs); /* @@ -491,22 +500,23 @@ main(int argc, char *argv[]) * So close the connection here so that they don't hang. */ - if (!rc) { + if (!c) { close(fds[0]); fds[0] = -1; } if (waitpid(child, &st, 0) == -1) - err(1, "waitpid"); - if (!(WIFEXITED(st) && WEXITSTATUS(st) == 0)) - rc = 0; + err(EXIT_FAILURE, "waitpid"); + if (!(WIFEXITED(st) && WEXITSTATUS(st) == EXIT_SUCCESS)) + c = 0; if (fds[0] != -1) close(fds[0]); - return rc; + return c ? EXIT_SUCCESS : EXIT_FAILURE; usage: - fprintf(stderr, "usage: %s [-Daglnoprtv] " - "[-e ssh-prog] [--delete] [--rsync-path=prog] src ... dst\n", + fprintf(stderr, "usage: %s [-Daghlnoprtv] " + "[-e ssh-prog] [--delete] " + "[--port=port] [--rsync-path=prog] src ... dst\n", getprogname()); - return 1; + return EXIT_FAILURE; } diff --git a/usr.bin/rsync/mktemp.c b/usr.bin/rsync/mktemp.c index 9f260d15e68..0e132e1ef07 100644 --- a/usr.bin/rsync/mktemp.c +++ b/usr.bin/rsync/mktemp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mktemp.c,v 1.4 2019/02/16 17:59:33 deraadt Exp $ */ +/* $OpenBSD: mktemp.c,v 1.5 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 1996-1998, 2008 Theo de Raadt * Copyright (c) 1997, 2008-2009 Todd C. Miller @@ -16,50 +16,71 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> + #include <errno.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> -#define MKTEMP_NAME 0 -#define MKTEMP_FILE 1 -#define MKTEMP_DIR 2 -#define MKTEMP_LINK 3 -#define MKTEMP_FIFO 4 -#define MKTEMP_NOD 5 -#define MKTEMP_SOCK 6 +#include "extern.h" + +/* + * The type of temporary files we can create. + */ +enum tmpmode { + MKTEMP_NAME, + MKTEMP_FILE, + MKTEMP_DIR, + MKTEMP_LINK, + MKTEMP_FIFO, + MKTEMP_NOD, + MKTEMP_SOCK +}; +/* + * Characters we'll use for replacement in the template string. + */ #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" #define NUM_CHARS (sizeof(TEMPCHARS) - 1) + +/* + * The number of template replacement values (foo.XXXXXX = 6) that we + * require as a minimum for the filename. + */ #define MIN_X 6 +/* + * The only flags we'll accept for creation of the temporary file. + */ #define MKOTEMP_FLAGS (O_APPEND | O_CLOEXEC | O_DSYNC | O_RSYNC | O_SYNC) #ifndef nitems -#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +# define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif -/* adapted from libc/stdio/mktemp.c */ +/* + * Adapted from libc/stdio/mktemp.c. + */ static int -mktemp_internalat(int pfd, char *path, int slen, int mode, int flags, - const char *link, mode_t dev_type, dev_t dev) +mktemp_internalat(int pfd, char *path, int slen, enum tmpmode mode, + int flags, const char *link, mode_t dev_type, dev_t dev) { - char *start, *cp, *ep; - const char tempchars[] = TEMPCHARS; - unsigned int tries; - struct stat sb; + char *start, *cp, *ep; + const char tempchars[] = TEMPCHARS; + unsigned int tries; + struct stat sb; struct sockaddr_un sun; - size_t len; - int fd, saved_errno; + size_t len; + int fd, saved_errno; len = strlen(path); if (len < MIN_X || slen < 0 || (size_t)slen > len - MIN_X) { @@ -69,7 +90,8 @@ mktemp_internalat(int pfd, char *path, int slen, int mode, int flags, ep = path + len - slen; for (start = ep; start > path && start[-1] == 'X'; start--) - ; + /* continue */ ; + if (ep - start < MIN_X) { errno = EINVAL; return(-1); @@ -183,69 +205,74 @@ mktemp_internalat(int pfd, char *path, int slen, int mode, int flags, * A combination of mkstemp(3) and openat(2). * On success returns a file descriptor and trailing Xs are overwritten in * path to create a unique file name. - * Returns -1 on failure. + * Returns -1 on failure and sets errno. */ int mkstempat(int fd, char *path) { - return(mktemp_internalat(fd, path, 0, MKTEMP_FILE, 0, NULL, 0, 0)); + + return mktemp_internalat(fd, path, 0, MKTEMP_FILE, 0, NULL, 0, 0); } /* * A combination of mkstemp(3) and symlinkat(2). * On success returns path with trailing Xs overwritten to create a unique * file name. - * Returns NULL on failure. + * Returns NULL on failure and sets errno. */ -char* +char * mkstemplinkat(char *link, int fd, char *path) { + if (mktemp_internalat(fd, path, 0, MKTEMP_LINK, 0, link, 0, 0) == -1) - return(NULL); - return(path); + return NULL; + return path; } /* * A combination of mkstemp(3) and mkfifoat(2). * On success returns path with trailing Xs overwritten to create a unique * file name. - * Returns NULL on failure. + * Returns NULL on failure and sets errno. */ -char* +char * mkstempfifoat(int fd, char *path) { + if (mktemp_internalat(fd, path, 0, MKTEMP_FIFO, 0, NULL, 0, 0) == -1) - return(NULL); - return(path); + return NULL; + return path; } /* * A combination of mkstemp(3) and mknodat(2). * On success returns path with trailing Xs overwritten to create a unique * file name. - * Returns NULL on failure. + * Returns NULL on failure and sets errno. */ -char* +char * mkstempnodat(int fd, char *path, mode_t mode, dev_t dev) { - if (mktemp_internalat(fd, path, 0, MKTEMP_NOD, 0, NULL, mode, dev) == - -1) - return(NULL); - return(path); + + if (mktemp_internalat(fd, path, 0, + MKTEMP_NOD, 0, NULL, mode, dev) == -1) + return NULL; + return path; } /* * A combination of mkstemp(3) and bind(2) on a unix domain socket. * On success returns path with trailing Xs overwritten to create a unique * file name. - * Returns NULL on failure. + * Returns NULL on failure and sets errno. */ -char* +char * mkstempsock(const char *root, char *path) { + if (mktemp_internalat(0, path, 0, MKTEMP_SOCK, 0, root, 0, 0) == -1) - return(NULL); - return(path); + return NULL; + return path; } /* @@ -256,19 +283,23 @@ mkstempsock(const char *root, char *path) * (excluding the final '\0'). */ int -mktemplate(char **ret, const char *path, int recursive) +mktemplate(struct sess *sess, char **ret, const char *path, int recursive) { int n, dirlen; const char *cp; if (recursive && (cp = strrchr(path, '/')) != NULL) { dirlen = cp - path; - if ((n = asprintf(ret, "%.*s/.%s.XXXXXXXXXX", dirlen, path, - path + dirlen + 1)) == -1) - *ret = NULL; - } else { - if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) == -1) + n = asprintf(ret, "%.*s/.%s.XXXXXXXXXX", + dirlen, path, path + dirlen + 1); + if (n < 0) { + ERR(sess, "asprintf"); *ret = NULL; + } + } else if ((n = asprintf(ret, ".%s.XXXXXXXXXX", path)) < 0) { + ERR(sess, "asprintf"); + *ret = NULL; } - return(n); + + return n; } diff --git a/usr.bin/rsync/receiver.c b/usr.bin/rsync/receiver.c index 02ba0810fc8..54d0ddb6382 100644 --- a/usr.bin/rsync/receiver.c +++ b/usr.bin/rsync/receiver.c @@ -1,4 +1,4 @@ -/* $Id: receiver.c,v 1.17 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: receiver.c,v 1.18 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> @@ -199,7 +199,7 @@ rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root) if (!io_read_size(sess, fdin, &excl)) { ERRX1(sess, "io_read_size"); goto out; - } else if (excl != 0) { + } else if (0 != excl) { ERRX(sess, "exclusion list is non-empty"); goto out; } @@ -220,7 +220,7 @@ rsync_receiver(struct sess *sess, int fdin, int fdout, const char *root) if (!io_read_int(sess, fdin, &ioerror)) { ERRX1(sess, "io_read_int"); goto out; - } else if (ioerror != 0) { + } else if (0 != ioerror) { ERRX1(sess, "io_error is non-zero"); goto out; } diff --git a/usr.bin/rsync/rsync.5 b/usr.bin/rsync/rsync.5 index b3b14ac47f5..a27f856986f 100644 --- a/usr.bin/rsync/rsync.5 +++ b/usr.bin/rsync/rsync.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: rsync.5,v 1.8 2019/02/16 17:59:33 deraadt Exp $ +.\" $OpenBSD: rsync.5,v 1.9 2019/02/18 21:34:54 benno 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: February 16 2019 $ +.Dd $Mdocdate: February 18 2019 $ .Dt RSYNC 5 .Os .Sh NAME @@ -234,7 +234,7 @@ the group id (integer) .It if a special file and .Fl D , -the device +the device .Dq rdev type (integer) .It diff --git a/usr.bin/rsync/rsyncd.5 b/usr.bin/rsync/rsyncd.5 index b5255948f38..15e576d1e5d 100644 --- a/usr.bin/rsync/rsyncd.5 +++ b/usr.bin/rsync/rsyncd.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: rsyncd.5,v 1.2 2019/02/10 23:24:14 benno Exp $ +.\" $OpenBSD: rsyncd.5,v 1.3 2019/02/18 21:34:54 benno 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: February 10 2019 $ +.Dd $Mdocdate: February 18 2019 $ .Dt RSYNCD 5 .Os .Sh NAME diff --git a/usr.bin/rsync/sender.c b/usr.bin/rsync/sender.c index d42d3a1a342..bb2c148538e 100644 --- a/usr.bin/rsync/sender.c +++ b/usr.bin/rsync/sender.c @@ -1,4 +1,4 @@ -/* $Id: sender.c,v 1.13 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: sender.c,v 1.14 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -34,7 +34,7 @@ * A request from the receiver to download updated file data. */ struct send_dl { - int32_t idx; /* index in our file list */ + int32_t idx; /* index in our file list */ struct blkset *blks; /* the sender's block information */ TAILQ_ENTRY(send_dl) entries; }; @@ -46,7 +46,6 @@ struct send_dl { struct send_up { struct send_dl *cur; /* file being updated or NULL */ struct blkstat stat; /* status of file being updated */ - int primed; /* blk_recv_ack() was called */ }; TAILQ_HEAD(send_dlq, send_dl); @@ -87,7 +86,189 @@ send_up_reset(struct send_up *p) p->stat.offs = 0; p->stat.hint = 0; p->stat.curst = BLKSTAT_NONE; - p->primed = 0; +} + +/* + * This is the bulk of the sender work. + * Here we tend to an output buffer that responds to receiver requests + * for data. + * This does not act upon the output descriptor itself so as to avoid + * blocking, which otherwise would deadlock the protocol. + * Returns zero on failure, non-zero on success. + */ +static int +send_up_fsm(struct sess *sess, size_t *phase, + struct send_up *up, void **wb, size_t *wbsz, size_t *wbmax, + const struct flist *fl) +{ + size_t pos = 0, isz = sizeof(int32_t), + dsz = MD4_DIGEST_LENGTH; + unsigned char fmd[MD4_DIGEST_LENGTH]; + off_t sz; + char buf[20]; + + switch (up->stat.curst) { + case BLKSTAT_DATA: + /* + * A data segment to be written: buffer both the length + * and the data. + * If we've finished the transfer, move on to the token; + * otherwise, keep sending data. + */ + + sz = MINIMUM(MAX_CHUNK, + up->stat.curlen - up->stat.curpos); + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_int(sess, *wb, &pos, *wbsz, sz); + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, sz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_buf(sess, *wb, &pos, *wbsz, + up->stat.map + up->stat.curpos, sz); + + up->stat.curpos += sz; + if (up->stat.curpos == up->stat.curlen) + up->stat.curst = BLKSTAT_TOK; + return 1; + case BLKSTAT_TOK: + /* + * The data token following (maybe) a data segment. + * These can also come standalone if, say, the file's + * being fully written. + * It's followed by a hash or another data segment, + * depending on the token. + */ + + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_int(sess, *wb, + &pos, *wbsz, up->stat.curtok); + up->stat.curst = up->stat.curtok ? + BLKSTAT_NEXT : BLKSTAT_HASH; + return 1; + case BLKSTAT_HASH: + /* + * The hash following transmission of all file contents. + * This is always followed by the state that we're + * finished with the file. + */ + + hash_file(up->stat.map, up->stat.mapsz, fmd, sess); + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, dsz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_buf(sess, *wb, &pos, *wbsz, fmd, dsz); + up->stat.curst = BLKSTAT_DONE; + return 1; + case BLKSTAT_DONE: + /* + * The data has been written. + * Clear our current send file and allow the block below + * to find another. + */ + + LOG3(sess, "%s: flushed %jd KB total, %.2f%% uploaded", + fl[up->cur->idx].path, + (intmax_t)up->stat.total / 1024, + 100.0 * up->stat.dirty / up->stat.total); + send_up_reset(up); + return 1; + case BLKSTAT_PHASE: + /* + * This is where we actually stop the algorithm: we're + * already at the second phase. + */ + + send_up_reset(up); + (*phase)++; + return 1; + case BLKSTAT_NEXT: + /* + * Our last case: we need to find the + * next block (and token) to transmit to + * the receiver. + * These will drive the finite state + * machine in the first few conditional + * blocks of this set. + */ + + assert(up->stat.fd != -1); + blk_match(sess, up->cur->blks, + fl[up->cur->idx].path, &up->stat); + return 1; + case BLKSTAT_NONE: + break; + } + + assert(BLKSTAT_NONE == up->stat.curst); + + /* + * We've either hit the phase change following the last file (or + * start, or prior phase change), or we need to prime the next + * file for transmission. + * We special-case dry-run mode. + */ + + if (up->cur->idx < 0) { + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_int(sess, *wb, &pos, *wbsz, -1); + + if (sess->opts->server && sess->rver > 27) { + if (!io_lowbuffer_alloc(sess, + wb, wbsz, wbmax, isz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_int(sess, *wb, &pos, *wbsz, -1); + } + up->stat.curst = BLKSTAT_PHASE; + } else if (sess->opts->dry_run) { + if (!sess->opts->server) + LOG1(sess, "%s", fl[up->cur->idx].wpath); + + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, isz)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + io_lowbuffer_int(sess, *wb, &pos, *wbsz, up->cur->idx); + up->stat.curst = BLKSTAT_NEXT; + } else { + assert(up->stat.fd != -1); + + /* + * FIXME: use the nice output of log_file() and so on in + * downloader.c, which means moving this into + * BLKSTAT_DONE instead of having it be here. + */ + + if (!sess->opts->server) + LOG1(sess, "%s", fl[up->cur->idx].wpath); + + if (!io_lowbuffer_alloc(sess, wb, wbsz, wbmax, 20)) { + ERRX1(sess, "io_lowbuffer_alloc"); + return 0; + } + assert(sizeof(buf) == 20); + blk_recv_ack(sess, buf, up->cur->blks, up->cur->idx); + io_lowbuffer_buf(sess, *wb, &pos, *wbsz, buf, 20); + + LOG3(sess, "%s: primed for %jd B total", + fl[up->cur->idx].path, + (intmax_t)up->cur->blks->size); + up->stat.curst = BLKSTAT_NEXT; + } + + return 1; } /* @@ -103,7 +284,7 @@ send_dl_enqueue(struct sess *sess, struct send_dlq *q, int32_t idx, const struct flist *fl, size_t flsz, int fd) { struct send_dl *s; - + /* End-of-phase marker. */ if (idx == -1) { @@ -118,7 +299,7 @@ send_dl_enqueue(struct sess *sess, struct send_dlq *q, } /* Validate the index. */ - + if (idx < 0 || (uint32_t)idx >= flsz) { ERRX(sess, "file index out of bounds: invalid %" PRId32 " out of %zu", idx, flsz); @@ -178,7 +359,6 @@ rsync_sender(struct sess *sess, int fdin, struct flist *fl = NULL; const struct flist *f; size_t i, flsz = 0, phase = 0, excl; - off_t sz; int rc = 0, c; int32_t idx; struct pollfd pfd[3]; @@ -186,9 +366,8 @@ rsync_sender(struct sess *sess, int fdin, struct send_dl *dl; struct send_up up; struct stat st; - unsigned char filemd[MD4_DIGEST_LENGTH]; void *wbuf = NULL; - size_t wbufpos = 0, pos, wbufsz = 0, wbufmax = 0; + size_t wbufpos = 0, wbufsz = 0, wbufmax = 0; ssize_t ssz; if (pledge("stdio getpw rpath unveil", NULL) == -1) { @@ -215,7 +394,7 @@ rsync_sender(struct sess *sess, int fdin, /* Client sends zero-length exclusions if deleting. */ if (!sess->opts->server && sess->opts->del && - !io_write_int(sess, fdout, 0)) { + !io_write_int(sess, fdout, 0)) { ERRX1(sess, "io_write_int"); goto out; } @@ -384,170 +563,20 @@ rsync_sender(struct sess *sess, int fdin, sess->total_write += ssz; } - if (pfd[1].revents & POLLOUT) { + /* + * Engage the FSM for the current transfer. + * If our phase changes, stop processing. + */ + + if (pfd[1].revents & POLLOUT && up.cur != NULL) { assert(pfd[2].fd == -1); assert(wbufpos == 0 && wbufsz == 0); - - /* - * If we have data to write, do it now according - * to the data finite state machine. - * If we receive an invalid index (-1), then - * we're either promoted to the second phase or - * it's time to exit, depending upon which phase - * we're in. - * Otherwise, we either start a transfer - * sequence (if not primed) or continue one. - */ - - pos = 0; - if (BLKSTAT_DATA == up.stat.curst) { - /* - * A data segment to be written: buffer - * both the length and the data, then - * put is in the token phase. - */ - - sz = MINIMUM(MAX_CHUNK, - up.stat.curlen - up.stat.curpos); - if (!io_lowbuffer_alloc(sess, &wbuf, - &wbufsz, &wbufmax, sizeof(int32_t))) { - ERRX1(sess, "io_lowbuffer_alloc"); - goto out; - } - io_lowbuffer_int(sess, - wbuf, &pos, wbufsz, sz); - if (!io_lowbuffer_alloc(sess, &wbuf, - &wbufsz, &wbufmax, sz)) { - ERRX1(sess, "io_lowbuffer_alloc"); - goto out; - } - io_lowbuffer_buf(sess, wbuf, &pos, wbufsz, - up.stat.map + up.stat.curpos, sz); - up.stat.curpos += sz; - if (up.stat.curpos == up.stat.curlen) - up.stat.curst = BLKSTAT_TOK; - } else if (BLKSTAT_TOK == up.stat.curst) { - /* - * The data token following (maybe) a - * data segment. - * These can also come standalone if, - * say, the file's being fully written. - * It's followed by a hash or another - * data segment, depending on the token. - */ - - if (!io_lowbuffer_alloc(sess, &wbuf, - &wbufsz, &wbufmax, sizeof(int32_t))) { - ERRX1(sess, "io_lowbuffer_alloc"); - goto out; - } - io_lowbuffer_int(sess, wbuf, - &pos, wbufsz, up.stat.curtok); - up.stat.curst = up.stat.curtok ? - BLKSTAT_NONE : BLKSTAT_HASH; - } else if (BLKSTAT_HASH == up.stat.curst) { - /* - * The hash following transmission of - * all file contents. - * This is always followed by the state - * that we're finished with the file. - */ - - hash_file(up.stat.map, - up.stat.mapsz, filemd, sess); - if (!io_lowbuffer_alloc(sess, &wbuf, - &wbufsz, &wbufmax, MD4_DIGEST_LENGTH)) { - ERRX1(sess, "io_lowbuffer_alloc"); - goto out; - } - io_lowbuffer_buf(sess, wbuf, &pos, - wbufsz, filemd, MD4_DIGEST_LENGTH); - up.stat.curst = BLKSTAT_DONE; - } else if (BLKSTAT_DONE == up.stat.curst) { - /* - * The data has been written. - * Clear our current send file and allow - * the block below to find another. - */ - - LOG3(sess, "%s: flushed %jd KB total, " - "%.2f%% uploaded", - fl[up.cur->idx].path, - (intmax_t)up.stat.total / 1024, - 100.0 * up.stat.dirty / up.stat.total); - send_up_reset(&up); - } else if (up.cur != NULL && up.cur->idx < 0) { - /* - * We've hit the phase change following - * the last file (or start, or prior - * phase change). - * Simply acknowledge it. - * FIXME: use buffering. - */ - - if (!io_write_int(sess, fdout, -1)) { - ERRX1(sess, "io_write_int"); - goto out; - } - if (sess->opts->server && sess->rver > 27 && - !io_write_int(sess, fdout, -1)) { - ERRX1(sess, "io_write_int"); - goto out; - } - send_up_reset(&up); - - /* - * This is where we actually stop the - * algorithm: we're already at the - * second phase. - */ - - if (phase++) - break; - } else if (up.cur != NULL && up.primed == 0) { - /* - * We're getting ready to send the file - * contents to the receiver. - * FIXME: use buffering. - */ - - if (!sess->opts->server) - LOG1(sess, "%s", fl[up.cur->idx].wpath); - - /* Dry-running does nothing but a response. */ - - if (sess->opts->dry_run && - !io_write_int(sess, fdout, up.cur->idx)) { - ERRX1(sess, "io_write_int"); - goto out; - } - - /* Actually perform the block send. */ - - assert(up.stat.fd != -1); - if (!blk_recv_ack(sess, fdout, - up.cur->blks, up.cur->idx)) { - ERRX1(sess, "blk_recv_ack"); - goto out; - } - LOG3(sess, "%s: primed for %jd B total", - fl[up.cur->idx].path, - (intmax_t)up.cur->blks->size); - up.primed = 1; - } else if (up.cur != NULL) { - /* - * Our last case: we need to find the - * next block (and token) to transmit to - * the receiver. - * These will drive the finite state - * machine in the first few conditional - * blocks of this set. - */ - - assert(up.stat.fd != -1); - blk_match(sess, up.cur->blks, - fl[up.cur->idx].path, &up.stat); - } + if (!send_up_fsm(sess, &phase, + &up, &wbuf, &wbufsz, &wbufmax, fl)) { + ERRX1(sess, "send_up_fsm"); + goto out; + } else if (phase > 1) + break; } /* @@ -586,7 +615,7 @@ rsync_sender(struct sess *sess, int fdin, pfd[1].fd = fdout; continue; } - + /* * Non-blocking open of file. * This will be picked up in the state machine @@ -628,6 +657,7 @@ rsync_sender(struct sess *sess, int fdin, out: send_up_reset(&up); while ((dl = TAILQ_FIRST(&sdlq)) != NULL) { + TAILQ_REMOVE(&sdlq, dl, entries); free(dl->blks); free(dl); } diff --git a/usr.bin/rsync/server.c b/usr.bin/rsync/server.c index c3f86a30a54..234d9c05c3b 100644 --- a/usr.bin/rsync/server.c +++ b/usr.bin/rsync/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.5 2019/02/17 16:17:07 deraadt Exp $ */ +/* $Id: server.c,v 1.6 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -55,7 +55,7 @@ rsync_server(const struct opts *opts, size_t argc, char *argv[]) { struct sess sess; int fdin = STDIN_FILENO, - fdout = STDOUT_FILENO, rc = 0; + fdout = STDOUT_FILENO, c = 0; memset(&sess, 0, sizeof(struct sess)); sess.opts = opts; @@ -87,10 +87,10 @@ rsync_server(const struct opts *opts, size_t argc, char *argv[]) sess.mplex_writes = 1; if (sess.rver < sess.lver) { - ERRX(&sess, - "remote protocol %d is older than our own %d: unsupported", - sess.rver, sess.lver); - rc = 2; + ERRX(&sess, "remote protocol is older " + "than our own (%" PRId32 " < %" PRId32 "): " + "this is not supported", + sess.rver, sess.lver); goto out; } @@ -156,7 +156,7 @@ rsync_server(const struct opts *opts, size_t argc, char *argv[]) WARNX(&sess, "data remains in read pipe"); #endif - rc = 1; + c = 1; out: - return rc; + return c; } diff --git a/usr.bin/rsync/socket.c b/usr.bin/rsync/socket.c index c8bf1224d94..7f9b41491ae 100644 --- a/usr.bin/rsync/socket.c +++ b/usr.bin/rsync/socket.c @@ -1,4 +1,4 @@ -/* $Id: socket.c,v 1.15 2019/02/17 18:11:50 deraadt Exp $ */ +/* $Id: socket.c,v 1.16 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -121,12 +121,12 @@ inet_resolve(struct sess *sess, const char *host, size_t *sz) LOG2(sess, "resolving: %s", host); if (error == EAI_AGAIN || error == EAI_NONAME) { - ERRX(sess, "Could not resolve hostname %s: %s", + ERRX(sess, "could not resolve hostname %s: %s", host, gai_strerror(error)); return NULL; } else if (error == EAI_SERVICE) { - ERRX(sess, "Could not resolve service '%s': %s", - sess->opts->port, gai_strerror(error)); + ERRX(sess, "could not resolve service rsync: %s", + gai_strerror(error)); return NULL; } else if (error) { ERRX(sess, "getaddrinfo: %s: %s", host, gai_strerror(error)); @@ -396,10 +396,10 @@ rsync_socket(const struct opts *opts, const struct fargs *f) /* Now we've completed the handshake. */ if (sess.rver < sess.lver) { - ERRX(&sess, - "remote protocol %d is older than own %d: unsupported\n", - sess.rver, sess.lver); - rc = 2; /* Protocol incompatibility*/ + ERRX(&sess, "remote protocol is older " + "than our own (%" PRId32 " < %" PRId32 "): " + "this is not supported", + sess.rver, sess.lver); goto out; } diff --git a/usr.bin/rsync/uploader.c b/usr.bin/rsync/uploader.c index 6f7c6dbd509..66ddc6f2c35 100644 --- a/usr.bin/rsync/uploader.c +++ b/usr.bin/rsync/uploader.c @@ -1,4 +1,4 @@ -/* $Id: uploader.c,v 1.14 2019/02/17 16:34:04 deraadt Exp $ */ +/* $Id: uploader.c,v 1.15 2019/02/18 21:34:54 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2019 Florian Obser <florian@openbsd.org> @@ -163,6 +163,10 @@ init_blk(struct blk *p, const struct blkset *set, off_t offs, } /* + * Handle a symbolic link. + * If we encounter directories existing in the symbolic link's place, + * then try to unlink the directory. + * Otherwise, simply overwrite with the symbolic link by renaming. * Return <0 on failure 0 on success. */ static int @@ -184,20 +188,24 @@ pre_link(struct upload *p, struct sess *sess) return 0; } - /* See if the symlink already exists. */ + /* + * See if the symlink already exists. + * If it's a directory, then try to unlink the directory prior + * to overwriting with a symbolic link. + * If it's a non-directory, we just overwrite it. + */ assert(p->rootfd != -1); rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW); if (rc != -1 && !S_ISLNK(st.st_mode)) { - if (S_ISDIR(st.st_mode)) { - if (unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { - WARN(sess, "%s", f->path); - return -1; - } + if (S_ISDIR(st.st_mode) && + unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { + ERR(sess, "%s: unlinkat", f->path); + return -1; } - rc = -1; /* overwrite object with symlink */ + rc = -1; } else if (rc == -1 && errno != ENOENT) { - WARN(sess, "%s: fstatat", f->path); + ERR(sess, "%s: fstatat", f->path); return -1; } @@ -209,7 +217,7 @@ pre_link(struct upload *p, struct sess *sess) if (rc != -1) { b = symlinkat_read(sess, p->rootfd, f->path); if (b == NULL) { - ERRX1(sess, "%s: symlinkat_read", f->path); + ERRX1(sess, "symlinkat_read"); return -1; } if (strcmp(f->link, b)) { @@ -223,24 +231,29 @@ pre_link(struct upload *p, struct sess *sess) b = NULL; } + /* + * Create the temporary file as a symbolic link, then rename the + * temporary file as the real one, overwriting anything there. + */ + if (rc == -1 || updatelink) { LOG3(sess, "%s: creating " "symlink: %s", f->path, f->link); - - if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { - ERR(sess, "asprintf"); + if (mktemplate(sess, &temp, + f->path, sess->opts->recursive) == -1) { + ERRX1(sess, "mktemplate"); return -1; } if (mkstemplinkat(f->link, p->rootfd, temp) == NULL) { - WARN(sess, "%s: symlinkat", temp); + ERR(sess, "mkstemplinkat"); free(temp); return -1; } newlink = 1; } - rsync_set_metadata_at(sess, newlink, p->rootfd, f, - newlink ? temp : f->path); + rsync_set_metadata_at(sess, newlink, + p->rootfd, f, newlink ? temp : f->path); if (newlink) { if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { @@ -257,6 +270,8 @@ pre_link(struct upload *p, struct sess *sess) } /* + * See pre_link(), but for devices. + * FIXME: this is very similar to the other pre_xxx() functions. * Return <0 on failure 0 on success. */ static int @@ -278,53 +293,55 @@ pre_dev(struct upload *p, struct sess *sess) return 0; } - /* See if the dev already exists */ - assert(p->rootfd != -1); + /* + * See if the dev already exists. + * If a non-device exists in its place, we'll replace that. + * If it replaces a directory, remove the directory first. + */ + assert(p->rootfd != -1); rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW); if (rc != -1 && !(S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) { - if (S_ISDIR(st.st_mode)) { - if (unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { - WARN(sess, "%s", f->path); - return -1; - } + if (S_ISDIR(st.st_mode) && + unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { + ERR(sess, "%s: unlinkat", f->path); + return -1; } - rc = -1; /* overwrite object with dev */ + rc = -1; } else if (rc == -1 && errno != ENOENT) { - WARN(sess, "%s: fstatat", f->path); + ERR(sess, "%s: fstatat", f->path); return -1; } - /* - * If the device already exists make sure it is of the correct type. - */ + /* Make sure existing device is of the correct type. */ if (rc != -1) { if ((f->st.mode & (S_IFCHR|S_IFBLK)) != - (st.st_mode & (S_IFCHR|S_IFBLK)) || f->st.rdev != - st.st_rdev) { - LOG3(sess, "%s: updating dev", f->path); + (st.st_mode & (S_IFCHR|S_IFBLK)) || + f->st.rdev != st.st_rdev) { + LOG3(sess, "%s: updating device", f->path); updatedev = 1; } } if (rc == -1 || updatedev) { newdev = 1; - if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { - ERR(sess, "asprintf"); + if (mktemplate(sess, &temp, f->path, + sess->opts->recursive) == -1) { + ERRX1(sess, "mktemplate"); return -1; } - if (mkstempnodat(p->rootfd, temp, f->st.mode & - (S_IFCHR|S_IFBLK), f->st.rdev) == NULL) { - WARN(sess, "%s: mknodat", temp); + if (mkstempnodat(p->rootfd, temp, + f->st.mode & (S_IFCHR|S_IFBLK), f->st.rdev) == NULL) { + ERR(sess, "mkstempnodat"); free(temp); return -1; } } - rsync_set_metadata_at(sess, newdev, p->rootfd, f, - newdev ? temp : f->path); + rsync_set_metadata_at(sess, newdev, + p->rootfd, f, newdev ? temp : f->path); if (newdev) { if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { @@ -335,11 +352,14 @@ pre_dev(struct upload *p, struct sess *sess) } free(temp); } + log_file(sess, f); return 0; } /* + * See pre_link(), but for FIFOs. + * FIXME: this is very similar to the other pre_xxx() functions. * Return <0 on failure 0 on success. */ static int @@ -361,39 +381,43 @@ pre_fifo(struct upload *p, struct sess *sess) return 0; } - /* See if the fifo already exists */ - assert(p->rootfd != -1); + /* + * See if the fifo already exists. + * If it exists as a non-FIFO, unlink it (if a directory) then + * mark it from replacement. + */ + assert(p->rootfd != -1); rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW); if (rc != -1 && !S_ISFIFO(st.st_mode)) { - if (S_ISDIR(st.st_mode)) { - if (unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { - WARN(sess, "%s", f->path); - return -1; - } + if (S_ISDIR(st.st_mode) && + unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { + ERR(sess, "%s: unlinkat", f->path); + return -1; } - rc = -1; /* overwrite object with fifo */ + rc = -1; } else if (rc == -1 && errno != ENOENT) { - WARN(sess, "%s: fstatat", f->path); + ERR(sess, "%s: fstatat", f->path); return -1; } if (rc == -1) { newfifo = 1; - if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { - ERR(sess, "asprintf"); + if (mktemplate(sess, &temp, f->path, + sess->opts->recursive) == -1) { + ERRX1(sess, "mktemplate"); return -1; } if (mkstempfifoat(p->rootfd, temp) == NULL) { - WARN(sess, "%s: mkfifoat", temp); + ERR(sess, "mkstempfifoat"); free(temp); return -1; } } - rsync_set_metadata_at(sess, newfifo, p->rootfd, f, - newfifo ? temp : f->path); + rsync_set_metadata_at(sess, newfifo, + p->rootfd, f, newfifo ? temp : f->path); if (newfifo) { if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { @@ -404,11 +428,14 @@ pre_fifo(struct upload *p, struct sess *sess) } free(temp); } + log_file(sess, f); return 0; } /* + * See pre_link(), but for socket files. + * FIXME: this is very similar to the other pre_xxx() functions. * Return <0 on failure 0 on success. */ static int @@ -430,38 +457,43 @@ pre_sock(struct upload *p, struct sess *sess) return 0; } + /* + * See if the fifo already exists. + * If it exists as a non-FIFO, unlink it (if a directory) then + * mark it from replacement. + */ + + assert(-1 != p->rootfd); rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW); if (rc != -1 && !S_ISSOCK(st.st_mode)) { - if (S_ISDIR(st.st_mode)) { - if (unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { - WARN(sess, "%s", f->path); - return -1; - } + if (S_ISDIR(st.st_mode) && + unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { + ERR(sess, "%s: unlinkat", f->path); + return -1; } - rc = -1; /* overwrite object with sock */ + rc = -1; } else if (rc == -1 && errno != ENOENT) { - WARN(sess, "%s: fstatat", f->path); + ERR(sess, "%s: fstatat", f->path); return -1; } if (rc == -1) { newsock = 1; - - if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { - ERR(sess, "asprintf"); + if (mktemplate(sess, &temp, f->path, + sess->opts->recursive) == -1) { + ERRX1(sess, "mktemplate"); return -1; } - if (mkstempsock(p->root, temp) == NULL) { - WARN(sess, "%s: mksockat", temp); + ERR(sess, "mkstempsock"); free(temp); return -1; } } - rsync_set_metadata_at(sess, newsock, p->rootfd, f, - newsock ? temp : f->path); + rsync_set_metadata_at(sess, newsock, + p->rootfd, f, newsock ? temp : f->path); if (newsock) { if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { @@ -472,6 +504,7 @@ pre_sock(struct upload *p, struct sess *sess) } free(temp); } + log_file(sess, f); return 0; } @@ -501,11 +534,12 @@ pre_dir(const struct upload *p, struct sess *sess) assert(p->rootfd != -1); rc = fstatat(p->rootfd, f->path, &st, AT_SYMLINK_NOFOLLOW); + if (rc == -1 && errno != ENOENT) { - WARN(sess, "%s: fstatat", f->path); + ERR(sess, "%s: fstatat", f->path); return -1; } else if (rc != -1 && !S_ISDIR(st.st_mode)) { - WARNX(sess, "%s: not a directory", f->path); + ERRX(sess, "%s: not a directory", f->path); return -1; } else if (rc != -1) { /* @@ -526,7 +560,7 @@ pre_dir(const struct upload *p, struct sess *sess) LOG3(sess, "%s: creating directory", f->path); if (mkdirat(p->rootfd, f->path, 0777 & ~p->oumask) == -1) { - WARN(sess, "%s: mkdirat", f->path); + ERR(sess, "%s: mkdirat", f->path); return -1; } @@ -824,7 +858,7 @@ rsync_uploader(struct upload *u, int *fileinfd, /* Go back to the event loop, if necessary. */ u->state = (*fileinfd == -1) ? - UPLOAD_WRITE_LOCAL : UPLOAD_READ_LOCAL; + UPLOAD_WRITE_LOCAL : UPLOAD_READ_LOCAL; if (u->state == UPLOAD_READ_LOCAL) return 1; } @@ -885,7 +919,7 @@ rsync_uploader(struct upload *u, int *fileinfd, mapsz = st.st_size; map = mmap(NULL, mapsz, PROT_READ, MAP_SHARED, *fileinfd, 0); if (map == MAP_FAILED) { - WARN(sess, "%s: mmap", u->fl[u->idx].path); + ERR(sess, "%s: mmap", u->fl[u->idx].path); close(*fileinfd); *fileinfd = -1; return -1; |