version 1.29, 2021/06/30 13:10:04 |
version 1.29.2.1, 2021/11/09 13:40:41 |
|
|
#include <sys/stat.h> |
#include <sys/stat.h> |
|
|
#include <assert.h> |
#include <assert.h> |
|
#include <err.h> |
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <inttypes.h> |
#include <inttypes.h> |
|
|
* Return <0 on failure 0 on success. |
* Return <0 on failure 0 on success. |
*/ |
*/ |
static int |
static int |
pre_link(struct upload *p, struct sess *sess) |
pre_symlink(struct upload *p, struct sess *sess) |
{ |
{ |
struct stat st; |
struct stat st; |
const struct flist *f; |
const struct flist *f; |
|
|
} |
} |
|
|
/* |
/* |
* See pre_link(), but for devices. |
* See pre_symlink(), but for devices. |
* FIXME: this is very similar to the other pre_xxx() functions. |
* FIXME: this is very similar to the other pre_xxx() functions. |
* Return <0 on failure 0 on success. |
* Return <0 on failure 0 on success. |
*/ |
*/ |
|
|
} |
} |
|
|
/* |
/* |
* See pre_link(), but for FIFOs. |
* See pre_symlink(), but for FIFOs. |
* FIXME: this is very similar to the other pre_xxx() functions. |
* FIXME: this is very similar to the other pre_xxx() functions. |
* Return <0 on failure 0 on success. |
* Return <0 on failure 0 on success. |
*/ |
*/ |
|
|
} |
} |
|
|
/* |
/* |
* 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. |
* FIXME: this is very similar to the other pre_xxx() functions. |
* Return <0 on failure 0 on success. |
* Return <0 on failure 0 on success. |
*/ |
*/ |
|
|
} |
} |
|
|
/* |
/* |
|
* 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. |
* Try to open the file at the current index. |
* If the file does not exist, returns with >0. |
* If the file does not exist, returns with >0. |
* Return <0 on failure, 0 on success w/nothing to be done, >0 on |
* Return <0 on failure, 0 on success w/nothing to be done, >0 on |
* success and the file needs attention. |
* success and the file needs attention. |
*/ |
*/ |
static int |
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) |
struct sess *sess) |
{ |
{ |
const struct flist *f; |
const struct flist *f; |
int rc; |
struct stat st; |
|
int i, rc, match = -1; |
|
|
f = &p->fl[p->idx]; |
f = &p->fl[p->idx]; |
assert(S_ISREG(f->st.mode)); |
assert(S_ISREG(f->st.mode)); |
|
|
return 0; |
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 |
* For non dry-run cases, we'll write the acknowledgement later |
* in the rsync_uploader() function. |
* in the rsync_uploader() function. |
*/ |
*/ |
|
|
|
*size = 0; |
*filefd = -1; |
*filefd = -1; |
rc = fstatat(p->rootfd, f->path, st, AT_SYMLINK_NOFOLLOW); |
|
|
|
if (rc == -1) { |
rc = check_file(p->rootfd, f, &st); |
if (errno == ENOENT) |
if (rc == -1) |
return 1; |
|
|
|
ERR("%s: fstatat", f->path); |
|
return -1; |
return -1; |
} |
if (rc == 2 && !S_ISREG(st.st_mode)) { |
if (!S_ISREG(st->st_mode)) { |
if (S_ISDIR(st.st_mode) && |
if (S_ISDIR(st->st_mode) && |
|
unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { |
unlinkat(p->rootfd, f->path, AT_REMOVEDIR) == -1) { |
ERR("%s: unlinkat", f->path); |
ERR("%s: unlinkat", f->path); |
return -1; |
return -1; |
} |
} |
return 1; |
|
} |
} |
|
if (rc == 0) { |
/* 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)) { |
if (!rsync_set_metadata_at(sess, 0, p->rootfd, f, f->path)) { |
ERRX1("rsync_set_metadata"); |
ERRX1("rsync_set_metadata"); |
return -1; |
return -1; |
} |
} |
|
LOG3("%s: skipping: up to date", f->path); |
return 0; |
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) { |
if (*filefd == -1 && errno != ENOENT) { |
ERR("%s: openat", f->path); |
ERR("%s: openat", f->path); |
return -1; |
return -1; |
|
|
struct sess *sess, int *fileoutfd) |
struct sess *sess, int *fileoutfd) |
{ |
{ |
struct blkset blk; |
struct blkset blk; |
struct stat st; |
|
void *mbuf, *bufp; |
void *mbuf, *bufp; |
ssize_t msz; |
ssize_t msz; |
size_t i, pos, sz; |
size_t i, pos, sz; |
off_t offs; |
off_t offs, filesize; |
int c; |
int c; |
|
|
/* Once finished this should never get called again. */ |
/* Once finished this should never get called again. */ |
|
|
if (S_ISDIR(u->fl[u->idx].st.mode)) |
if (S_ISDIR(u->fl[u->idx].st.mode)) |
c = pre_dir(u, sess); |
c = pre_dir(u, sess); |
else if (S_ISLNK(u->fl[u->idx].st.mode)) |
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)) |
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) || |
else if (S_ISBLK(u->fl[u->idx].st.mode) || |
S_ISCHR(u->fl[u->idx].st.mode)) |
S_ISCHR(u->fl[u->idx].st.mode)) |
c = pre_dev(u, sess); |
c = pre_dev(u, sess); |
|
|
memset(&blk, 0, sizeof(struct blkset)); |
memset(&blk, 0, sizeof(struct blkset)); |
blk.csum = u->csumlen; |
blk.csum = u->csumlen; |
|
|
if (*fileinfd != -1 && st.st_size > 0) { |
if (*fileinfd != -1 && filesize > 0) { |
init_blkset(&blk, st.st_size); |
init_blkset(&blk, filesize); |
assert(blk.blksz); |
assert(blk.blksz); |
|
|
blk.blks = calloc(blk.blksz, sizeof(struct blk)); |
blk.blks = calloc(blk.blksz, sizeof(struct blk)); |
|
|
/* Make sure the block metadata buffer is big enough. */ |
/* Make sure the block metadata buffer is big enough. */ |
|
|
u->bufsz = |
u->bufsz = |
sizeof(int32_t) + /* identifier */ |
sizeof(int32_t) + /* identifier */ |
sizeof(int32_t) + /* block count */ |
sizeof(int32_t) + /* block count */ |
sizeof(int32_t) + /* block length */ |
sizeof(int32_t) + /* block length */ |
sizeof(int32_t) + /* checksum length */ |
sizeof(int32_t) + /* checksum length */ |
sizeof(int32_t) + /* block remainder */ |
sizeof(int32_t) + /* block remainder */ |
blk.blksz * |
blk.blksz * |
(sizeof(int32_t) + /* short checksum */ |
(sizeof(int32_t) + /* short checksum */ |
blk.csum); /* long checksum */ |
blk.csum); /* long checksum */ |
|
|
if (u->bufsz > u->bufmax) { |
if (u->bufsz > u->bufmax) { |
if ((bufp = realloc(u->buf, u->bufsz)) == NULL) { |
if ((bufp = realloc(u->buf, u->bufsz)) == NULL) { |