From ba43e88527980718f99b680c3a3e3e2a3a97b6f3 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Fri, 13 Jun 2014 15:58:26 -0700 Subject: Fix hard-link bugs when receiver isn't capable. If the receiving side cannot hard-link symlinks and/or special files (including devices) then we now properly handle incoming hard-linked items (creating separate identical items). --- NEWS | 4 ++++ flist.c | 9 ++++++++- generator.c | 41 ++++++++++++++++++++--------------------- hlink.c | 2 +- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/NEWS b/NEWS index 22766ce6..5f2e7387 100644 --- a/NEWS +++ b/NEWS @@ -59,6 +59,10 @@ Changes since 3.1.0: transfer failure. This removal is flagged in the compatibility code, so if a better fix can be discovered, we have a way to flip it on again. + - Fixed a bug when the receiver is not configured to be able to hard link + symlimks/devices/special-file items but the sender sent some of these + items flagged as hard-linked. + - We now generate a better error if the buffer overflows in do_mknod(). - Fixed a problem reading more than 16 ACLs on some OSes. diff --git a/flist.c b/flist.c index fed0391f..c24672e8 100644 --- a/flist.c +++ b/flist.c @@ -948,7 +948,14 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x memcpy(bp, basename, basename_len); #ifdef SUPPORT_HARD_LINKS - if (xflags & XMIT_HLINKED) + if (xflags & XMIT_HLINKED +#ifndef CAN_HARDLINK_SYMLINK + && !S_ISLNK(mode) +#endif +#ifndef CAN_HARDLINK_SPECIAL + && !IS_SPECIAL(mode) && !IS_DEVICE(mode) +#endif + ) file->flags |= FLAG_HLINKED; #endif file->modtime = (time_t)modtime; diff --git a/generator.c b/generator.c index 63399d3d..91009a53 100644 --- a/generator.c +++ b/generator.c @@ -1511,7 +1511,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT); if (itemizing) itemize(fname, file, ndx, 0, &sx, 0, 0, NULL); -#if defined SUPPORT_HARD_LINKS && defined CAN_HARDLINK_SYMLINK +#ifdef SUPPORT_HARD_LINKS if (preserve_hard_links && F_IS_HLINKED(file)) finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1); #endif @@ -1537,7 +1537,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, fnamecmp = fnamecmpbuf; } } - if (atomic_create(file, fname, sl, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) { + if (atomic_create(file, fname, sl, NULL, MAKEDEV(0, 0), &sx, statret == 0 ? DEL_FOR_SYMLINK : 0)) { set_file_attrs(fname, file, NULL, NULL, 0); if (itemizing) { if (statret == 0 && !S_ISLNK(sx.st.st_mode)) @@ -1618,7 +1618,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, fname, (int)file->mode, (long)major(rdev), (long)minor(rdev)); } - if (atomic_create(file, fname, NULL, rdev, &sx, del_for_flag)) { + if (atomic_create(file, fname, NULL, NULL, rdev, &sx, del_for_flag)) { set_file_attrs(fname, file, NULL, NULL, 0); if (itemizing) { itemize(fnamecmp, file, ndx, statret, &sx, @@ -1923,11 +1923,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, } /* If we are replacing an existing hard link, symlink, device, or special file, - * create a temp-name item and rename it into place. Only a symlink or hard - * link puts a non-NULL value into the lnk arg. Only a device puts a non-0 - * value into the rdev arg. Specify 0 for the del_for_flag if there is not a - * file to replace. This returns 1 on success and 0 on failure. */ -int atomic_create(struct file_struct *file, char *fname, const char *lnk, + * create a temp-name item and rename it into place. A symlimk specifies slnk, + * a hard link specifies hlnk, otherwise we create a device based on rdev. + * Specify 0 for the del_for_flag if there is not a file to replace. This + * returns 1 on success and 0 on failure. */ +int atomic_create(struct file_struct *file, char *fname, const char *slnk, const char *hlnk, dev_t rdev, stat_x *sxp, int del_for_flag) { char tmpname[MAXPATHLEN]; @@ -1952,23 +1952,22 @@ int atomic_create(struct file_struct *file, char *fname, const char *lnk, create_name = skip_atomic ? fname : tmpname; - if (lnk) { + if (slnk) { #ifdef SUPPORT_LINKS - if (S_ISLNK(file->mode) -#ifdef SUPPORT_HARD_LINKS /* The first symlink in a hard-linked cluster is always created. */ - && (!F_IS_HLINKED(file) || file->flags & FLAG_HLINK_FIRST) -#endif - ) { - if (do_symlink(lnk, create_name) < 0) { - rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed", - full_fname(create_name), lnk); - return 0; - } - } else + if (do_symlink(slnk, create_name) < 0) { + rsyserr(FERROR_XFER, errno, "symlink %s -> \"%s\" failed", + full_fname(create_name), slnk); + return 0; + } +#else + return 0; #endif + } else if (hlnk) { #ifdef SUPPORT_HARD_LINKS - if (!hard_link_one(file, create_name, lnk, 0)) + if (!hard_link_one(file, create_name, hlnk, 0)) return 0; +#else + return 0; #endif } else { if (do_mknod(create_name, file->mode, rdev) < 0) { diff --git a/hlink.c b/hlink.c index df8e9182..3b578985 100644 --- a/hlink.c +++ b/hlink.c @@ -231,7 +231,7 @@ static int maybe_hard_link(struct file_struct *file, int ndx, } } - if (atomic_create(file, fname, oldname, MAKEDEV(0, 0), sxp, statret == 0 ? DEL_FOR_FILE : 0)) { + if (atomic_create(file, fname, NULL, oldname, MAKEDEV(0, 0), sxp, statret == 0 ? DEL_FOR_FILE : 0)) { if (itemizing) { itemize(fname, file, ndx, statret, sxp, ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0, -- cgit v1.2.3