Skip to content

Commit 1d1d69b

Browse files
dschogitster
authored andcommitted
path: add is_ntfs_dotgit() helper
We do not allow paths with a ".git" component to be added to the index, as that would mean repository contents could overwrite our repository files. However, asking "is this path the same as .git" is not as simple as strcmp() on some filesystems. On NTFS (and FAT32), there exist so-called "short names" for backwards-compatibility: 8.3 compliant names that refer to the same files as their long names. As ".git" is not an 8.3 compliant name, a short name is generated automatically, typically "git~1". Depending on the Windows version, any combination of trailing spaces and periods are ignored, too, so that both "git~1." and ".git." still refer to the Git directory. The reason is that 8.3 stores file names shorter than 8 characters with trailing spaces. So literally, it does not matter for the short name whether it is padded with spaces or whether it is shorter than 8 characters, it is considered to be the exact same. The period is the separator between file name and file extension, and again, an empty extension consists just of spaces in 8.3 format. So technically, we would need only take care of the equivalent of this regex: (\.git {0,4}|git~1 {0,3})\. {0,3} However, there are indications that at least some Windows versions might be more lenient and accept arbitrary combinations of trailing spaces and periods and strip them out. So we're playing it real safe here. Besides, there can be little doubt about the intention behind using file names matching even the more lenient pattern specified above, therefore we should be fine with disallowing such patterns. Extra care is taken to catch names such as '.\\.git\\booh' because the backslash is marked as a directory separator only on Windows, and we want to use this new helper function also in fsck on other platforms. A big thank you goes to Ed Thomson and an unnamed Microsoft engineer for the detailed analysis performed to come up with the corresponding fixes for libgit2. This commit adds a function to detect whether a given file name can refer to the Git directory by mistake. Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a18fcc9 commit 1d1d69b

File tree

2 files changed

+34
-0
lines changed

2 files changed

+34
-0
lines changed

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes);
759759
char *strip_path_suffix(const char *path, const char *suffix);
760760
int daemon_avoid_alias(const char *path);
761761
int offset_1st_component(const char *path);
762+
extern int is_ntfs_dotgit(const char *name);
762763

763764
/* object replacement */
764765
#define READ_SHA1_FILE_REPLACE 1

path.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,3 +830,36 @@ int offset_1st_component(const char *path)
830830
return 2 + is_dir_sep(path[2]);
831831
return is_dir_sep(path[0]);
832832
}
833+
834+
static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
835+
{
836+
if (len < skip)
837+
return 0;
838+
len -= skip;
839+
path += skip;
840+
while (len-- > 0) {
841+
char c = *(path++);
842+
if (c != ' ' && c != '.')
843+
return 0;
844+
}
845+
return 1;
846+
}
847+
848+
int is_ntfs_dotgit(const char *name)
849+
{
850+
int len;
851+
852+
for (len = 0; ; len++)
853+
if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
854+
if (only_spaces_and_periods(name, len, 4) &&
855+
!strncasecmp(name, ".git", 4))
856+
return 1;
857+
if (only_spaces_and_periods(name, len, 5) &&
858+
!strncasecmp(name, "git~1", 5))
859+
return 1;
860+
if (name[len] != '\\')
861+
return 0;
862+
name += len + 1;
863+
len = -1;
864+
}
865+
}

0 commit comments

Comments
 (0)