X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/eabc85ef5ecd7ed9892ee3d9562468dbb84cbef5..aa3faf5f8c2a05110bb1f39fd8d0742d5ca0431e:/rsync.c diff --git a/rsync.c b/rsync.c index 0e635b03..17ce76a4 100644 --- a/rsync.c +++ b/rsync.c @@ -3,7 +3,7 @@ * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras - * Copyright (C) 2003-2008 Wayne Davison + * Copyright (C) 2003-2009 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ 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; @@ -45,7 +46,7 @@ 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; @@ -80,23 +81,23 @@ void setup_iconv(void) # endif if (!am_server && !allow_8bit_chars) { - /* It's OK if this fails... */ ic_chck = iconv_open(defset, defset); - if (DEBUG_GTE(ICONV, 1)) { + if (DEBUG_GTE(ICONV, 2)) { if (ic_chck == (iconv_t)-1) { rprintf(FINFO, - "note: iconv_open(\"%s\", \"%s\") failed (%d)" - " -- using isprint() instead of iconv().\n", + "msg checking via isprint()" + " (iconv_open(\"%s\", \"%s\") errno: %d)\n", defset, defset, errno); } else { rprintf(FINFO, - "note: iconv_open(\"%s\", \"%s\") succeeded.\n", - defset, defset); + "msg checking charset: %s\n", + defset); } } - } + } else + ic_chck = (iconv_t)-1; # ifdef ICONV_OPTION if (!iconv_opt) @@ -126,47 +127,82 @@ void setup_iconv(void) exit_cleanup(RERR_UNSUPPORTED); } - if (INFO_GTE(MISC, 2)) { - rprintf(FINFO, "%s charset: %s\n", - am_server ? "server" : "client", - *charset ? charset : "[LOCALE]"); + if (DEBUG_GTE(ICONV, 1)) { + rprintf(FINFO, "[%s] charset: %s\n", + who_am_i(), *charset ? charset : "[LOCALE]"); } # 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) { @@ -178,16 +214,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--; @@ -197,9 +249,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; } @@ -221,16 +277,19 @@ void send_protected_args(int fd, char *args[]) if (DEBUG_GTE(CMD, 1)) print_child_argv("protected args:", args + i + 1); do { + if (!args[i][0]) + write_buf(fd, ".", 2); #ifdef ICONV_OPTION - if (convert) { + 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; - } else + } #endif + else write_buf(fd, args[i], strlen(args[i]) + 1); } while (args[++i]); write_byte(fd, 0); @@ -241,7 +300,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; @@ -257,61 +316,71 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, break; if (ndx == NDX_DONE) return ndx; - if (!inc_recurse || am_sender) - goto invalid_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) + last = first_flist->prev->ndx_start + first_flist->prev->used - 1; + else + last = -1; + rprintf(FERROR, + "Invalid file index: %d (%d - %d) [%s]\n", + ndx, NDX_DONE, last, who_am_i()); + exit_cleanup(RERR_PROTOCOL); + } 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; if (ndx < 0 || ndx >= dir_flist->used) { ndx = NDX_FLIST_OFFSET - ndx; rprintf(FERROR, - "[%s] Invalid dir index: %d (%d - %d)\n", - who_am_i(), ndx, NDX_FLIST_OFFSET, - NDX_FLIST_OFFSET - dir_flist->used + 1); + "Invalid dir index: %d (%d - %d) [%s]\n", + ndx, NDX_FLIST_OFFSET, + NDX_FLIST_OFFSET - dir_flist->used + 1, + who_am_i()); 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; } - if (!(flist = flist_for_ndx(ndx))) { - int start, used; - invalid_ndx: - start = first_flist ? first_flist->ndx_start : 0; - used = first_flist ? first_flist->used : 0; - rprintf(FERROR, - "Invalid file index: %d (%d - %d) with iflags %x [%s]\n", - ndx, start - 1, start + used -1, iflags, who_am_i()); - exit_cleanup(RERR_PROTOCOL); + 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; + } } - cur_flist = flist; if (iflags & ITEM_BASIS_TYPE_FOLLOWS) fnamecmp_type = read_byte(f_in); @@ -393,12 +462,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, full_fname(fname)); return 0; } -#ifdef SUPPORT_ACLS - sx2.acc_acl = sx2.def_acl = NULL; -#endif -#ifdef SUPPORT_XATTRS - sx2.xattr = NULL; -#endif + init_stat_x(&sx2); sxp = &sx2; inherit = !preserve_perms; } else @@ -425,11 +489,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)); @@ -444,7 +510,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 @@ -491,8 +557,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 @@ -564,9 +632,10 @@ int finish_transfer(const char *fname, const char *fnametmp, } if (make_backups > 0 && overwriting_basis) { - if (!make_backup(fname)) + int ok = make_backup(fname, False); + if (!ok) return 1; - if (fnamecmp == fname) + if (ok == 1 && fnamecmp == fname) fnamecmp = get_backup_name(fname); } @@ -577,15 +646,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; } @@ -612,28 +679,48 @@ int finish_transfer(const char *fname, const char *fnametmp, return 1; } -struct file_list *flist_for_ndx(int ndx) +struct file_list *flist_for_ndx(int ndx, const char *fatal_error_loc) { struct file_list *flist = cur_flist; if (!flist && !(flist = first_flist)) - return NULL; + goto not_found; while (ndx < flist->ndx_start-1) { if (flist == first_flist) - return NULL; + goto not_found; flist = flist->prev; } while (ndx >= flist->ndx_start + flist->used) { if (!(flist = flist->next)) - return NULL; + goto not_found; } return flist; + + not_found: + if (fatal_error_loc) { + int first, last; + if (first_flist) { + first = first_flist->ndx_start - 1; + last = first_flist->prev->ndx_start + first_flist->prev->used - 1; + } else { + first = 0; + last = -1; + } + rprintf(FERROR, + "File-list index %d not in %d - %d (%s) [%s]\n", + ndx, first, last, fatal_error_loc, who_am_i()); + exit_cleanup(RERR_PROTOCOL); + } + return NULL; } 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 */ }