Hi guys,

I recently stumbled across what I think is an rsync bug.  The problem
was I was trying to rsync from one CVS checkout, to another, and was
using rsync -Cavz to do the job.  In this CVS module, there happens to
be two sub-directories with the names of "core" and "tags"
respectively, which were ignored, since these names matched the
default CVS ignore list.

When rsync builds the file lists with the -C option, it should include
any CVS controlled files (by checking CVS/Entries) before applying the
default CVS excludes, and the per-directory .cvsignore files.

Its actually surprising how many projects out there have
sub-directories with the name "core".  Linux is one of them.

Anyway, I have included a patch against rsync 2.4.6 which I believe
fixes this bug.  If this patch is ok, could it be included in the next
release?

Thanks for writing and maintaining such a useful tool!

--
Cheers,
David

diff -r -u --exclude=.o --exclude=rsync --exclude=configure --exclude=config.log 
/home/sits/orig/rsync-2.4.6/exclude.c ./exclude.c
--- /home/sits/orig/rsync-2.4.6/exclude.c       Wed Sep  6 13:46:43 2000
+++ ./exclude.c Sat Sep 29 21:45:53 2001
@@ -27,6 +27,10 @@
 
 static struct exclude_struct **exclude_list;
 
+/* Global CVS exclude list, set from cvs_ignore_list, ~/.cvsignore
+   and getenv("CVSIGNORE"). */
+static struct exclude_struct **global_cvs_exclude_list;
+
 /* build an exclude structure given a exclude pattern */
 static struct exclude_struct *make_exclude(char *pattern, int include)
 {
@@ -125,6 +129,7 @@
                  STRUCT_STAT *st)
 {
        int n;
+       extern int cvs_exclude;
 
        if (name && (name[0] == '.') && !name[1])
                /* never exclude '.', even if somebody does --exclude '*' */
@@ -142,6 +147,18 @@
                                return !local_exclude_list[n]->include;
        }
 
+       /* The global CVS exclude list must be checked last, since the user may
+          have files under CVS control (eg directories called "core" or
+          "tags") that should not be excluded if cvs_exclude is set.  CVS
+          controlled files and directories will be set in
+          local_exclude_list.  */
+       if (cvs_exclude) {
+               for (n=0; global_cvs_exclude_list[n]; n++)
+                       if (check_one_exclude(name,
+                                             global_cvs_exclude_list[n],st))
+                               return !global_cvs_exclude_list[n]->include;
+       }
+
        return 0;
 }
 
@@ -319,16 +336,21 @@
        return(t);
 }
 
-       
-void add_exclude_line(char *p)
+static void add_exclude_line_to_list(char *p,
+                                    struct exclude_struct ***list)
 {
        char *tok;
        if (!p || !*p) return;
        p = strdup(p);
        if (!p) out_of_memory("add_exclude_line");
        for (tok=get_exclude_tok(p); tok; tok=get_exclude_tok(NULL))
-               add_exclude(tok, 0);
+               add_exclude_list(tok, list, 0);
        free(p);
+}      
+       
+void add_exclude_line(char *p)
+{
+       add_exclude_line_to_list(p, &exclude_list);
 }
 
 void add_include_line(char *p)
@@ -342,6 +364,35 @@
        free(p);
 }
 
