=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/rsync/uploader.c,v retrieving revision 1.29 retrieving revision 1.30 diff -c -r1.29 -r1.30 *** src/usr.bin/rsync/uploader.c 2021/06/30 13:10:04 1.29 --- src/usr.bin/rsync/uploader.c 2021/10/22 11:10:34 1.30 *************** *** 1,4 **** ! /* $OpenBSD: uploader.c,v 1.29 2021/06/30 13:10:04 claudio Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * Copyright (c) 2019 Florian Obser --- 1,4 ---- ! /* $OpenBSD: uploader.c,v 1.30 2021/10/22 11:10:34 claudio Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons * Copyright (c) 2019 Florian Obser *************** *** 19,24 **** --- 19,25 ---- #include #include + #include #include #include #include *************** *** 166,172 **** * Return <0 on failure 0 on success. */ static int ! pre_link(struct upload *p, struct sess *sess) { struct stat st; const struct flist *f; --- 167,173 ---- * Return <0 on failure 0 on success. */ static int ! pre_symlink(struct upload *p, struct sess *sess) { struct stat st; const struct flist *f; *************** *** 266,272 **** } /* ! * See pre_link(), but for devices. * FIXME: this is very similar to the other pre_xxx() functions. * Return <0 on failure 0 on success. */ --- 267,273 ---- } /* ! * 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,361 **** } /* ! * See pre_link(), but for FIFOs. * FIXME: this is very similar to the other pre_xxx() functions. * Return <0 on failure 0 on success. */ --- 356,362 ---- } /* ! * 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,438 **** } /* ! * 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. */ --- 433,439 ---- } /* ! * 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,657 **** } /* * 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, struct sess *sess) { const struct flist *f; ! int rc; f = &p->fl[p->idx]; assert(S_ISREG(f->st.mode)); --- 642,696 ---- } /* + * 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, off_t *size, struct sess *sess) { const struct flist *f; ! struct stat st; ! int i, rc, match = -1; f = &p->fl[p->idx]; assert(S_ISREG(f->st.mode)); *************** *** 670,705 **** * in the rsync_uploader() function. */ *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); return -1; ! } ! if (!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 (!rsync_set_metadata_at(sess, 0, p->rootfd, f, f->path)) { ERRX1("rsync_set_metadata"); return -1; } return 0; } *filefd = openat(p->rootfd, f->path, O_RDONLY | O_NOFOLLOW, 0); if (*filefd == -1 && errno != ENOENT) { ERR("%s: openat", f->path); --- 709,776 ---- * in the rsync_uploader() function. */ + *size = 0; *filefd = -1; ! rc = check_file(p->rootfd, f, &st); ! if (rc == -1) return -1; ! 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; } } ! 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,788 **** 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; int c; /* Once finished this should never get called again. */ --- 849,858 ---- struct sess *sess, int *fileoutfd) { struct blkset blk; void *mbuf, *bufp; ssize_t msz; size_t i, pos, sz; ! off_t offs, filesize; int c; /* Once finished this should never get called again. */ *************** *** 849,857 **** 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); else if (S_ISREG(u->fl[u->idx].st.mode)) ! c = pre_file(u, fileinfd, &st, sess); else if (S_ISBLK(u->fl[u->idx].st.mode) || S_ISCHR(u->fl[u->idx].st.mode)) c = pre_dev(u, sess); --- 919,927 ---- 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_symlink(u, sess); else if (S_ISREG(u->fl[u->idx].st.mode)) ! 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,903 **** memset(&blk, 0, sizeof(struct blkset)); blk.csum = u->csumlen; ! if (*fileinfd != -1 && st.st_size > 0) { ! init_blkset(&blk, st.st_size); assert(blk.blksz); blk.blks = calloc(blk.blksz, sizeof(struct blk)); --- 966,973 ---- memset(&blk, 0, sizeof(struct blkset)); blk.csum = u->csumlen; ! if (*fileinfd != -1 && filesize > 0) { ! init_blkset(&blk, filesize); assert(blk.blksz); blk.blks = calloc(blk.blksz, sizeof(struct blk));