version 1.10, 2019/02/16 10:47:20 |
version 1.11, 2019/02/16 10:48:05 |
|
|
size_t bufpos; /* position in buf */ |
size_t bufpos; /* position in buf */ |
size_t idx; /* current transfer index */ |
size_t idx; /* current transfer index */ |
mode_t oumask; /* umask for creating files */ |
mode_t oumask; /* umask for creating files */ |
|
char *root; /* destination directory path */ |
int rootfd; /* destination directory */ |
int rootfd; /* destination directory */ |
size_t csumlen; /* checksum length */ |
size_t csumlen; /* checksum length */ |
int fdout; /* write descriptor to sender */ |
int fdout; /* write descriptor to sender */ |
|
|
pre_link(struct upload *p, struct sess *sess) |
pre_link(struct upload *p, struct sess *sess) |
{ |
{ |
struct stat st; |
struct stat st; |
struct timespec tv[2]; |
|
const struct flist *f; |
const struct flist *f; |
int rc, newlink = 0, updatelink = 0; |
int rc, newlink = 0, updatelink = 0; |
mode_t mode; |
char *b, *temp = NULL; |
char *b; |
|
|
|
f = &p->fl[p->idx]; |
f = &p->fl[p->idx]; |
assert(S_ISLNK(f->st.mode)); |
assert(S_ISLNK(f->st.mode)); |
|
|
LOG3(sess, "%s: creating " |
LOG3(sess, "%s: creating " |
"symlink: %s", f->path, f->link); |
"symlink: %s", f->path, f->link); |
|
|
if (mktemplate(&b, f->path, sess->opts->recursive) == -1) { |
if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { |
ERR(sess, "asprintf"); |
ERR(sess, "asprintf"); |
return -1; |
return -1; |
} |
} |
if (mkstemplinkat(f->link, p->rootfd, b) == NULL) { |
if (mkstemplinkat(f->link, p->rootfd, temp) == NULL) { |
WARN(sess, "%s: symlinkat", b); |
WARN(sess, "%s: symlinkat", temp); |
free(b); |
free(temp); |
return -1; |
return -1; |
} |
} |
newlink = 1; |
newlink = 1; |
} |
} |
|
|
/* |
rsync_set_metadata_at(sess, newlink, p->rootfd, f, |
* Optionally preserve times/perms on the symlink. |
newlink ? temp : f->path); |
* FIXME: run rsync_set_metadata()? |
|
*/ |
|
|
|
if (sess->opts->preserve_times) { |
if (newlink) { |
struct timeval now; |
if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { |
|
ERR(sess, "%s: renameat %s", temp, f->path); |
|
(void)unlinkat(p->rootfd, temp, 0); |
|
free(temp); |
|
return -1; |
|
} |
|
free(temp); |
|
} |
|
|
gettimeofday(&now, NULL); |
log_link(sess, f); |
TIMEVAL_TO_TIMESPEC(&now, &tv[0]); |
return 0; |
tv[1].tv_sec = f->st.mtime; |
} |
tv[1].tv_nsec = 0; |
|
rc = utimensat(p->rootfd, newlink ? b : f->path, tv, |
/* |
AT_SYMLINK_NOFOLLOW); |
* Return <0 on failure 0 on success. |
if (rc == -1) { |
*/ |
ERR(sess, "%s: futimes", f->path); |
static int |
if (newlink) { |
pre_dev(struct upload *p, struct sess *sess) |
(void)unlinkat(p->rootfd, b, 0); |
{ |
free(b); |
struct stat st; |
|
const struct flist *f; |
|
int rc, newdev = 0, updatedev = 0; |
|
char *temp = NULL; |
|
|
|
f = &p->fl[p->idx]; |
|
assert(S_ISBLK(f->st.mode) || S_ISCHR(f->st.mode)); |
|
|
|
if (!sess->opts->devices || getuid() != 0) { |
|
WARNX(sess, "skipping non-regular file %s", f->path); |
|
return 0; |
|
} else if (sess->opts->dry_run) { |
|
log_file(sess, f); |
|
return 0; |
|
} |
|
|
|
/* See if the dev already exists */ |
|
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; |
} |
} |
|
} |
|
rc = -1; /* overwrite object with dev */ |
|
} else if (rc == -1 && errno != ENOENT) { |
|
WARN(sess, "%s: fstatat", f->path); |
|
return -1; |
|
} |
|
|
|
/* |
|
* If the device already exists make sure it 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); |
|
updatedev = 1; |
|
} |
|
} |
|
|
|
if (rc == -1 || updatedev) { |
|
newdev = 1; |
|
if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { |
|
ERR(sess, "asprintf"); |
return -1; |
return -1; |
} |
} |
LOG4(sess, "%s: updated symlink date", f->path); |
if (mkstempnodat(p->rootfd, temp, f->st.mode & |
|
(S_IFCHR|S_IFBLK), f->st.rdev) == NULL) { |
|
WARN(sess, "%s: mknodat", temp); |
|
free(temp); |
|
return -1; |
|
} |
} |
} |
|
|
if (newlink || sess->opts->preserve_perms) { |
rsync_set_metadata_at(sess, newdev, p->rootfd, f, |
if (updatelink && !sess->opts->preserve_perms) |
newdev ? temp : f->path); |
/* carry over permissions from replaced symlink */ |
|
mode = st.st_mode; |
|
else |
|
mode = f->st.mode; |
|
|
|
rc = fchmodat(p->rootfd, newlink ? b : f->path, mode, |
if (newdev) { |
AT_SYMLINK_NOFOLLOW); |
if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { |
if (rc == -1) { |
ERR(sess, "%s: renameat %s", temp, f->path); |
ERR(sess, "%s: fchmodat", newlink ? b : f->path); |
(void)unlinkat(p->rootfd, temp, 0); |
if (newlink) { |
free(temp); |
(void)unlinkat(p->rootfd, b, 0); |
return -1; |
free(b); |
} |
|
free(temp); |
|
} |
|
log_file(sess, f); |
|
return 0; |
|
} |
|
|
|
/* |
|
* Return <0 on failure 0 on success. |
|
*/ |
|
static int |
|
pre_fifo(struct upload *p, struct sess *sess) |
|
{ |
|
struct stat st; |
|
const struct flist *f; |
|
int rc, newfifo = 0; |
|
char *temp = NULL; |
|
|
|
f = &p->fl[p->idx]; |
|
assert(S_ISFIFO(f->st.mode)); |
|
|
|
if (!sess->opts->specials) { |
|
WARNX(sess, "skipping non-regular file %s", f->path); |
|
return 0; |
|
} else if (sess->opts->dry_run) { |
|
log_file(sess, f); |
|
return 0; |
|
} |
|
|
|
/* See if the fifo already exists */ |
|
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; |
} |
} |
|
} |
|
rc = -1; /* overwrite object with fifo */ |
|
} else if (rc == -1 && errno != ENOENT) { |
|
WARN(sess, "%s: fstatat", f->path); |
|
return -1; |
|
} |
|
|
|
if (rc == -1) { |
|
newfifo = 1; |
|
if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { |
|
ERR(sess, "asprintf"); |
return -1; |
return -1; |
} |
} |
LOG4(sess, "%s: updated symlink mode", f->path); |
if (mkstempfifoat(p->rootfd, temp) == NULL) { |
|
WARN(sess, "%s: mkfifoat", temp); |
|
free(temp); |
|
return -1; |
|
} |
} |
} |
|
|
if (newlink) { |
rsync_set_metadata_at(sess, newfifo, p->rootfd, f, |
if (renameat(p->rootfd, b, p->rootfd, f->path) == -1) { |
newfifo ? temp : f->path); |
ERR(sess, "%s: renameat %s", b, f->path); |
|
(void)unlinkat(p->rootfd, b, 0); |
if (newfifo) { |
free(b); |
if (renameat(p->rootfd, temp, p->rootfd, f->path) == -1) { |
|
ERR(sess, "%s: renameat %s", temp, f->path); |
|
(void)unlinkat(p->rootfd, temp, 0); |
|
free(temp); |
return -1; |
return -1; |
} |
} |
free(b); |
free(temp); |
} |
} |
|
log_file(sess, f); |
|
return 0; |
|
} |
|
|
log_link(sess, f); |
/* |
|
* Return <0 on failure 0 on success. |
|
*/ |
|
static int |
|
pre_sock(struct upload *p, struct sess *sess) |
|
{ |
|
struct stat st; |
|
const struct flist *f; |
|
int rc, newsock = 0; |
|
char *temp = NULL; |
|
|
|
f = &p->fl[p->idx]; |
|
assert(S_ISSOCK(f->st.mode)); |
|
|
|
if (!sess->opts->specials) { |
|
WARNX(sess, "skipping non-regular file %s", f->path); |
|
return 0; |
|
} else if (sess->opts->dry_run) { |
|
log_file(sess, f); |
|
return 0; |
|
} |
|
|
|
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; |
|
} |
|
} |
|
rc = -1; /* overwrite object with sock */ |
|
} else if (rc == -1 && errno != ENOENT) { |
|
WARN(sess, "%s: fstatat", f->path); |
|
return -1; |
|
} |
|
|
|
if (rc == -1) { |
|
newsock = 1; |
|
|
|
if (mktemplate(&temp, f->path, sess->opts->recursive) == -1) { |
|
ERR(sess, "asprintf"); |
|
return -1; |
|
} |
|
|
|
if (mkstempsock(p->root, temp) == NULL) { |
|
WARN(sess, "%s: mksockat", temp); |
|
free(temp); |
|
return -1; |
|
} |
|
} |
|
|
|
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) { |
|
ERR(sess, "%s: renameat %s", temp, f->path); |
|
(void)unlinkat(p->rootfd, temp, 0); |
|
free(temp); |
|
return -1; |
|
} |
|
free(temp); |
|
} |
|
log_file(sess, f); |
return 0; |
return 0; |
} |
} |
|
|
|
|
* On success, upload_free() must be called with the allocated pointer. |
* On success, upload_free() must be called with the allocated pointer. |
*/ |
*/ |
struct upload * |
struct upload * |
upload_alloc(struct sess *sess, int rootfd, int fdout, |
upload_alloc(struct sess *sess, const char *root, int rootfd, int fdout, |
size_t clen, const struct flist *fl, size_t flsz, mode_t msk) |
size_t clen, const struct flist *fl, size_t flsz, mode_t msk) |
{ |
{ |
struct upload *p; |
struct upload *p; |
|
|
|
|
p->state = UPLOAD_FIND_NEXT; |
p->state = UPLOAD_FIND_NEXT; |
p->oumask = msk; |
p->oumask = msk; |
|
p->root = strdup(root); |
|
if (p->root == NULL) { |
|
ERR(sess, "strdup"); |
|
free(p); |
|
return NULL; |
|
} |
p->rootfd = rootfd; |
p->rootfd = rootfd; |
p->csumlen = clen; |
p->csumlen = clen; |
p->fdout = fdout; |
p->fdout = fdout; |
|
|
p->newdir = calloc(flsz, sizeof(int)); |
p->newdir = calloc(flsz, sizeof(int)); |
if (p->newdir == NULL) { |
if (p->newdir == NULL) { |
ERR(sess, "calloc"); |
ERR(sess, "calloc"); |
|
free(p->root); |
free(p); |
free(p); |
return NULL; |
return NULL; |
} |
} |
|
|
|
|
if (p == NULL) |
if (p == NULL) |
return; |
return; |
|
free(p->root); |
free(p->newdir); |
free(p->newdir); |
free(p->buf); |
free(p->buf); |
free(p); |
free(p); |
|
|
c = pre_link(u, sess); |
c = pre_link(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, sess); |
c = pre_file(u, fileinfd, sess); |
|
else if (S_ISBLK(u->fl[u->idx].st.mode) || |
|
S_ISCHR(u->fl[u->idx].st.mode)) |
|
c = pre_dev(u, sess); |
|
else if (S_ISFIFO(u->fl[u->idx].st.mode)) |
|
c = pre_fifo(u, sess); |
|
else if (S_ISSOCK(u->fl[u->idx].st.mode)) |
|
c = pre_sock(u, sess); |
else |
else |
c = 0; |
c = 0; |
|
|