diff --git a/SOURCES/rsync-3.1.3-filtering-rules.patch b/SOURCES/rsync-3.1.3-filtering-rules.patch
new file mode 100644
index 0000000..a4741cf
--- /dev/null
+++ b/SOURCES/rsync-3.1.3-filtering-rules.patch
@@ -0,0 +1,333 @@
+diff --git a/exclude.c b/exclude.c
+index 13c4253..232249f 100644
+--- a/exclude.c
++++ b/exclude.c
+@@ -79,6 +79,10 @@ static filter_rule **mergelist_parents;
+ static int mergelist_cnt = 0;
+ static int mergelist_size = 0;
+ 
++#define LOCAL_RULE   1
++#define REMOTE_RULE  2
++static uchar cur_elide_value = REMOTE_RULE;
++
+ /* Each filter_list_struct describes a singly-linked list by keeping track
+  * of both the head and tail pointers.  The list is slightly unusual in that
+  * a parent-dir's content can be appended to the end of the local list in a
+@@ -218,6 +222,7 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
+ 				slash_cnt++;
+ 		}
+ 	}
++	rule->elide = 0;
+ 	strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
+ 	pat_len += pre_len;
+ 	if (suf_len) {
+@@ -364,6 +369,8 @@ void implied_include_partial_string(const char *s_start, const char *s_end)
+ void free_implied_include_partial_string()
+ {
+ 	if (partial_string_buf) {
++		if (partial_string_len)
++			add_implied_include("", 0);
+ 		free(partial_string_buf);
+ 		partial_string_buf = NULL;
+ 	}
+@@ -374,9 +381,8 @@ void free_implied_include_partial_string()
+  * that the receiver uses to validate the file list from the sender. */
+ void add_implied_include(const char *arg, int skip_daemon_module)
+ {
+-	filter_rule *rule;
+ 	int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
+-	int slash_cnt = 1; /* We know we're adding a leading slash. */
++	int slash_cnt = 0;
+ 	const char *cp;
+ 	char *p;
+ 	if (am_server || old_style_args || list_only || read_batch || filesfrom_host != NULL)
+@@ -407,6 +413,7 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 		arg++;
+ 	arg_len = strlen(arg);
+ 	if (arg_len) {
++		char *new_pat;
+ 		if (strpbrk(arg, "*[?")) {
+ 			/* We need to add room to escape backslashes if wildcard chars are present. */
+ 			for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
+@@ -414,16 +421,9 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 			saw_wild = 1;
+ 		}
+ 		arg_len++; /* Leave room for the prefixed slash */
+-		rule = new0(filter_rule);
+-		if (!implied_filter_list.head)
+-			implied_filter_list.head = implied_filter_list.tail = rule;
+-		else {
+-			rule->next = implied_filter_list.head;
+-			implied_filter_list.head = rule;
+-		}
+-		rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
+-		p = rule->pattern = new_array(char, arg_len + 1);
++		p = new_pat = new_array(char, arg_len + 1);
+ 		*p++ = '/';
++		slash_cnt++;
+ 		for (cp = arg; *cp; ) {
+ 			switch (*cp) {
+ 			  case '\\':
+@@ -439,15 +439,70 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 				break;
+ 			  case '/':
+ 				if (p[-1] == '/') { /* This is safe because of the initial slash. */
++					if (*++cp == '\0') {
++						slash_cnt--;
++						p--;
++					}
++				} else if (cp[1] == '\0') {
+ 					cp++;
+-					break;
++				} else {
++					slash_cnt++;
++					*p++ = *cp++;
+ 				}
+-				if (relative_paths) {
+-					filter_rule const *ent;
++				break;
++			  case '.':
++				if (p[-1] == '/') {
++					if (cp[1] == '/') {
++						cp += 2;
++						if (!*cp) {
++							slash_cnt--;
++							p--;
++						}
++					} else if (cp[1] == '\0') {
++						cp++;
++						slash_cnt--;
++						p--;
++					} else
++						*p++ = *cp++;
++				} else
++					*p++ = *cp++;
++				break;
++			  case '[':
++				saw_live_open_brkt = 1;
++				*p++ = *cp++;
++				break;
++			  default:
++				*p++ = *cp++;
++				break;
++			}
++		}
++		*p = '\0';
++		arg_len = p - new_pat;
++		if (!arg_len)
++			free(new_pat);
++		else {
++			filter_rule *rule = new0(filter_rule);
++			rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
++			rule->u.slash_cnt = slash_cnt;
++			arg = rule->pattern = new_pat;
++			if (!implied_filter_list.head)
++				implied_filter_list.head = implied_filter_list.tail = rule;
++			else {
++				rule->next = implied_filter_list.head;
++				implied_filter_list.head = rule;
++			}
++			if (DEBUG_GTE(FILTER, 3))
++				rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), arg);
++			if (saw_live_open_brkt)
++				maybe_add_literal_brackets_rule(rule, arg_len);
++			if (relative_paths && slash_cnt) {
++				filter_rule const *ent;
++				slash_cnt = 1;
++				for (p = new_pat + 1; (p = strchr(p, '/')) != NULL; p++) {
+ 					int found = 0;
+ 					*p = '\0';
+ 					for (ent = implied_filter_list.head; ent; ent = ent->next) {
+-						if (ent != rule && strcmp(ent->pattern, rule->pattern) == 0) {
++						if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
+ 							found = 1;
+ 							break;
+ 						}
+@@ -456,9 +511,9 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 						filter_rule *R_rule = new0(filter_rule);
+ 						R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY;
+ 						/* Check if our sub-path has wildcards or escaped backslashes */
+-						if (saw_wild && strpbrk(rule->pattern, "*[?\\"))
++						if (saw_wild && strpbrk(new_pat, "*[?\\"))
+ 							R_rule->rflags |= FILTRULE_WILD;
+-						R_rule->pattern = strdup(rule->pattern);
++						R_rule->pattern = strdup(new_pat);
+ 						R_rule->u.slash_cnt = slash_cnt;
+ 						R_rule->next = implied_filter_list.head;
+ 						implied_filter_list.head = R_rule;
+@@ -469,32 +524,16 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 						if (saw_live_open_brkt)
+ 							maybe_add_literal_brackets_rule(R_rule, -1);
+ 					}
++					*p = '/';
++					slash_cnt++;
+ 				}
+-				slash_cnt++;
+-				*p++ = *cp++;
+-				break;
+-			  case '[':
+-				saw_live_open_brkt = 1;
+-				*p++ = *cp++;
+-				break;
+-			  default:
+-				*p++ = *cp++;
+-				break;
+ 			}
+ 		}
+-		*p = '\0';
+-		rule->u.slash_cnt = slash_cnt;
+-		arg = rule->pattern;
+-		arg_len = p - arg; /* We recompute it due to backslash weirdness. */
+-		if (DEBUG_GTE(FILTER, 3))
+-			rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
+-		if (saw_live_open_brkt)
+-			maybe_add_literal_brackets_rule(rule, arg_len);
+ 	}
+ 
+ 	if (recurse || xfer_dirs) {
+ 		/* Now create a rule with an added "/" & "**" or "*" at the end */
+-		rule = new0(filter_rule);
++		filter_rule *rule = new0(filter_rule);
+ 		rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
+ 		if (recurse)
+ 			rule->rflags |= FILTRULE_WILD2;
+@@ -502,7 +541,7 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 		if (!saw_wild && backslash_cnt) {
+ 			/* We are appending a wildcard, so now the backslashes need to be escaped. */
+ 			p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
+-			for (cp = arg; *cp; ) {
++			for (cp = arg; *cp; ) { /* Note that arg_len != 0 because backslash_cnt > 0 */
+ 				if (*cp == '\\')
+ 					*p++ = '\\';
+ 				*p++ = *cp++;
+@@ -514,13 +553,15 @@ void add_implied_include(const char *arg, int skip_daemon_module)
+ 				p += arg_len;
+ 			}
+ 		}
+-		if (p[-1] != '/')
++		if (p[-1] != '/') {
+ 			*p++ = '/';
++			slash_cnt++;
++		}
+ 		*p++ = '*';
+ 		if (recurse)
+ 			*p++ = '*';
+ 		*p = '\0';
+-		rule->u.slash_cnt = slash_cnt + 1;
++		rule->u.slash_cnt = slash_cnt;
+ 		rule->next = implied_filter_list.head;
+ 		implied_filter_list.head = rule;
+ 		if (DEBUG_GTE(FILTER, 3))
+@@ -869,7 +910,7 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
+ 	const char *strings[16]; /* more than enough */
+ 	const char *name = fname + (*fname == '/');
+ 
+-	if (!*name)
++	if (!*name || ex->elide == cur_elide_value)
+ 		return 0;
+ 
+ 	if (!(name_flags & NAME_IS_XATTR) ^ !(ex->rflags & FILTRULE_XATTR))
+@@ -985,6 +1026,15 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
+ 	return 0;
+ }
+ 
++int check_server_filter(filter_rule_list *listp, enum logcode code, const char *name, int name_flags)
++{
++	int ret;
++	cur_elide_value = LOCAL_RULE;
++	ret = check_filter(listp, code, name, name_flags);
++	cur_elide_value = REMOTE_RULE;
++	return ret;
++}
++
+ /* Return -1 if file "name" is defined to be excluded by the specified
+  * exclude list, 1 if it is included, and 0 if it was not matched. */
+ int check_filter(filter_rule_list *listp, enum logcode code,
+@@ -1550,7 +1600,7 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
+ 
+ static void send_rules(int f_out, filter_rule_list *flp)
+ {
+-	filter_rule *ent, *prev = NULL;
++	filter_rule *ent;
+ 
+ 	for (ent = flp->head; ent; ent = ent->next) {
+ 		unsigned int len, plen, dlen;
+@@ -1565,21 +1615,15 @@ static void send_rules(int f_out, filter_rule_list *flp)
+ 		 * merge files as an optimization (since they can only have
+ 		 * include/exclude rules). */
+ 		if (ent->rflags & FILTRULE_SENDER_SIDE)
+-			elide = am_sender ? 1 : -1;
++			elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
+ 		if (ent->rflags & FILTRULE_RECEIVER_SIDE)
+-			elide = elide ? 0 : am_sender ? -1 : 1;
++			elide = elide ? 0 : am_sender ? REMOTE_RULE : LOCAL_RULE;
+ 		else if (delete_excluded && !elide
+ 		 && (!(ent->rflags & FILTRULE_PERDIR_MERGE)
+ 		  || ent->rflags & FILTRULE_NO_PREFIXES))
+-			elide = am_sender ? 1 : -1;
+-		if (elide < 0) {
+-			if (prev)
+-				prev->next = ent->next;
+-			else
+-				flp->head = ent->next;
+-		} else
+-			prev = ent;
+-		if (elide > 0)
++			elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
++		ent->elide = elide;
++		if (elide == LOCAL_RULE)
+ 			continue;
+ 		if (ent->rflags & FILTRULE_CVS_IGNORE
+ 		    && !(ent->rflags & FILTRULE_MERGE_FILE)) {
+@@ -1607,7 +1651,6 @@ static void send_rules(int f_out, filter_rule_list *flp)
+ 		if (dlen)
+ 			write_byte(f_out, '/');
+ 	}
+-	flp->tail = prev;
+ }
+ 
+ /* This is only called by the client. */
+diff --git a/options.c b/options.c
+index afc33ce..4d0a1a6 100644
+--- a/options.c
++++ b/options.c
+@@ -2426,7 +2426,9 @@ char *safe_arg(const char *opt, const char *arg)
+ 	char *ret;
+ 	if (!protect_args && old_style_args < 2 && (!old_style_args || (!is_filename_arg && opt != SPLIT_ARG_WHEN_OLD))) {
+ 		const char *f;
+-		if (!old_style_args && *arg == '~' && (relative_paths || !strchr(arg, '/'))) {
++		if (!old_style_args && *arg == '~' 
++				&& ((relative_paths && !strstr(arg, "/./")) 
++				|| !strchr(arg, '/'))) {
+ 			extras++;
+ 			escape_leading_tilde = 1;
+ 		}
+diff --git a/flist.c b/flist.c
+index 630d685..8c2397b 100644
+--- a/flist.c
++++ b/flist.c
+@@ -904,10 +904,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
+ 		exit_cleanup(RERR_UNSUPPORTED);
+ 	}
+ 
+-	if (*thisname != '.' || thisname[1] != '\0') {
++	if (*thisname == '/' ? thisname[1] != '.' || thisname[2] != '\0' : *thisname != '.' || thisname[1] != '\0') {
+ 		int filt_flags = S_ISDIR(mode) ? NAME_IS_DIR : NAME_IS_FILE;
+ 		if (!trust_sender_filter /* a per-dir filter rule means we must trust the sender's filtering */
+-		 && filter_list.head && check_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
++		 && filter_list.head && check_server_filter(&filter_list, FINFO, thisname, filt_flags) < 0) {
+ 			rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname);
+ 			exit_cleanup(RERR_PROTOCOL);
+ 		}
+diff --git a/rsync.h b/rsync.h
+index 53fff2d..b357dad 100644
+--- a/rsync.h
++++ b/rsync.h
+@@ -899,6 +899,7 @@ typedef struct filter_struct {
+ 		int slash_cnt;
+ 		struct filter_list_struct *mergelist;
+ 	} u;
++	uchar elide;
+ } filter_rule;
+ 
+ typedef struct filter_list_struct {
diff --git a/SOURCES/rsync-3.1.3-missing-xattr-filter.patch b/SOURCES/rsync-3.1.3-missing-xattr-filter.patch
new file mode 100644
index 0000000..5718fa5
--- /dev/null
+++ b/SOURCES/rsync-3.1.3-missing-xattr-filter.patch
@@ -0,0 +1,13 @@
+diff --git a/exclude.c.old b/exclude.c
+index 232249f..2f6dccc 100644
+--- a/exclude.c.old
++++ b/exclude.c
+@@ -1575,6 +1575,8 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
+ 	}
+ 	if (rule->rflags & FILTRULE_EXCLUDE_SELF)
+ 		*op++ = 'e';
++	if (rule->rflags & FILTRULE_XATTR)
++		*op++ = 'x';
+ 	if (rule->rflags & FILTRULE_SENDER_SIDE
+ 	    && (!for_xfer || protocol_version >= 29))
+ 		*op++ = 's';
diff --git a/SPECS/rsync.spec b/SPECS/rsync.spec
index e6050c5..0832d0c 100644
--- a/SPECS/rsync.spec
+++ b/SPECS/rsync.spec
@@ -9,7 +9,7 @@
 Summary: A program for synchronizing files over a network
 Name: rsync
 Version: 3.1.3
-Release: 19%{?dist}
+Release: 19%{?dist}.1
 Group: Applications/Internet
 URL: http://rsync.samba.org/
 
@@ -40,6 +40,8 @@ Patch9: rsync-3.1.3-cve-2018-25032.patch
 Patch10: rsync-3.1.3-sparse-block.patch
 Patch11: rsync-3.1.3-cve-2022-29154.patch
 Patch12: rsync-3.1.3-cve-2022-37434.patch
+Patch13: rsync-3.1.3-filtering-rules.patch
+Patch14: rsync-3.1.3-missing-xattr-filter.patch
 
 %description
 Rsync uses a reliable algorithm to bring remote and host files into
@@ -90,6 +92,8 @@ patch -p1 -i patches/copy-devices.diff
 %patch10 -p1 -b .spars-block
 %patch11 -p1 -b .cve-2022-29154
 %patch12 -p1 -b .cve-2022-37434
+%patch13 -p1 -b .filtering-rules
+%patch14 -p1 -b .xattr-filter
 
 %build
 %configure
@@ -136,6 +140,9 @@ chmod -x support/*
 %systemd_postun_with_restart rsyncd.service
 
 %changelog
+* Wed Nov 02 2022 Michal Ruprich <mruprich@redhat.com> - 3.1.3-19.1
+- Resolves: #2139118 - rsync-daemon fail on 3.1.3
+
 * Thu Aug 18 2022 Michal Ruprich <mruprich@redhat.com> - 3.1.3-19
 - Resolves: #2116668 - zlib: a heap-based buffer over-read or buffer overflow in inflate in inflate.c via a large gzip header extra field