X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/6755a7d7426dcc9b887f80f066021dbacffc7b10..3f0211b63a6cdc4a2cecfd2a0dffeba172c86a47:/rsync.c diff --git a/rsync.c b/rsync.c index 1ae33684..c5bcbb5a 100644 --- a/rsync.c +++ b/rsync.c @@ -6,8 +6,9 @@ * Copyright (C) 2003-2007 Wayne Davison * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,6 +20,7 @@ */ #include "rsync.h" +#include "ifuncs.h" #if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET #include #elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO @@ -32,7 +34,6 @@ extern int preserve_xattrs; extern int preserve_perms; extern int preserve_executability; extern int preserve_times; -extern int omit_dir_times; extern int am_root; extern int am_server; extern int am_sender; @@ -55,29 +56,28 @@ extern char *iconv_opt; #ifdef ICONV_CONST iconv_t ic_chck = (iconv_t)-1; -#ifdef ICONV_OPTION +# ifdef ICONV_OPTION iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1; -int ic_ndx; -#endif +# endif static const char *default_charset(void) { -#if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET +# if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET return locale_charset(); -#elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO +# elif defined HAVE_LANGINFO_H && defined HAVE_NL_LANGINFO return nl_langinfo(CODESET); -#else +# else return ""; /* Works with (at the very least) gnu iconv... */ -#endif +# endif } -void setup_iconv() +void setup_iconv(void) { const char *defset = default_charset(); # ifdef ICONV_OPTION const char *charset; char *cp; -#endif +# endif if (!am_server && !allow_8bit_chars) { @@ -126,9 +126,6 @@ void setup_iconv() exit_cleanup(RERR_UNSUPPORTED); } - if (!am_sender || inc_recurse) - ic_ndx = ++file_extra_cnt; - if (verbose > 1) { rprintf(FINFO, "%s charset: %s\n", am_server ? "server" : "client", @@ -136,6 +133,76 @@ void setup_iconv() } # 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'. + * 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. */ +int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags) +{ + ICONV_CONST char *ibuf; + size_t icnt, ocnt; + char *obuf; + + if (!out->size && flags & ICB_EXPAND_OUT) + alloc_xbuf(out, 1024); + + if (!in->pos) + 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; + + while (icnt) { + while (iconv(ic, &ibuf, &icnt, &obuf, &ocnt) == (size_t)-1) { + if (errno == EINTR) + continue; + if (errno == EINVAL) { + if (!(flags & ICB_INCLUDE_INCOMPLETE)) + goto finish; + } else if (errno == EILSEQ) { + if (!(flags & ICB_INCLUDE_BAD)) + goto finish; + } else { + size_t opos = obuf - out->buf; + if (!(flags & ICB_EXPAND_OUT)) { + errno = E2BIG; + goto finish; + } + realloc_xbuf(out, out->size + 1024); + obuf = out->buf + opos; + ocnt += 1024; + continue; + } + *obuf++ = *ibuf++; + ocnt--, icnt--; + } + } + + errno = 0; + + finish: + in->len = icnt; + in->pos = ibuf - in->buf; + out->len = obuf - out->buf - out->pos; + + return errno ? -1 : 0; +} #endif int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, @@ -144,7 +211,7 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, int len, iflags = 0; struct file_list *flist; uchar fnamecmp_type = FNAMECMP_FNAME; - int ndx; + int ndx, save_verbose = verbose; read_loop: while (1) { @@ -172,15 +239,17 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, } /* Send everything read from f_in to msg_fd_out. */ - send_msg_int(MSG_FLIST, ndx); - start_flist_forward(f_in); if (verbose > 3) { rprintf(FINFO, "[%s] receiving flist for dir %d\n", who_am_i(), ndx); } + verbose = 0; + send_msg_int(MSG_FLIST, ndx); + start_flist_forward(f_in); flist = recv_file_list(f_in); flist->parent_ndx = ndx; stop_flist_forward(); + verbose = save_verbose; } iflags = protocol_version >= 29 ? read_shortint(f_in) @@ -198,7 +267,8 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, invalid_ndx: rprintf(FERROR, "Invalid file index: %d (%d - %d) with iflags %x [%s]\n", - ndx, first_flist->ndx_start - 1, first_flist->prev->ndx_end, + ndx, first_flist ? first_flist->ndx_start - 1 : -1, + first_flist ? first_flist->prev->ndx_end : -1, iflags, who_am_i()); exit_cleanup(RERR_PROTOCOL); } @@ -267,19 +337,20 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms, return new_mode; } -int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp, +int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, const char *fnamecmp, int flags) { int updated = 0; - statx sx2; + stat_x sx2; int change_uid, change_gid; mode_t new_mode = file->mode; + int inherit; if (!sxp) { if (dry_run) return 1; if (link_stat(fname, &sx2.st, 0) < 0) { - rsyserr(FERROR, errno, "stat %s failed", + rsyserr(FERROR_XFER, errno, "stat %s failed", full_fname(fname)); return 0; } @@ -289,13 +360,15 @@ int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp, #ifdef SUPPORT_XATTRS sx2.xattr = NULL; #endif - if (!preserve_perms && S_ISDIR(new_mode) - && sx2.st.st_mode & S_ISGID) { - /* We just created this directory and its setgid - * bit is on, so make sure it stays on. */ - new_mode |= S_ISGID; - } sxp = &sx2; + inherit = !preserve_perms; + } else + inherit = !preserve_perms && file->flags & FLAG_DIR_CREATED; + + if (inherit && S_ISDIR(new_mode) && sxp->st.st_mode & S_ISGID) { + /* We just created this directory and its setgid + * bit is on, so make sure it stays on. */ + new_mode |= S_ISGID; } #ifdef SUPPORT_ACLS @@ -310,13 +383,13 @@ int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp, set_stat_xattr(fname, file); #endif - if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times)) + if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && preserve_times == 1)) 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); if (ret < 0) { - rsyserr(FERROR, errno, "failed to set times on %s", + rsyserr(FERROR_XFER, errno, "failed to set times on %s", full_fname(fname)); goto cleanup; } @@ -328,9 +401,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp, 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 - if (S_ISLNK(sxp->st.st_mode)) + if (S_ISLNK(sxp->st.st_mode)) { ; - else + } else #endif if (change_uid || change_gid) { if (verbose > 2) { @@ -352,7 +425,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp, change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid) != 0) { /* shouldn't have attempted to change uid or gid * unless have the privilege */ - rsyserr(FERROR, errno, "%s %s failed", + rsyserr(FERROR_XFER, errno, "%s %s failed", change_uid ? "chown" : "chgrp", full_fname(fname)); goto cleanup; @@ -385,7 +458,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp, if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) { int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode); if (ret < 0) { - rsyserr(FERROR, errno, + rsyserr(FERROR_XFER, errno, "failed to set permissions on %s", full_fname(fname)); goto cleanup; @@ -460,7 +533,7 @@ void finish_transfer(const char *fname, const char *fnametmp, ret = robust_rename(fnametmp, fname, partialptr, file->mode & INITACCESSPERMS); if (ret < 0) { - rsyserr(FERROR, errno, "%s %s -> \"%s\"", + rsyserr(FERROR_XFER, errno, "%s %s -> \"%s\"", ret == -2 ? "copy" : "rename", full_fname(fnametmp), fname); do_unlink(fnametmp); @@ -480,7 +553,7 @@ void finish_transfer(const char *fname, const char *fnametmp, if (partialptr) { if (do_rename(fnametmp, fname) < 0) { - rsyserr(FERROR, errno, "rename %s -> \"%s\"", + rsyserr(FERROR_XFER, errno, "rename %s -> \"%s\"", full_fname(fnametmp), fname); } else handle_partial_dir(partialptr, PDIR_DELETE);