=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/rsync/uploader.c,v retrieving revision 1.29 retrieving revision 1.29.2.1 diff -u -r1.29 -r1.29.2.1 --- src/usr.bin/rsync/uploader.c 2021/06/30 13:10:04 1.29 +++ src/usr.bin/rsync/uploader.c 2021/11/09 13:40:41 1.29.2.1 @@ -1,4 +1,4 @@ -/* $OpenBSD: uploader.c,v 1.29 2021/06/30 13:10:04 claudio Exp $ */ +/* $OpenBSD: uploader.c,v 1.29.2.1 2021/11/09 13:40:41 benno Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * Copyright (c) 2019 Florian Obser @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -166,7 +167,7 @@ * 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 @@ } /* - * 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 @@ } /* - * 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 @@ } /* - * 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 @@ } /* + * 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)); @@ -665,42 +704,83 @@ return 0; } + if (sess->opts->max_size >= 0 && f->st.size > sess->opts->max_size) { + WARNX("skipping over max-size file %s", f->path); + return 0; + } + if (sess->opts->min_size >= 0 && f->st.size < sess->opts->min_size) { + WARNX("skipping under min-size file %s", f->path); + return 0; + } + /* * For non dry-run cases, we'll write the acknowledgement later * 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; } - *filefd = openat(p->rootfd, f->path, O_RDONLY | O_NOFOLLOW, 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); + 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); if (*filefd == -1 && errno != ENOENT) { ERR("%s: openat", f->path); return -1; @@ -778,11 +858,10 @@ 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 +928,9 @@ 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 +975,8 @@ 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)); @@ -956,14 +1035,14 @@ /* Make sure the block metadata buffer is big enough. */ u->bufsz = - sizeof(int32_t) + /* identifier */ - sizeof(int32_t) + /* block count */ - sizeof(int32_t) + /* block length */ - sizeof(int32_t) + /* checksum length */ - sizeof(int32_t) + /* block remainder */ - blk.blksz * - (sizeof(int32_t) + /* short checksum */ - blk.csum); /* long checksum */ + sizeof(int32_t) + /* identifier */ + sizeof(int32_t) + /* block count */ + sizeof(int32_t) + /* block length */ + sizeof(int32_t) + /* checksum length */ + sizeof(int32_t) + /* block remainder */ + blk.blksz * + (sizeof(int32_t) + /* short checksum */ + blk.csum); /* long checksum */ if (u->bufsz > u->bufmax) { if ((bufp = realloc(u->buf, u->bufsz)) == NULL) {