Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add independent update mode for symbolic links #198

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/build-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build & publish
on: {push: {branches: [master]}, pull_request: {branches: [master]}, workflow_dispatch}

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: '0'
- id: debianise
uses: twojstaryzdomu/debianise@HEAD
with:
create_changelog: true
install_build_depends: false
debug: true
- id: action-gh-release
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
files: ${{ steps.debianise.outputs.files }}
name: ${{ steps.debianise.outputs.release_name }}
tag_name: ${{ steps.debianise.outputs.tag_name }}
fail_on_unmatched_files: true
draft: true
prerelease: true
1 change: 1 addition & 0 deletions debian/compat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
11
16 changes: 16 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Source: rsync
Section: unknown
Priority: optional
Maintainer: unknown <pi@>
Build-Depends: debhelper (>= 11)
Standards-Version: 4.1.3
Homepage: https://github.com/twojstaryzdomu/rsync
Vcs-Git: https://github.com/twojstaryzdomu/rsync.git
Vcs-Browser: https://github.com/twojstaryzdomu/rsync


Package: rsync
Section: unknown
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: rsync description
38 changes: 38 additions & 0 deletions debian/patches/disable_reconfigure_req.diff
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
diff --git a/Makefile.in b/Makefile.in
index 3cde955..ef71d7e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -210,15 +210,6 @@ configure.sh config.h.in: configure.ac aclocal.m4
else \
echo "config.h.in has CHANGED."; \
fi
- @if test -f configure.sh.old || test -f config.h.in.old; then \
- if test "$(MAKECMDGOALS)" = reconfigure; then \
- echo 'Continuing with "make reconfigure".'; \
- else \
- echo 'You may need to run:'; \
- echo ' make reconfigure'; \
- exit 1; \
- fi \
- fi

.PHONY: reconfigure
reconfigure: configure.sh
@@ -232,17 +223,6 @@ restatus:
Makefile: Makefile.in config.status configure.sh config.h.in
@if test -f Makefile; then cp -p Makefile Makefile.old; else touch Makefile.old; fi
@./config.status
- @if diff Makefile Makefile.old >/dev/null 2>&1; then \
- echo "Makefile is unchanged."; \
- rm Makefile.old; \
- else \
- if test "$(MAKECMDGOALS)" = reconfigure; then \
- echo 'Continuing with "make reconfigure".'; \
- else \
- echo "Makefile updated -- rerun your make command."; \
- exit 1; \
- fi \
- fi

