diff --git a/net/rsync/Makefile b/net/rsync/Makefile index 859aea23c..f52559f2d 100644 --- a/net/rsync/Makefile +++ b/net/rsync/Makefile @@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=rsync -PKG_VERSION:=3.2.4 +PKG_VERSION:=3.2.5 PKG_RELEASE:=$(AUTORELEASE) PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://download.samba.org/pub/rsync/src -PKG_HASH:=6f761838d08052b0b6579cf7f6737d93e47f01f4da04c5d24d3447b7f2a5fad1 +PKG_SOURCE_URL:=https://download.samba.org/pub/$(PKG_NAME)/src +PKG_HASH:=2ac4d21635cdf791867bc377c35ca6dda7f50d919a58be45057fd51600c69aba PKG_MAINTAINER:=Maxim Storchak PKG_LICENSE:=GPL-3.0-or-later diff --git a/net/rsync/patches/010-fix-CVE-2022-29154.patch b/net/rsync/patches/010-fix-CVE-2022-29154.patch deleted file mode 100644 index efecc730d..000000000 --- a/net/rsync/patches/010-fix-CVE-2022-29154.patch +++ /dev/null @@ -1,385 +0,0 @@ -From b7231c7d02cfb65d291af74ff66e7d8c507ee871 Mon Sep 17 00:00:00 2001 -From: Wayne Davison -Date: Sun, 31 Jul 2022 16:55:34 -0700 -Subject: [PATCH] Some extra file-list safety checks. - ---- - exclude.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++- - flist.c | 17 ++++++- - io.c | 4 ++ - main.c | 7 ++- - receiver.c | 11 +++-- - rsync.1.md | 44 ++++++++++++++++-- - 6 files changed, 202 insertions(+), 11 deletions(-) - ---- a/exclude.c -+++ b/exclude.c -@@ -27,16 +27,22 @@ extern int am_server; - extern int am_sender; - extern int eol_nulls; - extern int io_error; -+extern int xfer_dirs; -+extern int recurse; - extern int local_server; - extern int prune_empty_dirs; - extern int ignore_perishable; -+extern int old_style_args; -+extern int relative_paths; - extern int delete_mode; - extern int delete_excluded; - extern int cvs_exclude; - extern int sanitize_paths; - extern int protocol_version; -+extern int list_only; - extern int module_id; - -+extern char *filesfrom_host; - extern char curr_dir[MAXPATHLEN]; - extern unsigned int curr_dir_len; - extern unsigned int module_dirlen; -@@ -44,8 +50,10 @@ extern unsigned int module_dirlen; - filter_rule_list filter_list = { .debug_type = "" }; - filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" }; - filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" }; -+filter_rule_list implied_filter_list = { .debug_type = " [implied]" }; - - int saw_xattr_filter = 0; -+int trust_sender_filter = 0; - - /* Need room enough for ":MODS " prefix plus some room to grow. */ - #define MAX_RULE_PREFIX (16) -@@ -292,6 +300,125 @@ static void add_rule(filter_rule_list *l - } - } - -+/* Each arg the client sends to the remote sender turns into an implied include -+ * that the receiver uses to validate the file list from the sender. */ -+void add_implied_include(const char *arg) -+{ -+ filter_rule *rule; -+ int arg_len, saw_wild = 0, backslash_cnt = 0; -+ int slash_cnt = 1; /* We know we're adding a leading slash. */ -+ const char *cp; -+ char *p; -+ if (old_style_args || list_only || filesfrom_host != NULL) -+ return; -+ if (relative_paths) { -+ cp = strstr(arg, "/./"); -+ if (cp) -+ arg = cp+3; -+ } else { -+ if ((cp = strrchr(arg, '/')) != NULL) -+ arg = cp + 1; -+ } -+ arg_len = strlen(arg); -+ if (arg_len) { -+ if (strpbrk(arg, "*[?")) { -+ /* We need to add room to escape backslashes if wildcard chars are present. */ -+ cp = arg; -+ while ((cp = strchr(cp, '\\')) != NULL) { -+ arg_len++; -+ cp++; -+ } -+ 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++ = '/'; -+ cp = arg; -+ while (*cp) { -+ switch (*cp) { -+ case '\\': -+ backslash_cnt++; -+ if (saw_wild) -+ *p++ = '\\'; -+ *p++ = *cp++; -+ break; -+ case '/': -+ if (p[-1] == '/') /* This is safe because of the initial slash. */ -+ break; -+ if (relative_paths) { -+ filter_rule const *ent; -+ int found = 0; -+ *p = '\0'; -+ for (ent = implied_filter_list.head; ent; ent = ent->next) { -+ if (ent != rule && strcmp(ent->pattern, rule->pattern) == 0) -+ found = 1; -+ } -+ if (!found) { -+ filter_rule *R_rule = new0(filter_rule); -+ R_rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0); -+ R_rule->pattern = strdup(rule->pattern); -+ R_rule->u.slash_cnt = slash_cnt; -+ R_rule->next = implied_filter_list.head; -+ implied_filter_list.head = R_rule; -+ } -+ } -+ slash_cnt++; -+ *p++ = *cp++; -+ break; -+ default: -+ *p++ = *cp++; -+ break; -+ } -+ } -+ *p = '\0'; -+ rule->u.slash_cnt = slash_cnt; -+ arg = (const char *)rule->pattern; -+ } -+ -+ if (recurse || xfer_dirs) { -+ /* Now create a rule with an added "/" & "**" or "*" at the end */ -+ rule = new0(filter_rule); -+ if (recurse) -+ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD | FILTRULE_WILD2; -+ else -+ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD; -+ /* A +4 in the len leaves enough room for / * * \0 or / * \0 \0 */ -+ 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); -+ cp = arg; -+ while (*cp) { -+ if (*cp == '\\') -+ *p++ = '\\'; -+ *p++ = *cp++; -+ } -+ } else { -+ p = rule->pattern = new_array(char, arg_len + 3 + 1); -+ if (arg_len) { -+ memcpy(p, arg, arg_len); -+ p += arg_len; -+ } -+ } -+ if (p[-1] != '/') -+ *p++ = '/'; -+ *p++ = '*'; -+ if (recurse) -+ *p++ = '*'; -+ *p = '\0'; -+ rule->u.slash_cnt = slash_cnt + 1; -+ rule->next = implied_filter_list.head; -+ implied_filter_list.head = rule; -+ } -+} -+ - /* This frees any non-inherited items, leaving just inherited items on the list. */ - static void pop_filter_list(filter_rule_list *listp) - { -@@ -718,7 +845,7 @@ static void report_filter_result(enum lo - : name_flags & NAME_IS_DIR ? "directory" - : "file"; - rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n", -- w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)], -+ w, actions[*w=='g'][!(ent->rflags & FILTRULE_INCLUDE)], - t, name, ent->pattern, - ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type); - } -@@ -890,6 +1017,7 @@ static filter_rule *parse_rule_tok(const - } - switch (ch) { - case ':': -+ trust_sender_filter = 1; - rule->rflags |= FILTRULE_PERDIR_MERGE - | FILTRULE_FINISH_SETUP; - /* FALL THROUGH */ ---- a/flist.c -+++ b/flist.c -@@ -73,6 +73,7 @@ extern int need_unsorted_flist; - extern int sender_symlink_iconv; - extern int output_needs_newline; - extern int sender_keeps_checksum; -+extern int trust_sender_filter; - extern int unsort_ndx; - extern uid_t our_uid; - extern struct stats stats; -@@ -83,8 +84,7 @@ extern char curr_dir[MAXPATHLEN]; - - extern struct chmod_mode_struct *chmod_modes; - --extern filter_rule_list filter_list; --extern filter_rule_list daemon_filter_list; -+extern filter_rule_list filter_list, implied_filter_list, daemon_filter_list; - - #ifdef ICONV_OPTION - extern int filesfrom_convert; -@@ -986,6 +986,19 @@ static struct file_struct *recv_file_ent - exit_cleanup(RERR_UNSUPPORTED); - } - -+ if (*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) { -+ rprintf(FERROR, "ERROR: rejecting excluded file-list name: %s\n", thisname); -+ exit_cleanup(RERR_PROTOCOL); -+ } -+ if (implied_filter_list.head && check_filter(&implied_filter_list, FINFO, thisname, filt_flags) <= 0) { -+ rprintf(FERROR, "ERROR: rejecting unrequested file-list name: %s\n", thisname); -+ exit_cleanup(RERR_PROTOCOL); -+ } -+ } -+ - if (inc_recurse && S_ISDIR(mode)) { - if (one_file_system) { - /* Room to save the dir's device for -x */ ---- a/io.c -+++ b/io.c -@@ -419,6 +419,7 @@ static void forward_filesfrom_data(void) - while (s != eob) { - if (*s++ == '\0') { - ff_xb.len = s - sob - 1; -+ add_implied_include(sob); - if (iconvbufs(ic_send, &ff_xb, &iobuf.out, flags) < 0) - exit_cleanup(RERR_PROTOCOL); /* impossible? */ - write_buf(iobuf.out_fd, s-1, 1); /* Send the '\0'. */ -@@ -450,9 +451,12 @@ static void forward_filesfrom_data(void) - char *f = ff_xb.buf + ff_xb.pos; - char *t = ff_xb.buf; - char *eob = f + len; -+ char *cur = t; - /* Eliminate any multi-'\0' runs. */ - while (f != eob) { - if (!(*t++ = *f++)) { -+ add_implied_include(cur); -+ cur = t; - while (f != eob && *f == '\0') - f++; - } ---- a/main.c -+++ b/main.c -@@ -89,6 +89,7 @@ extern int backup_dir_len; - extern int basis_dir_cnt; - extern int default_af_hint; - extern int stdout_format_has_i; -+extern int trust_sender_filter; - extern struct stats stats; - extern char *stdout_format; - extern char *logfile_format; -@@ -104,7 +105,7 @@ extern char curr_dir[MAXPATHLEN]; - extern char backup_dir_buf[MAXPATHLEN]; - extern char *basis_dir[MAX_BASIS_DIRS+1]; - extern struct file_list *first_flist; --extern filter_rule_list daemon_filter_list; -+extern filter_rule_list daemon_filter_list, implied_filter_list; - - uid_t our_uid; - gid_t our_gid; -@@ -635,6 +636,7 @@ static pid_t do_cmd(char *cmd, char *mac - #ifdef ICONV_CONST - setup_iconv(); - #endif -+ trust_sender_filter = 1; - } else if (local_server) { - /* If the user didn't request --[no-]whole-file, force - * it on, but only if we're not batch processing. */ -@@ -1500,6 +1502,8 @@ static int start_client(int argc, char * - char *dummy_host; - int dummy_port = rsync_port; - int i; -+ if (filesfrom_fd < 0) -+ add_implied_include(remote_argv[0]); - /* For remote source, any extra source args must have either - * the same hostname or an empty hostname. */ - for (i = 1; i < remote_argc; i++) { -@@ -1523,6 +1527,7 @@ static int start_client(int argc, char * - if (!rsync_port && !*arg) /* Turn an empty arg into a dot dir. */ - arg = "."; - remote_argv[i] = arg; -+ add_implied_include(arg); - } - } - ---- a/receiver.c -+++ b/receiver.c -@@ -593,10 +593,13 @@ int recv_files(int f_in, int f_out, char - if (DEBUG_GTE(RECV, 1)) - rprintf(FINFO, "recv_files(%s)\n", fname); - -- if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0') -- && check_filter(&daemon_filter_list, FLOG, fname, 0) < 0) { -- rprintf(FERROR, "attempt to hack rsync failed.\n"); -- exit_cleanup(RERR_PROTOCOL); -+ if (daemon_filter_list.head && (*fname != '.' || fname[1] != '\0')) { -+ int filt_flags = S_ISDIR(file->mode) ? NAME_IS_DIR : NAME_IS_FILE; -+ if (check_filter(&daemon_filter_list, FLOG, fname, filt_flags) < 0) { -+ rprintf(FERROR, "ERROR: rejecting file transfer request for daemon excluded file: %s\n", -+ fname); -+ exit_cleanup(RERR_PROTOCOL); -+ } - } - - #ifdef SUPPORT_XATTRS ---- a/rsync.1.md -+++ b/rsync.1.md -@@ -154,6 +154,33 @@ rsync daemon by leaving off the module n - - See the following section for more details. - -+## MULTI-HOST SECURITY -+ -+Rsync takes steps to ensure that the file requests that are shared in a -+transfer are protected against various security issues. Most of the potential -+problems arise on the receiving side where rsync takes steps to ensure that the -+list of files being transferred remains within the bounds of what was -+requested. -+ -+Toward this end, rsync 3.1.2 and later have aborted when a file list contains -+an absolute or relative path that tries to escape out of the top of the -+transfer. Also, beginning with version 3.2.5, rsync does two more safety -+checks of the file list to (1) ensure that no extra source arguments were added -+into the transfer other than those that the client requested and (2) ensure -+that the file list obeys the exclude rules that we sent to the sender. -+ -+For those that don't yet have a 3.2.5 client rsync, it is safest to do a copy -+into a dedicated destination directory for the remote files rather than -+requesting the remote content get mixed in with other local content. For -+example, doing an rsync copy into your home directory is potentially unsafe on -+an older rsync if the remote rsync is being controlled by a bad actor: -+ -+> rsync -aiv host1:dir1 ~ -+ -+A safer command would be: -+ -+> rsync -aiv host1:dir1 ~/host1-files -+ - ## ADVANCED USAGE - - The syntax for requesting multiple files from a remote host is done by -@@ -2323,6 +2350,12 @@ your home directory (remove the '=' for - behavior. The environment is always overridden by manually specified - positive or negative options (the negative is `--no-old-args`). - -+ Note that this option also disables the extra safety check added in 3.2.5 -+ that ensures that a remote sender isn't including extra top-level items in -+ the file-list that you didn't request. This side-effect is necessary -+ because we can't know for sure what names to expect when the remote shell -+ is interpreting the args. -+ - This option conflicts with the [`--protect-args`](#opt) option. - - 0. `--protect-args`, `-s` -@@ -3754,8 +3787,13 @@ available rule prefixes: - - 0. `exclude, '-'` specifies an exclude pattern. - 0. `include, '+'` specifies an include pattern. --0. `merge, '.'` specifies a merge-file to read for more rules. --0. `dir-merge, ':'` specifies a per-directory merge-file. -+0. `merge, '.'` specifies a merge-file on the client side to read for more -+rules. -+0. `dir-merge, ':'` specifies a per-directory merge-file. Using this kind of -+ filter rule requires that you trust the sending side's filter checking, and -+ thus it disables the receiver's verification of the file-list names against -+ the filter rules (since only the sender can know for sure if it obeyed all -+ the filter rules when some are per-dir merged from the sender's files). - 0. `hide, 'H'` specifies a pattern for hiding files from the transfer. - 0. `show, 'S'` files that match the pattern are not hidden. - 0. `protect, 'P'` specifies a pattern for protecting files from deletion. diff --git a/net/rsync/patches/011-more-improvements-to-file-list-checking.patch b/net/rsync/patches/011-more-improvements-to-file-list-checking.patch deleted file mode 100644 index 9239f6fda..000000000 --- a/net/rsync/patches/011-more-improvements-to-file-list-checking.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 7e5424b806e8eea053016268ad186276e9083b77 Mon Sep 17 00:00:00 2001 -From: Wayne Davison -Date: Mon, 1 Aug 2022 07:00:51 -0700 -Subject: [PATCH] More improvements to file-list checking - -- Avoid implied rules on generator and (with extra certainty) on server -- Add -R implied-directory path elements as directory includes -- Log about extra file-list checking using a new --debug=FILTER3 level ---- - exclude.c | 21 ++++++++++++++++----- - main.c | 1 + - options.c | 2 +- - 3 files changed, 18 insertions(+), 6 deletions(-) - ---- a/exclude.c -+++ b/exclude.c -@@ -25,6 +25,7 @@ - - extern int am_server; - extern int am_sender; -+extern int am_generator; - extern int eol_nulls; - extern int io_error; - extern int xfer_dirs; -@@ -309,7 +310,7 @@ void add_implied_include(const char *arg - int slash_cnt = 1; /* We know we're adding a leading slash. */ - const char *cp; - char *p; -- if (old_style_args || list_only || filesfrom_host != NULL) -+ if (am_server || old_style_args || list_only || filesfrom_host != NULL) - return; - if (relative_paths) { - cp = strstr(arg, "/./"); -@@ -363,11 +364,16 @@ void add_implied_include(const char *arg - } - if (!found) { - filter_rule *R_rule = new0(filter_rule); -- R_rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0); -+ R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY -+ | (saw_wild ? FILTRULE_WILD : 0); - R_rule->pattern = strdup(rule->pattern); - R_rule->u.slash_cnt = slash_cnt; - R_rule->next = implied_filter_list.head; - implied_filter_list.head = R_rule; -+ if (DEBUG_GTE(FILTER, 3)) { -+ rprintf(FINFO, "[%s] add_implied_include(%s/)\n", -+ who_am_i(), rule->pattern); -+ } - } - } - slash_cnt++; -@@ -381,6 +387,8 @@ void add_implied_include(const char *arg - *p = '\0'; - rule->u.slash_cnt = slash_cnt; - arg = (const char *)rule->pattern; -+ if (DEBUG_GTE(FILTER, 3)) -+ rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern); - } - - if (recurse || xfer_dirs) { -@@ -416,6 +424,8 @@ void add_implied_include(const char *arg - rule->u.slash_cnt = slash_cnt + 1; - 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(), rule->pattern); - } - } - -@@ -833,11 +843,12 @@ static void report_filter_result(enum lo - filter_rule const *ent, - int name_flags, const char *type) - { -+ int log_level = am_sender || am_generator ? 1 : 3; -+ - /* If a trailing slash is present to match only directories, - * then it is stripped out by add_rule(). So as a special -- * case we add it back in here. */ -- -- if (DEBUG_GTE(FILTER, 1)) { -+ * case we add it back in the log output. */ -+ if (DEBUG_GTE(FILTER, log_level)) { - static char *actions[2][2] - = { {"show", "hid"}, {"risk", "protect"} }; - const char *w = who_am_i(); ---- a/main.c -+++ b/main.c -@@ -1078,6 +1078,7 @@ static int do_recv(int f_in, int f_out, - } - - am_generator = 1; -+ implied_filter_list.head = implied_filter_list.tail = NULL; - flist_receiving_enabled = True; - - io_end_multiplex_in(MPLX_SWITCHING); ---- a/options.c -+++ b/options.c -@@ -293,7 +293,7 @@ static struct output_struct debug_words[ - DEBUG_WORD(DELTASUM, W_SND|W_REC, "Debug delta-transfer checksumming (levels 1-4)"), - DEBUG_WORD(DUP, W_REC, "Debug weeding of duplicate names"), - DEBUG_WORD(EXIT, W_CLI|W_SRV, "Debug exit events (levels 1-3)"), -- DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-2)"), -+ DEBUG_WORD(FILTER, W_SND|W_REC, "Debug filter actions (levels 1-3)"), - DEBUG_WORD(FLIST, W_SND|W_REC, "Debug file-list operations (levels 1-4)"), - DEBUG_WORD(FUZZY, W_REC, "Debug fuzzy scoring (levels 1-2)"), - DEBUG_WORD(GENR, W_REC, "Debug generator functions"), diff --git a/net/rsync/patches/012-a-few-more-minor-changes.patch b/net/rsync/patches/012-a-few-more-minor-changes.patch deleted file mode 100644 index 7946e85f6..000000000 --- a/net/rsync/patches/012-a-few-more-minor-changes.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 3d7015afa223494e3318495c2f5de9cb49229da9 Mon Sep 17 00:00:00 2001 -From: Wayne Davison -Date: Mon, 1 Aug 2022 07:29:44 -0700 -Subject: [PATCH] A few more minor changes. - ---- - exclude.c | 21 +++++++++++---------- - 1 file changed, 11 insertions(+), 10 deletions(-) - ---- a/exclude.c -+++ b/exclude.c -@@ -313,13 +313,10 @@ void add_implied_include(const char *arg - if (am_server || old_style_args || list_only || filesfrom_host != NULL) - return; - if (relative_paths) { -- cp = strstr(arg, "/./"); -- if (cp) -- arg = cp+3; -- } else { -- if ((cp = strrchr(arg, '/')) != NULL) -- arg = cp + 1; -- } -+ if ((cp = strstr(arg, "/./")) != NULL) -+ arg = cp + 3; -+ } else if ((cp = strrchr(arg, '/')) != NULL) -+ arg = cp + 1; - arg_len = strlen(arg); - if (arg_len) { - if (strpbrk(arg, "*[?")) { -@@ -359,13 +356,17 @@ void add_implied_include(const char *arg - 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, rule->pattern) == 0) { - found = 1; -+ break; -+ } - } - if (!found) { - filter_rule *R_rule = new0(filter_rule); -- R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY -- | (saw_wild ? FILTRULE_WILD : 0); -+ R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY; -+ /* Check if our sub-path has wildcards or escaped backslashes */ -+ if (saw_wild && strpbrk(rule->pattern, "*[?\\")) -+ R_rule->rflags |= FILTRULE_WILD; - R_rule->pattern = strdup(rule->pattern); - R_rule->u.slash_cnt = slash_cnt; - R_rule->next = implied_filter_list.head; diff --git a/net/rsync/patches/013-handle-a-trailing-slash.patch b/net/rsync/patches/013-handle-a-trailing-slash.patch deleted file mode 100644 index 8a5ae9a50..000000000 --- a/net/rsync/patches/013-handle-a-trailing-slash.patch +++ /dev/null @@ -1,24 +0,0 @@ -From d659610afc8a3ee53fe68a8a4bbd7fc768fcd6e9 Mon Sep 17 00:00:00 2001 -From: Wayne Davison -Date: Mon, 8 Aug 2022 17:36:36 -0700 -Subject: [PATCH] Handle a trailing "/." at the end of a source arg. - ---- - exclude.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - ---- a/exclude.c -+++ b/exclude.c -@@ -315,8 +315,11 @@ void add_implied_include(const char *arg - if (relative_paths) { - if ((cp = strstr(arg, "/./")) != NULL) - arg = cp + 3; -- } else if ((cp = strrchr(arg, '/')) != NULL) -+ } else if ((cp = strrchr(arg, '/')) != NULL) { - arg = cp + 1; -+ if (*arg == '.' && arg[1] == '\0') -+ arg++; -+ } - arg_len = strlen(arg); - if (arg_len) { - if (strpbrk(arg, "*[?")) {