I took a stab at addressing Savannah bug #44102 [1], which pointed out
Patch will silently change file permissions, even if the original
permissions listed in the diff file do not match the actual permissions
on the disk.

While looking into this issue I discovered a second, related issue. The
"old mode" field of diff files was always being read in as "0". This
meant we were always working with bad data on the starting point of file
permissions. I've attempted to fix this.

Also, I added a warning so when file permissions do not match "old mode"
the user is told. Also, if "--force" is not used, the original file
permissions are maintained. Both fixes are in the attached patch.

- Jesse

1. https://savannah.gnu.org/bugs/index.php?44102
diff --git a/src/patch.c b/src/patch.c
index 3794319..da4872a 100644
--- a/src/patch.c
+++ b/src/patch.c
@@ -580,7 +580,23 @@ main (int argc, char **argv)
                      struct timespec new_time = pch_timestamp (! reverse);
                      mode_t mode = file_type |
                          ((set_mode ? new_mode : instat.st_mode) & S_IRWXUGO);
-
+                      /* If mode is changing and old mode is not
+                         the same as expected in the patch, warn. */
+                      if ( (old_mode) && (old_mode != instat.st_mode) )
+                      {
+                          if (! force)
+                          { 
+                              say("Warning: original file's permission mode 
does not "
+                                  "match expected mode in patch file. Leaving "
+                                  "original mode in place. Use --force to 
overrule.\n");
+                              mode = instat.st_mode;
+                          }
+                          else
+                          {
+                              say("Warning: original file's permission mode 
does not "
+                                  "match expected mode in patch file.\n"); 
+                          }
+                      }
                      if ((set_time | set_utc) && new_time.tv_sec != -1)
                        {
                          struct timespec old_time = pch_timestamp (reverse);
diff --git a/src/pch.c b/src/pch.c
index cb54e03..5e81049 100644
--- a/src/pch.c
+++ b/src/pch.c
@@ -343,8 +343,14 @@ fetchmode (char const *str)
      }
    if (*s == '\r')
      s++;
+   /*
+   This check causes old mode to be reset every time.
    if (*s != '\n')
+   {
+     printf("DEBUG resetting mode due to newline\n");
      mode = 0;
+   }
+   */
 
     /* NOTE: The "diff --git" format always sets the file mode permission
        bits of symlinks to 0.  (On Linux, symlinks actually always have

Reply via email to