Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 2e2a2d1

Browse files
committed
NTFS: Prevent problematic paths from being checked out
On Windows' default filesystems, FAT and NTFS, DOS-style 8.3 file names are supported for backwards compatibility. That means that there are multiple ways to reference the same file. For example, the file credential-cache--daemon.c can also be accessed via CREDEN~1.C (unless another file has already been mapped to that so-called "short name", i.e. the exact short name is unpredictable). Since this mapping is unpredictable, we need to disallow such file names on Windows, and while at it, we also exclude other file names incompatible with Windows' file systems (e.g. NUL, CON, etc). We use the core.protectNTFS guard introduced in the previous commit to make sure that we prevent such file names only when appropriate. 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. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 7bf3a55 commit 2e2a2d1

File tree

3 files changed

+95
-1
lines changed

3 files changed

+95
-1
lines changed

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,8 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes);
784784
char *strip_path_suffix(const char *path, const char *suffix);
785785
int daemon_avoid_alias(const char *path);
786786
int offset_1st_component(const char *path);
787+
extern int is_ntfs_reserved_character(char c);
788+
extern int is_ntfs_reserved(const char *path, size_t len);
787789
extern int is_ntfs_dotgit(const char *name);
788790

789791
/* object replacement */

path.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,3 +863,87 @@ int is_ntfs_dotgit(const char *name)
863863
len = -1;
864864
}
865865
}
866+
867+
/*
868+
* This function detects characters incompatible with NT paths.
869+
*/
870+
int is_ntfs_reserved_character(char c)
871+
{
872+
switch (c) {
873+
case '\\':
874+
case '<':
875+
case '>':
876+
case ':':
877+
case '"':
878+
case '|':
879+
case '?':
880+
case '*':
881+
return 1;
882+
}
883+
884+
if (c >= 0 && c < 32)
885+
return 1;
886+
887+
return 0;
888+
}
889+
890+
static int conflicts_with_ntfs_short_name(const char *path, size_t len)
891+
{
892+
if (len < 3 || len > 8 + 1 + 3)
893+
return 0;
894+
895+
/* strip extension, if any */
896+
if (path[len - 1] == '.')
897+
len--;
898+
else if (path[len - 2] == '.')
899+
len -= 2;
900+
else if (path[len - 3] == '.')
901+
len -= 3;
902+
else if (len > 3 && path[len - 4] == '.')
903+
len -= 4;
904+
905+
/* short names are in 8.3 format */
906+
if (len < 3 || len > 8)
907+
return 0;
908+
909+
if (!isdigit(path[len-1]))
910+
return 0;
911+
912+
while (len && isdigit(path[len-1]))
913+
len--;
914+
915+
return len > 0 && path[len-1] == '~';
916+
}
917+
918+
/*
919+
* This function rejects path components
920+
* - with trailing ~<N>, i.e. paths conflicting with 8.3 DOS paths
921+
* - ending in a dot, a space or a colon,
922+
* - that are reserved on Windows (CON, PRN, etc)
923+
*/
924+
int is_ntfs_reserved(const char *path, size_t len)
925+
{
926+
if (conflicts_with_ntfs_short_name(path, len))
927+
return 1;
928+
929+
switch (path[len - 1]) {
930+
case '.':
931+
case ' ':
932+
case ':':
933+
return 1;
934+
}
935+
936+
if (only_spaces_and_periods(path, len, 3)) {
937+
if (!strncasecmp(path, "CON", 3) ||
938+
!strncasecmp(path, "PRN", 3) ||
939+
!strncasecmp(path, "AUX", 3) ||
940+
!strncasecmp(path, "NUL", 3))
941+
return 1;
942+
}
943+
else if (only_spaces_and_periods(path, len, 4) && isdigit(path[3]))
944+
if (!strncasecmp(path, "COM", 3) ||
945+
!strncasecmp(path, "LPT", 3))
946+
return 1;
947+
948+
return 0;
949+
}

read-cache.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,7 @@ static int verify_dotfile(const char *rest)
769769

770770
int verify_path(const char *path)
771771
{
772+
const char *start;
772773
char c;
773774

774775
if (has_dos_drive_prefix(path))
@@ -777,18 +778,25 @@ int verify_path(const char *path)
777778
goto inside;
778779
for (;;) {
779780
if (!c)
780-
return 1;
781+
return !protect_ntfs ||
782+
!is_ntfs_reserved(start, path - 1 - start);
781783
if (is_dir_sep(c)) {
784+
if (protect_ntfs && is_ntfs_reserved(start,
785+
path - 1 - start))
786+
return 0;
782787
inside:
783788
if (protect_hfs && is_hfs_dotgit(path))
784789
return 0;
785790
if (protect_ntfs && is_ntfs_dotgit(path))
786791
return 0;
792+
start = path;
787793
c = *path++;
788794
if ((c == '.' && !verify_dotfile(path)) ||
789795
is_dir_sep(c) || c == '\0')
790796
return 0;
791797
}
798+
if (protect_ntfs && is_ntfs_reserved_character(c))
799+
return 0;
792800
c = *path++;
793801
}
794802
}

0 commit comments

Comments
 (0)