X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/21cddef2b460098f5289b9cdbd592bf8f0f9e759..73b9b90a0b548d1177d2bf004c800ae6d9926a1f:/rsync.c diff --git a/rsync.c b/rsync.c index 2c026a2d..c42d5535 100644 --- a/rsync.c +++ b/rsync.c @@ -36,16 +36,15 @@ extern int preserve_times; extern int am_root; extern int am_server; extern int am_sender; +extern int am_receiver; extern int am_generator; extern int am_starting_up; extern int allow_8bit_chars; extern int protocol_version; -extern int uid_ndx; -extern int gid_ndx; extern int inc_recurse; extern int inplace; extern int flist_eof; -extern int msgs2stderr; +extern int file_old_total; extern int keep_dirlinks; extern int make_backups; extern struct file_list *cur_flist, *first_flist, *dir_flist; @@ -133,39 +132,75 @@ void setup_iconv(void) # endif } -/* This function converts the characters in the "in" xbuf into characters - * in the "out" xbuf. The "len" of the "in" xbuf is used starting from its - * "pos". The "size" of the "out" xbuf restricts how many characters can be - * stored, starting at its "pos+len" position. Note that the last byte of - * the buffer is never used, which reserves space for a terminating '\0'. +/* This function converts the chars in the "in" xbuf into characters in the + * "out" xbuf. The ".len" chars of the "in" xbuf is used starting from its + * ".pos". The ".size" of the "out" xbuf restricts how many characters can + * be stored, starting at its ".pos+.len" position. Note that the last byte + * of the "out" xbuf is not used, which reserves space for a trailing '\0' + * (though it is up to the caller to store a trailing '\0', as needed). + * * We return a 0 on success or a -1 on error. An error also sets errno to * E2BIG, EILSEQ, or EINVAL (see below); otherwise errno will be set to 0. - * The "in" xbuf is altered to update "pos" and "len". The "out" xbuf has - * data appended, and its "len" incremented. If ICB_EXPAND_OUT is set in - * "flags", the "out" xbuf will also be allocated if empty, and expanded if - * too small (so E2BIG will not be returned). If ICB_INCLUDE_BAD is set in - * "flags", any badly-encoded chars are included verbatim in the "out" xbuf, - * so EILSEQ will not be returned. Likewise for ICB_INCLUDE_INCOMPLETE with - * respect to an incomplete multi-byte char at the end, which ensures that - * EINVAL is not returned. Anytime "in.pos" is 0 we will reset the iconv() - * state prior to processing the characters. */ + * The "in" xbuf is altered to update ".pos" and ".len". The "out" xbuf has + * data appended, and its ".len" incremented (see below for a ".size" note). + * + * If ICB_CIRCULAR_OUT is set in "flags", the chars going into the "out" xbuf + * can wrap around to the start, and the xbuf may have its ".size" reduced + * (presumably by 1 byte) if the iconv code doesn't have space to store a + * multi-byte character at the physical end of the ".buf" (though no reducing + * happens if ".pos" is <= 1, since there is no room to wrap around). + * + * If ICB_EXPAND_OUT is set in "flags", the "out" xbuf will be allocated if + * empty, and (as long as ICB_CIRCULAR_OUT is not set) expanded if too small. + * This prevents the return of E2BIG (except for a circular xbuf). + * + * If ICB_INCLUDE_BAD is set in "flags", any badly-encoded chars are included + * verbatim in the "out" xbuf, so EILSEQ will not be returned. + * + * If ICB_INCLUDE_INCOMPLETE is set in "flags", any incomplete multi-byte + * chars are included, which ensures that EINVAL is not returned. + * + * If ICB_INIT is set, the iconv() conversion state is initialized prior to + * processing the characters. */ int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags) { ICONV_CONST char *ibuf; - size_t icnt, ocnt; + size_t icnt, ocnt, opos; char *obuf; - if (!out->size && flags & ICB_EXPAND_OUT) - alloc_xbuf(out, 1024); + if (!out->size && flags & ICB_EXPAND_OUT) { + size_t siz = ROUND_UP_1024(in->len * 2); + alloc_xbuf(out, siz); + } else if (out->len+1 >= out->size) { + /* There is no room to even start storing data. */ + if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) { + errno = E2BIG; + return -1; + } + realloc_xbuf(out, out->size + ROUND_UP_1024(in->len * 2)); + } - if (!in->pos) + if (flags & ICB_INIT) iconv(ic, NULL, 0, NULL, 0); ibuf = in->buf + in->pos; icnt = in->len; - obuf = out->buf + (out->pos + out->len); - ocnt = out->size - (out->pos + out->len) - 1; + opos = out->pos + out->len; + if (flags & ICB_CIRCULAR_OUT) { + if (opos >= out->size) { + opos -= out->size; + /* We know that out->pos is not 0 due to the "no room" check + * above, so this can't go "negative". */ + ocnt = out->pos - opos - 1; + } else { + /* Allow the use of all bytes to the physical end of the buffer + * unless pos is 0, in which case we reserve our trailing '\0'. */ + ocnt = out->size - opos - (out->pos ? 0 : 1); + } + } else + ocnt = out->size - opos - 1; + obuf = out->buf + opos; while (icnt) { while (iconv(ic, &ibuf, &icnt, &obuf, &ocnt) == (size_t)-1) { @@ -177,16 +212,32 @@ int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags) } else if (errno == EILSEQ) { if (!(flags & ICB_INCLUDE_BAD)) goto finish; - } else { - size_t opos = obuf - out->buf; - if (!(flags & ICB_EXPAND_OUT)) { + } else if (errno == E2BIG) { + size_t siz; + opos = obuf - out->buf; + if (flags & ICB_CIRCULAR_OUT && out->pos > 1 && opos > out->pos) { + /* We are in a divided circular buffer at the physical + * end with room to wrap to the start. If iconv() refused + * to use one or more trailing bytes in the buffer, we + * set the size to ignore the unused bytes. */ + if (opos < out->size) + reduce_iobuf_size(out, opos); + obuf = out->buf; + ocnt = out->pos - 1; + continue; + } + if (!(flags & ICB_EXPAND_OUT) || flags & ICB_CIRCULAR_OUT) { errno = E2BIG; goto finish; } - realloc_xbuf(out, out->size + 1024); + siz = ROUND_UP_1024(in->len * 2); + realloc_xbuf(out, out->size + siz); obuf = out->buf + opos; - ocnt += 1024; + ocnt += siz; continue; + } else { + rsyserr(FERROR, errno, "unexpected error from iconv()"); + exit_cleanup(RERR_UNSUPPORTED); } *obuf++ = *ibuf++; ocnt--, icnt--; @@ -196,9 +247,13 @@ int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags) errno = 0; finish: + opos = obuf - out->buf; + if (flags & ICB_CIRCULAR_OUT && opos < out->pos) + opos += out->size; + out->len = opos - out->pos; + in->len = icnt; in->pos = ibuf - in->buf; - out->len = obuf - out->buf - out->pos; return errno ? -1 : 0; } @@ -226,7 +281,7 @@ void send_protected_args(int fd, char *args[]) else if (convert) { INIT_XBUF_STRLEN(inbuf, args[i]); iconvbufs(ic_send, &inbuf, &outbuf, - ICB_EXPAND_OUT | ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE); + ICB_EXPAND_OUT | ICB_INCLUDE_BAD | ICB_INCLUDE_INCOMPLETE | ICB_INIT); outbuf.buf[outbuf.len] = '\0'; write_buf(fd, outbuf.buf, outbuf.len + 1); outbuf.len = 0; @@ -243,7 +298,7 @@ void send_protected_args(int fd, char *args[]) #endif } -int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, +int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, char *buf, int *len_ptr) { int len, iflags = 0; @@ -259,6 +314,12 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, break; if (ndx == NDX_DONE) return ndx; + if (ndx == NDX_DEL_STATS) { + read_del_stats(f_in); + if (am_sender && am_server) + write_del_stats(f_out); + continue; + } if (!inc_recurse || am_sender) { int last; if (first_flist) @@ -272,7 +333,9 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, } if (ndx == NDX_FLIST_EOF) { flist_eof = 1; - send_msg(MSG_FLIST_EOF, "", 0, 0); + if (DEBUG_GTE(FLIST, 3)) + rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i()); + write_int(f_out, NDX_FLIST_EOF); continue; } ndx = NDX_FLIST_OFFSET - ndx; @@ -286,34 +349,36 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, exit_cleanup(RERR_PROTOCOL); } - /* Send everything read from f_in to msg_fd_out. */ if (DEBUG_GTE(FLIST, 2)) { rprintf(FINFO, "[%s] receiving flist for dir %d\n", who_am_i(), ndx); } - if (!msgs2stderr) - negate_output_levels(); /* turn off all info/debug output */ - send_msg_int(MSG_FLIST, ndx); - start_flist_forward(f_in); + /* Send all the data we read for this flist to the generator. */ + start_flist_forward(ndx); flist = recv_file_list(f_in); flist->parent_ndx = ndx; stop_flist_forward(); - if (!msgs2stderr) - negate_output_levels(); /* restore info/debug output */ } iflags = protocol_version >= 29 ? read_shortint(f_in) : ITEM_TRANSFER | ITEM_MISSING_DATA; - /* Honor the old-style keep-alive indicator. */ - if (protocol_version < 30 - && ndx == cur_flist->used && iflags == ITEM_IS_NEW) { + /* Support the protocol-29 keep-alive style. */ + if (protocol_version < 30 && ndx == cur_flist->used && iflags == ITEM_IS_NEW) { if (am_sender) - maybe_send_keepalive(); + maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); goto read_loop; } - cur_flist = flist_for_ndx(ndx, "read_ndx_and_attrs"); + flist = flist_for_ndx(ndx, "read_ndx_and_attrs"); + if (flist != cur_flist) { + cur_flist = flist; + if (am_sender) { + file_old_total = cur_flist->used; + for (flist = first_flist; flist != cur_flist; flist = flist->next) + file_old_total += flist->used; + } + } if (iflags & ITEM_BASIS_TYPE_FOLLOWS) fnamecmp_type = read_byte(f_in); @@ -422,11 +487,13 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, set_xattr(fname, file, fnamecmp, sxp); #endif - if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && preserve_times == 1)) + if (!preserve_times + || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode)) + || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode))) flags |= ATTRS_SKIP_MTIME; if (!(flags & ATTRS_SKIP_MTIME) && cmp_time(sxp->st.st_mtime, file->modtime) != 0) { - int ret = set_modtime(fname, file->modtime, sxp->st.st_mode); + int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode); if (ret < 0) { rsyserr(FERROR_XFER, errno, "failed to set times on %s", full_fname(fname)); @@ -441,7 +508,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file); change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file); -#if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK +#ifndef CAN_CHOWN_SYMLINK if (S_ISLNK(sxp->st.st_mode)) { ; } else @@ -460,9 +527,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, } } if (am_root >= 0) { - if (do_lchown(fname, - change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid, - change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid) != 0) { + uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid; + gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid; + if (do_lchown(fname, uid, gid) != 0) { /* We shouldn't have attempted to change uid * or gid unless have the privilege. */ rsyserr(FERROR_XFER, errno, "%s %s failed", @@ -470,6 +537,10 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, full_fname(fname)); goto cleanup; } + if (uid == (uid_t)-1 && sxp->st.st_uid != (uid_t)-1) + rprintf(FERROR_XFER, "uid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname)); + if (gid == (gid_t)-1 && sxp->st.st_gid != (gid_t)-1) + rprintf(FERROR_XFER, "gid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname)); /* A lchown had been done, so we need to re-stat if * the destination had the setuid or setgid bits set * (due to the side effect of the chown call). */ @@ -488,8 +559,10 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, * If set_acl() changes permission bits in the process of setting * an access ACL, it changes sxp->st.st_mode so we know whether we * need to chmod(). */ - if (preserve_acls && !S_ISLNK(new_mode) && set_acl(fname, file, sxp) == 0) - updated = 1; + if (preserve_acls && !S_ISLNK(new_mode)) { + if (set_acl(fname, file, sxp, new_mode) > 0) + updated = 1; + } #endif #ifdef HAVE_CHMOD @@ -575,15 +648,13 @@ int finish_transfer(const char *fname, const char *fnametmp, /* move tmp file over real file */ if (DEBUG_GTE(RECV, 1)) rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname); - ret = robust_rename(fnametmp, fname, temp_copy_name, - file->mode & INITACCESSPERMS); + ret = robust_rename(fnametmp, fname, temp_copy_name, file->mode); if (ret < 0) { rsyserr(FERROR_XFER, errno, "%s %s -> \"%s\"", ret == -2 ? "copy" : "rename", full_fname(fnametmp), fname); if (!partialptr || (ret == -2 && temp_copy_name) - || robust_rename(fnametmp, partialptr, NULL, - file->mode & INITACCESSPERMS) < 0) + || robust_rename(fnametmp, partialptr, NULL, file->mode) < 0) do_unlink(fnametmp); return 0; } @@ -650,5 +721,8 @@ const char *who_am_i(void) { if (am_starting_up) return am_server ? "server" : "client"; - return am_sender ? "sender" : am_generator ? "generator" : "receiver"; + return am_sender ? "sender" + : am_generator ? "generator" + : am_receiver ? "receiver" + : "Receiver"; /* pre-forked receiver */ }