stunnel-rsyncd.conf: $(srcdir)/stunnel-rsyncd.conf.in Makefile
sed 's;\@bindir\@;$(bindir);g' <$(srcdir)/stunnel-rsyncd.conf.in >stunnel-rsyncd.conf
1 change: 1 addition & 0 deletions debian/patches/series
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
disable_reconfigure_req.diff
3 changes: 3 additions & 0 deletions debian/rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/make -f
%:
dh $@
3 changes: 3 additions & 0 deletions debian/watch
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
version=4
opts=filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/rsync-$1\.tar\.gz/ \
https://github.com/twojstaryzdomu/rsync/releases .*/v?(\d\S+)\.tar\.gz
8 changes: 7 additions & 1 deletion flist.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ extern int implied_dirs;
extern int ignore_perishable;
extern int non_perishable_cnt;
extern int prune_empty_dirs;
extern int update_links;
extern int copy_links;
extern int copy_unsafe_links;
extern int protocol_version;
Expand Down Expand Up @@ -234,10 +235,15 @@ static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf)
int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks)
{
#ifdef SUPPORT_LINKS
if (copy_links)
if (copy_links && update_links == 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they asked for --copy-links they should get --copy-links. Remember that it's only the sending side that sees this flag set, not the receiving side, so combining --copy-links with --update-links would mean that the sender doesn't send any symlinks, and the receiver could deal with any existing symlinks in the destination using the new option. (Thus, drop this change.)

Copy link
Author

@twojstaryzdomu twojstaryzdomu Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two modes appear mutually exclusive from a user standpoint. Either every symlink is transformed into referent (--copy-links) or copied across if newer (--update-links).

so combining --copy-links with --update-links would mean that the sender doesn't send any symlink

From testing the PR'd code briefly now, when both modes are enabled, --update-links trumps --copy-links, by virtue of a subsequent condition at if (update_links && S_ISLNK(stp->st_mode)).

Added #274 to discuss how to handle symlink mode precedence.

return x_stat(path, stp, NULL);
if (x_lstat(path, stp, NULL) < 0)
return -1;
if (update_links && S_ISLNK(stp->st_mode)) {
STRUCT_STAT st;
if (x_stat(path, &st, NULL) == 0 && !S_ISDIR(st.st_mode))
return 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hunk baffles me. It does a stat of the referent file, and if it's a non-dir it re-stats the symlink's stat that is already present in stp and returns. But if it is not a dir, the following code also returns the same stat data that is already in stp. So, it seems like this can be dropped as well.

Copy link
Author

@twojstaryzdomu twojstaryzdomu Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precisely, if the symlink points at a non-directory referent, return the symlink's stat or fall through. It's only apparent looking at the surrounding code why the x_lstat call is redundant. Committed as aed6235, works on assumption lstat or lstat64 is guaranteed to return either 0 or -1 on all platforms.

if (follow_dirlinks && S_ISLNK(stp->st_mode)) {
STRUCT_STAT st;
if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode))
Expand Down
78 changes: 78 additions & 0 deletions generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ extern int ignore_errors;
extern int remove_source_files;
extern int delay_updates;
extern int update_only;
extern int update_links;
extern int copy_links;
extern int allow_link_update_dir;
extern int human_readable;
extern int ignore_existing;
extern int ignore_non_existing;
Expand Down Expand Up @@ -1193,6 +1196,19 @@ static BOOL is_below(struct file_struct *file, struct file_struct *subtree)
&& (!implied_dirs_are_missing || f_name_has_prefix(file, subtree));
}

static BOOL dir_empty(char *dirname) {
int n = 3;
DIR *dir = opendir(dirname);
if (dir == NULL)
return -1;
while (readdir(dir)) {
if (!--n)
break;
}
closedir(dir);
return n;
}

/* Acts on the indicated item in cur_flist whose name is fname. If a dir,
* make sure it exists, and has the right permissions/timestamp info. For
* all other non-regular files (symlinks, etc.) we create them here. For
Expand Down Expand Up @@ -1540,6 +1556,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& hard_link_check(file, ndx, fname, statret, &sx, itemizing, code))
goto cleanup;
#endif
if (DEBUG_GTE(GENR, 1))
rprintf(FINFO, "%s src mtime=%ld dest mtime=%ld modify_window=%d statret=%d copy_links=%d update_links=%d allow_link_update_dir=%d\n", fname, file->modtime, (long)sx.st.st_mtime, modify_window, statret, copy_links, update_links, allow_link_update_dir);

if (preserve_links && ftype == FT_SYMLINK) {
#ifdef SUPPORT_LINKS
Expand Down Expand Up @@ -1588,6 +1606,66 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
fnamecmp = fnamecmpbuf;
}
}
if (statret == 0) {
if (update_links > 0) {
if (S_ISDIR(sx.st.st_mode) && allow_link_update_dir == 0) {
if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "symlink \"%s\" is a directory on destination and allow-link-update-dir isn't enabled, skipping\n", fname);
goto cleanup;
}
else {
int mtime_offset = sx.st.st_mtime - file->modtime;
char *st_mode = S_ISDIR(sx.st.st_mode)
? "directory"
: S_ISLNK(sx.st.st_mode)
? "symlink"
: S_ISCHR(sx.st.st_mode)
? "character device"
: S_ISBLK(sx.st.st_mode)
? "block device"
: S_ISFIFO(sx.st.st_mode)
? "named pipe"
: S_ISSOCK(sx.st.st_mode)
? "socket"
: "file";
if (mtime_offset > modify_window) {
if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "%s \"%s\" is newer by %d sec, skipping\n", st_mode, fname, mtime_offset - modify_window);
goto cleanup;
}
else if (mtime_offset < modify_window) {
if (S_ISDIR(sx.st.st_mode)) {
if (!dir_empty(fname)) {
rprintf(FINFO, "directory %s not empty, skipping\n", fname);
goto cleanup;
}
}
if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "%s \"%s\" is older by %d sec, updating\n", st_mode, fname, - mtime_offset - modify_window);
}
else if (S_ISLNK(sx.st.st_mode)) {
char lnk[MAXPATHLEN];
int llen = do_readlink(fname, lnk, MAXPATHLEN - 1);
lnk[llen] = '\0';
if (strcmp(lnk, F_SYMLINK(file)) == 0) {
if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "symlink \"%s\" points to the same referent %s, skipping\n", fname, lnk);
goto cleanup;
}
else if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "symlink \"%s\" points to a different referent on source (%s) than destination (%s), updating\n", fname, F_SYMLINK(file), lnk);
}
else {
if (INFO_GTE(SKIP, 1))
rprintf(FINFO, "symlink \"%s\" more recent than %s on destination, updating\n", fname, st_mode);
}
}
}
else {
if (DEBUG_GTE(GENR, 1))
rprintf(FINFO, "update-links not enabled for \"%s\"\n, skipping", fname);
}
}
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) {
Expand Down
16 changes: 15 additions & 1 deletion options.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ int whole_file = -1;
int append_mode = 0;
int keep_dirlinks = 0;
int copy_dirlinks = 0;
int update_links = 0;
int allow_link_update_dir = 0;
int copy_links = 0;
int copy_devices = 0;
int write_devices = 0;
Expand Down Expand Up @@ -693,6 +695,8 @@ static struct poptOption long_options[] = {
{"no-one-file-system",0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
{"no-x", 0, POPT_ARG_VAL, &one_file_system, 0, 0, 0 },
{"update", 'u', POPT_ARG_NONE, &update_only, 0, 0, 0 },
{"update-links", 0, POPT_ARG_NONE, &update_links, 0, 0, 0 },
{"allow-link-update-dir",0,POPT_ARG_NONE, &allow_link_update_dir, 0, 0, 0 },
{"existing", 0, POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 },
{"ignore-non-existing",0,POPT_ARG_NONE, &ignore_non_existing, 0, 0, 0 },
{"ignore-existing", 0, POPT_ARG_NONE, &ignore_existing, 0, 0, 0 },
Expand Down Expand Up @@ -1380,6 +1384,8 @@ int parse_arguments(int *argc_p, const char ***argv_p)
* only special cases are returned and listed here. */

switch (opt) {
case 'L':
break;
case 'V':
version_opt_cnt++;
break;
Expand Down Expand Up @@ -2321,6 +2327,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
parse_filter_str(&filter_list, backup_dir_buf, rule_template(0), 0);
}

if (update_links)
preserve_links = 1;

if (make_backups && !backup_dir)
omit_dir_times = -1; /* Implied, so avoid -O to sender. */

Expand Down Expand Up @@ -2615,7 +2624,7 @@ void server_options(char **args, int *argc_p)
argstr[x++] = 'u';
if (!do_xfers) /* Note: NOT "dry_run"! */
argstr[x++] = 'n';
if (preserve_links)
if (preserve_links || update_links)
argstr[x++] = 'l';
if ((xfer_dirs >= 2 && xfer_dirs < 4)
|| (xfer_dirs && !recurse && (list_only || (delete_mode && am_sender))))
Expand All @@ -2634,6 +2643,11 @@ void server_options(char **args, int *argc_p)
if (fuzzy_basis > 1)
argstr[x++] = 'y';
}
if (update_links) {
args[ac++] = "--update-links";
if (allow_link_update_dir)
args[ac++] = "--allow-link-update-dir";
}
} else {
if (copy_links)
argstr[x++] = 'L';
Expand Down
Loading