+/* Include all entries from fname, which is a CVS/Entries file, into list */
+void add_cvs_entries(char *fname, struct exclude_struct ***list)
+{
+       char line[MAXPATHLEN];
+       FILE *f = fopen(fname,"r");
+       if (f) {
+               while (fgets(line,MAXPATHLEN,f)) {
+                       int len = strlen(line);
+                       int offset = -1;
+
+                       /* Determine offset for filename in line, depending
+                          if the entry is a directory or a file */
+                       if (len >= 4 && line[0] == 'D' && line[1] == '/')
+                               offset = 2;
+                       else if (len >= 3 && line[0] == '/')
+                               offset = 1;
+
+                       /* Extract the entry name */
+                       if (offset != -1) {
+                               char *p = strchr(&line[offset],'/');
+                               if (p) {
+                                       *p = 0;
+                                       add_exclude_list(&line[offset],
+                                                        list, 1);
+                               }
+                       }
+               }
+       }
+}
 
 static char *cvs_ignore_list[] = {
   "RCS","SCCS","CVS","CVS.adm","RCSLOG","cvslog.*",
@@ -359,12 +410,13 @@
        int i;
   
        for (i=0; cvs_ignore_list[i]; i++)
-               add_exclude(cvs_ignore_list[i], 0);
+               add_exclude_list(cvs_ignore_list[i], &global_cvs_exclude_list, 0);
 
        if ((p=getenv("HOME")) && strlen(p) < (MAXPATHLEN-12)) {
                slprintf(fname,sizeof(fname), "%s/.cvsignore",p);
-               add_exclude_file(fname,0,0);
+               global_cvs_exclude_list =
+                       make_exclude_list(fname, global_cvs_exclude_list, 0, 0);
        }
 
-       add_exclude_line(getenv("CVSIGNORE"));
+       add_exclude_line_to_list(getenv("CVSIGNORE"), &global_cvs_exclude_list);
 }
Binary files /home/sits/orig/rsync-2.4.6/exclude.o and ./exclude.o differ
diff -r -u --exclude=.o --exclude=rsync --exclude=configure --exclude=config.log 
/home/sits/orig/rsync-2.4.6/flist.c ./flist.c
--- /home/sits/orig/rsync-2.4.6/flist.c Wed Sep  6 13:46:43 2000
+++ ./flist.c   Sat Sep 29 21:46:59 2001
@@ -663,9 +663,26 @@
        local_exclude_list = NULL;
 
        if (cvs_exclude) {
+               /* Add the files specified in CVS/Entries to
+                  local_include_list, since they may contain directory
+                  names like "core" and "tags" which shouldn't be
+                  ignored.  Note this should be added before the exclude
+                  patterns in ./.cvsignore, since it may contain bogus
+                  entries that actually refer to CVS controlled files. */
+               if (strlen(fname) + strlen("CVS/Entries") <= MAXPATHLEN-1) {
+                       strcpy(p,"CVS/Entries");
+                       add_cvs_entries(fname,&local_exclude_list);
+               } else {
+                       io_error = 1;
+                       rprintf(FINFO,"cannot scan CVS/Entries in long-named directory 
+%s\n",fname);
+               }
+
+               /* Add the exclude patterns specified in the ./.cvsignore file
+                  if it exists. */
                if (strlen(fname) + strlen(".cvsignore") <= MAXPATHLEN-1) {
                        strcpy(p,".cvsignore");
-                       local_exclude_list = make_exclude_list(fname,NULL,0,0);
+                       local_exclude_list = 
+make_exclude_list(fname,local_exclude_list,
+                                                              0,0);
                } else {
                        io_error = 1;
                        rprintf(FINFO,"cannot cvs-exclude in long-named directory 
%s\n",fname);
@@ -681,6 +698,7 @@
                send_file_name(f,flist,fname,recurse,0);
        }
 
+       /* Free the exclude entries in local_exclude_list */
        if (local_exclude_list) {
                add_exclude_list("!", &local_exclude_list, 0);
        }
Binary files /home/sits/orig/rsync-2.4.6/flist.o and ./flist.o differ
diff -r -u --exclude=.o --exclude=rsync --exclude=configure --exclude=config.log 
/home/sits/orig/rsync-2.4.6/proto.h ./proto.h
--- /home/sits/orig/rsync-2.4.6/proto.h Wed Sep  6 13:46:43 2000
+++ ./proto.h   Sat Sep 29 20:33:47 2001
@@ -33,6 +33,7 @@
 char *get_exclude_tok(char *p);
 void add_exclude_line(char *p);
 void add_include_line(char *p);
+void add_cvs_entries(char *fname, struct exclude_struct ***list);
 void add_cvs_excludes(void);
 int sparse_end(int f);
 int write_file(int f,char *buf,int len);


Reply via email to