--- embedaddon/rsync/rsync.c 2012/02/17 15:09:30 1.1 +++ embedaddon/rsync/rsync.c 2021/03/17 00:32:36 1.1.1.4 @@ -3,7 +3,7 @@ * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras - * Copyright (C) 2003-2009 Wayne Davison + * Copyright (C) 2003-2020 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 @@ -27,32 +27,49 @@ #include #endif -extern int verbose; extern int dry_run; extern int preserve_acls; extern int preserve_xattrs; extern int preserve_perms; +extern int preserve_fileflags; extern int preserve_executability; extern int preserve_times; +extern int copy_devices; extern int am_root; extern int am_server; +extern int am_daemon; extern int am_sender; extern int am_receiver; extern int am_generator; +extern int am_dbadmin; extern int am_starting_up; extern int allow_8bit_chars; extern int protocol_version; +extern int got_kill_signal; +extern int called_from_signal_handler; extern int inc_recurse; extern int inplace; extern int flist_eof; +extern int file_old_total; extern int keep_dirlinks; extern int make_backups; +extern char *link_by_hash_dir; +extern int sanitize_paths; extern struct file_list *cur_flist, *first_flist, *dir_flist; extern struct chmod_mode_struct *daemon_chmod_modes; #ifdef ICONV_OPTION extern char *iconv_opt; #endif +#define UPDATED_OWNER (1<<0) +#define UPDATED_GROUP (1<<1) +#define UPDATED_MTIME (1<<2) +#define UPDATED_ATIME (1<<3) +#define UPDATED_ACLS (1<<4) +#define UPDATED_MODE (1<<5) + +#define UPDATED_TIMES (UPDATED_MTIME|UPDATED_ATIME) + #ifdef ICONV_CONST iconv_t ic_chck = (iconv_t)-1; # ifdef ICONV_OPTION @@ -82,19 +99,20 @@ void setup_iconv(void) /* It's OK if this fails... */ ic_chck = iconv_open(defset, defset); - if (verbose > 3) { + 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) @@ -124,47 +142,82 @@ void setup_iconv(void) exit_cleanup(RERR_UNSUPPORTED); } - if (verbose > 1) { - 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) { @@ -173,31 +226,58 @@ int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int fla if (errno == EINVAL) { if (!(flags & ICB_INCLUDE_INCOMPLETE)) goto finish; + if (!ocnt) + goto e2big; } else if (errno == EILSEQ) { if (!(flags & ICB_INCLUDE_BAD)) goto finish; - } else { - size_t opos = obuf - out->buf; - if (!(flags & ICB_EXPAND_OUT)) { + if (!ocnt) + goto e2big; + } else if (errno == E2BIG) { + size_t siz; + e2big: + 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--; + if (!icnt) + break; } } 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; } @@ -216,7 +296,7 @@ void send_protected_args(int fd, char *args[]) for (i = 0; args[i]; i++) {} /* find first NULL */ args[i] = "rsync"; /* set a new arg0 */ - if (verbose > 1) + if (DEBUG_GTE(CMD, 1)) print_child_argv("protected args:", args + i + 1); do { if (!args[i][0]) @@ -225,7 +305,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; @@ -242,13 +322,12 @@ void send_protected_args(int fd, char *args[]) #endif } -int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr, - char *buf, int *len_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; struct file_list *flist; uchar fnamecmp_type = FNAMECMP_FNAME; - int ndx, save_verbose = verbose; + int ndx; read_loop: while (1) { @@ -258,6 +337,12 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar 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) @@ -271,7 +356,9 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar } 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; @@ -285,18 +372,15 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar exit_cleanup(RERR_PROTOCOL); } - /* Send everything read from f_in to msg_fd_out. */ - if (verbose > 3) { + if (DEBUG_GTE(FLIST, 2)) { 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); + /* Send all the data we read for this flist to the generator. */ + start_flist_forward(ndx); + flist = recv_file_list(f_in, ndx); flist->parent_ndx = ndx; stop_flist_forward(); - verbose = save_verbose; } iflags = protocol_version >= 29 ? read_shortint(f_in) @@ -305,11 +389,19 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar /* 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); @@ -318,6 +410,11 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar if (iflags & ITEM_XNAME_FOLLOWS) { if ((len = read_vstring(f_in, buf, MAXPATHLEN)) < 0) exit_cleanup(RERR_PROTOCOL); + + if (sanitize_paths) { + sanitize_path(buf, buf, "", 0, SP_DEFAULT); + len = strlen(buf); + } } else { *buf = '\0'; len = -1; @@ -326,7 +423,8 @@ int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar if (iflags & ITEM_TRANSFER) { int i = ndx - cur_flist->ndx_start; - if (i < 0 || !S_ISREG(cur_flist->files[i]->mode)) { + struct file_struct *file = cur_flist->files[i]; + if (i < 0 || !(S_ISREG(file->mode) || (copy_devices && IS_DEVICE(file->mode)))) { rprintf(FERROR, "received request to transfer non-regular file: %d [%s]\n", ndx, who_am_i()); @@ -374,6 +472,54 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, return new_mode; } +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE +/* Set a file's st_flags. */ +static int set_fileflags(const char *fname, uint32 fileflags) +{ + if (do_chflags(fname, fileflags) != 0) { + rsyserr(FERROR_XFER, errno, + "failed to set file flags on %s", + full_fname(fname)); + return 0; + } + + return 1; +} + +/* Remove immutable flags from an object, so it can be altered/removed. */ +int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags) +{ + if (S_ISLNK(mode) || !(fileflags & iflags)) + return 0; + if (!set_fileflags(fname, fileflags & ~iflags)) + return -1; + return 1; +} + +/* Undo a prior make_mutable() call that returned a 1. */ +int undo_make_mutable(const char *fname, uint32 fileflags) +{ + if (!set_fileflags(fname, fileflags)) + return -1; + return 1; +} +#endif + +static int same_mtime(struct file_struct *file, STRUCT_STAT *st, int extra_accuracy) +{ +#ifdef ST_MTIME_NSEC + uint32 f1_nsec = F_MOD_NSEC_or_0(file); + uint32 f2_nsec = (uint32)st->ST_MTIME_NSEC; +#else + uint32 f1_nsec = 0, f2_nsec = 0; +#endif + + if (extra_accuracy) /* ignore modify_window when setting the time after a transfer or checksum check */ + return file->modtime == st->st_mtime && f1_nsec == f2_nsec; + + return same_time(file->modtime, f1_nsec, st->st_mtime , f2_nsec); +} + int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, const char *fnamecmp, int flags) { @@ -391,12 +537,7 @@ int set_file_attrs(const char *fname, struct file_stru 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 @@ -416,41 +557,18 @@ int set_file_attrs(const char *fname, struct file_stru get_acl(fname, sxp); #endif -#ifdef SUPPORT_XATTRS - if (am_root < 0) - set_stat_xattr(fname, file, new_mode); - if (preserve_xattrs && fnamecmp) - set_xattr(fname, file, fnamecmp, sxp); -#endif - - 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); - if (ret < 0) { - rsyserr(FERROR_XFER, errno, "failed to set times on %s", - full_fname(fname)); - goto cleanup; - } - if (ret == 0) /* ret == 1 if symlink could not be set */ - updated = 1; - else - file->flags |= FLAG_TIME_FAILED; - } - - change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file); + change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file) + && !(omit_dir_changes && S_ISDIR(sxp->st.st_mode)); change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP) - && sxp->st.st_gid != (gid_t)F_GROUP(file); + && sxp->st.st_gid != (gid_t)F_GROUP(file) + && !(omit_dir_changes && S_ISDIR(sxp->st.st_mode)); #ifndef CAN_CHOWN_SYMLINK if (S_ISLNK(sxp->st.st_mode)) { ; } else #endif if (change_uid || change_gid) { - if (verbose > 2) { + if (DEBUG_GTE(OWN, 1)) { if (change_uid) { rprintf(FINFO, "set uid of %s from %u to %u\n", @@ -465,12 +583,12 @@ int set_file_attrs(const char *fname, struct file_stru if (am_root >= 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) { + if (do_lchown(fname, uid, gid, sxp->st.st_mode, ST_FLAGS(sxp->st)) != 0) { /* We shouldn't have attempted to change uid * or gid unless have the privilege. */ rsyserr(FERROR_XFER, errno, "%s %s failed", - change_uid ? "chown" : "chgrp", - full_fname(fname)); + change_uid ? "chown" : "chgrp", + full_fname(fname)); goto cleanup; } if (uid == (uid_t)-1 && sxp->st.st_uid != (uid_t)-1) @@ -485,9 +603,80 @@ int set_file_attrs(const char *fname, struct file_stru keep_dirlinks && S_ISDIR(sxp->st.st_mode)); } } - updated = 1; + if (change_uid) + updated |= UPDATED_OWNER; + if (change_gid) + updated |= UPDATED_GROUP; } +#ifdef SUPPORT_XATTRS + if (am_root < 0) + set_stat_xattr(fname, file, new_mode); + if (preserve_xattrs && fnamecmp) { + uint32 tmpflags = sxp->st.st_flags; + sxp->st.st_flags = F_FFLAGS(file); /* set_xattr() needs to check UF_COMPRESSED */ + set_xattr(fname, file, fnamecmp, sxp); + sxp->st.st_flags = tmpflags; + if (S_ISDIR(sxp->st.st_mode)) + link_stat(fname, &sx2.st, 0); + } +#endif + + 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 | ATTRS_SKIP_ATIME; + else if (sxp != &sx2) + memcpy(&sx2.st, &sxp->st, sizeof (sx2.st)); + if (!atimes_ndx || S_ISDIR(sxp->st.st_mode)) + flags |= ATTRS_SKIP_ATIME; + /* Don't set the creation date on the root folder of an HFS+ volume. */ + if (sxp->st.st_ino == 2 && S_ISDIR(sxp->st.st_mode)) + flags |= ATTRS_SKIP_CRTIME; + if (!(flags & ATTRS_SKIP_MTIME) +#ifdef SUPPORT_HFS_COMPRESSION + && !(sxp->st.st_flags & UF_COMPRESSED) /* setting this alters mtime, so defer to after set_fileflags */ +#endif + && !same_mtime(file, &sxp->st, flags & ATTRS_ACCURATE_TIME)) { + sx2.st.st_mtime = file->modtime; +#ifdef ST_MTIME_NSEC + sx2.st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file); +#endif + updated |= UPDATED_MTIME; + } + if (!(flags & ATTRS_SKIP_ATIME)) { + time_t file_atime = F_ATIME(file); + if (flags & ATTRS_ACCURATE_TIME || !same_time(sxp->st.st_atime, 0, file_atime, 0)) { + sx2.st.st_atime = file_atime; +#ifdef ST_ATIME_NSEC + sx2.st.ST_ATIME_NSEC = 0; +#endif + updated |= UPDATED_ATIME; + } + } + if (updated & UPDATED_TIMES) { + int ret = set_times(fname, &sx2.st); + if (ret < 0) { + rsyserr(FERROR_XFER, errno, "failed to set times on %s", + full_fname(fname)); + goto cleanup; + } + if (ret > 0) { /* ret == 1 if symlink could not be set */ + updated &= ~UPDATED_TIMES; + file->flags |= FLAG_TIME_FAILED; + } + } +#ifdef SUPPORT_CRTIMES + if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) { + time_t file_crtime = F_CRTIME(file); + if (sxp->crtime == 0) + sxp->crtime = get_create_time(fname); + if (!same_time(sxp->crtime, 0L, file_crtime, 0L) + && set_create_time(fname, file_crtime) == 0) + updated = 1; + } +#endif + #ifdef SUPPORT_ACLS /* It's OK to call set_acl() now, even for a dir, as the generator * will enable owner-writability using chmod, if necessary. @@ -497,13 +686,13 @@ int set_file_attrs(const char *fname, struct file_stru * need to chmod(). */ if (preserve_acls && !S_ISLNK(new_mode)) { if (set_acl(fname, file, sxp, new_mode) > 0) - updated = 1; + updated |= UPDATED_ACLS; } #endif #ifdef HAVE_CHMOD if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) { - int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode); + int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode, ST_FLAGS(sxp->st)); if (ret < 0) { rsyserr(FERROR_XFER, errno, "failed to set permissions on %s", @@ -511,32 +700,50 @@ int set_file_attrs(const char *fname, struct file_stru goto cleanup; } if (ret == 0) /* ret == 1 if symlink could not be set */ - updated = 1; + updated |= UPDATED_MODE; } #endif - if (verbose > 1 && flags & ATTRS_REPORT) { +#ifdef SUPPORT_FILEFLAGS + if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode) + && sxp->st.st_flags != F_FFLAGS(file)) { + uint32 fileflags = F_FFLAGS(file); + if (flags & ATTRS_DELAY_IMMUTABLE) + fileflags &= ~ALL_IMMUTABLE; + if (sxp->st.st_flags != fileflags + && !set_fileflags(fname, fileflags)) + goto cleanup; + updated = 1; +#ifdef SUPPORT_HFS_COMPRESSION + int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), new_mode, fileflags); + if (ret < 0) { + rsyserr(FERROR_XFER, errno, "failed to set times on %s", + full_fname(fname)); + goto cleanup; + } + if (ret != 0) + file->flags |= FLAG_TIME_FAILED; +#endif + } +#endif + + if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) { if (updated) rprintf(FCLIENT, "%s\n", fname); else rprintf(FCLIENT, "%s is uptodate\n", fname); } cleanup: - if (sxp == &sx2) { -#ifdef SUPPORT_ACLS - if (preserve_acls) - free_acl(&sx2); -#endif -#ifdef SUPPORT_XATTRS - if (preserve_xattrs) - free_xattr(&sx2); -#endif - } + if (sxp == &sx2) + free_stat_x(&sx2); return updated; } -RETSIGTYPE sig_int(UNUSED(int val)) +/* This is only called for SIGINT, SIGHUP, and SIGTERM. */ +void sig_int(int sig_num) { + called_from_signal_handler = 1; + /* KLUGE: if the user hits Ctrl-C while ssh is prompting * for a password, then our cleanup's sending of a SIGUSR1 * signal to all our children may kill ssh before it has a @@ -546,6 +753,24 @@ RETSIGTYPE sig_int(UNUSED(int val)) * not ssh waiting for a password, then this tiny delay * shouldn't hurt anything. */ msleep(400); + + /* If we're an rsync daemon listener (not a daemon server), + * we'll exit with status 0 if we received SIGTERM. */ + if (am_daemon && !am_server && sig_num == SIGTERM) + exit_cleanup(0); + + /* If the signal arrived on the server side (or for the receiver + * process on the client), we want to try to do a controlled shutdown + * that lets the client side (generator process) know what happened. + * To do this, we set a flag and let the normal process handle the + * shutdown. We only attempt this if multiplexed IO is in effect and + * we didn't already set the flag. */ + if (!got_kill_signal && (am_server || am_receiver)) { + got_kill_signal = sig_num; + called_from_signal_handler = 0; + return; + } + exit_cleanup(RERR_SIGNAL); } @@ -553,7 +778,7 @@ RETSIGTYPE sig_int(UNUSED(int val)) * attributes (e.g. permissions, ownership, etc.). If the robust_rename() * call is forced to copy the temp file and partialptr is both non-NULL and * not an absolute path, we stage the file into the partial-dir and then - * rename it into place. This returns 1 on succcess or 0 on failure. */ + * rename it into place. This returns 1 on success or 0 on failure. */ int finish_transfer(const char *fname, const char *fnametmp, const char *fnamecmp, const char *partialptr, struct file_struct *file, int ok_to_set_time, @@ -563,40 +788,44 @@ int finish_transfer(const char *fname, const char *fna const char *temp_copy_name = partialptr && *partialptr != '/' ? partialptr : NULL; if (inplace) { - if (verbose > 2) + if (DEBUG_GTE(RECV, 1)) rprintf(FINFO, "finishing %s\n", fname); fnametmp = fname; goto do_set_file_attrs; } - if (make_backups > 0 && overwriting_basis) { - if (!make_backup(fname)) - return 1; - if (fnamecmp == fname) + if (make_backups > 1 && overwriting_basis) { + int ok = make_backup(fname, False); + if (!ok) + exit_cleanup(RERR_FILEIO); + if (ok == 1 && fnamecmp == fname) fnamecmp = get_backup_name(fname); } /* Change permissions before putting the file into place. */ set_file_attrs(fnametmp, file, NULL, fnamecmp, - ok_to_set_time ? 0 : ATTRS_SKIP_MTIME); + ATTRS_DELAY_IMMUTABLE + | (ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME)); /* move tmp file over real file */ - if (verbose > 2) + 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; } if (ret == 0) { /* The file was moved into place (not copied), so it's done. */ +#ifdef SUPPORT_FILEFLAGS + if (preserve_fileflags && F_FFLAGS(file) & ALL_IMMUTABLE) + set_fileflags(fname, F_FFLAGS(file)); +#endif return 1; } /* The file was copied, so tweak the perms of the copied file. If it @@ -605,7 +834,7 @@ int finish_transfer(const char *fname, const char *fna do_set_file_attrs: set_file_attrs(fnametmp, file, NULL, fnamecmp, - ok_to_set_time ? 0 : ATTRS_SKIP_MTIME); + ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME); if (temp_copy_name) { if (do_rename(fnametmp, fname) < 0) { @@ -656,6 +885,8 @@ struct file_list *flist_for_ndx(int ndx, const char *f const char *who_am_i(void) { + if (am_dbadmin) + return "rsyncdb"; if (am_starting_up) return am_server ? "server" : "client"; return am_sender ? "sender"