diff --git a/compat/mingw.c b/compat/mingw.c index 09e69d097a..2c2c45bb96 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -853,7 +853,7 @@ int mingw_lstat(const char *file_name, struct stat *buf) buf->st_uid = 0; buf->st_nlink = 1; buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes, - findbuf.dwReserved0); + findbuf.dwReserved0, file_name); buf->st_size = S_ISLNK(buf->st_mode) ? MAX_LONG_PATH : fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32); buf->st_dev = buf->st_rdev = 0; /* not used by Git */ @@ -902,7 +902,7 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf) buf->st_dev = buf->st_rdev = 0; /* not used by Git */ buf->st_gid = buf->st_uid = 0; buf->st_nlink = 1; - buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes, 0); + buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes, 0, NULL); buf->st_size = fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32); buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); @@ -3072,12 +3072,25 @@ int is_inside_windows_container(void) return inside_container; } -int file_attr_to_st_mode (DWORD attr, DWORD tag) +int file_attr_to_st_mode (DWORD attr, DWORD tag, const char *path) { int fMode = S_IREAD; - if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && tag == IO_REPARSE_TAG_SYMLINK) - fMode |= S_IFLNK; - else if (attr & FILE_ATTRIBUTE_DIRECTORY) + if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && + tag == IO_REPARSE_TAG_SYMLINK) { + int flag = S_IFLNK; + char buf[MAX_LONG_PATH]; + + /* + * Windows containers' mapped volumes are marked as reparse + * points and look like symbolic links, but they are not. + */ + if (path && is_inside_windows_container() && + readlink(path, buf, sizeof(buf)) > 27 && + starts_with(buf, "/ContainerMappedDirectories/")) + flag = S_IFDIR; + + fMode |= flag; + } else if (attr & FILE_ATTRIBUTE_DIRECTORY) fMode |= S_IFDIR; else fMode |= S_IFREG; diff --git a/compat/win32.h b/compat/win32.h index 52169ae19f..299f01bdf0 100644 --- a/compat/win32.h +++ b/compat/win32.h @@ -6,7 +6,7 @@ #include #endif -extern int file_attr_to_st_mode (DWORD attr, DWORD tag); +extern int file_attr_to_st_mode (DWORD attr, DWORD tag, const char *path); static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) { diff --git a/compat/win32/fscache.c b/compat/win32/fscache.c index 393823389c..b1a2c1d762 100644 --- a/compat/win32/fscache.c +++ b/compat/win32/fscache.c @@ -149,8 +149,21 @@ static struct fsentry *fseentry_create_entry(struct fsentry *list, fse = fsentry_alloc(list, buf, len); + if (fdata->dwReserved0 == IO_REPARSE_TAG_SYMLINK && + sizeof(buf) > (list ? list->len + 1 : 0) + fse->len + 1 && + is_inside_windows_container()) { + size_t off = 0; + if (list) { + memcpy(buf, list->name, list->len); + buf[list->len] = '/'; + off = list->len + 1; + } + memcpy(buf + off, fse->name, fse->len); + buf[off + fse->len] = '\0'; + } + fse->st_mode = file_attr_to_st_mode(fdata->dwFileAttributes, - fdata->dwReserved0); + fdata->dwReserved0, buf); fse->st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH : fdata->nFileSizeLow | (((off_t) fdata->nFileSizeHigh) << 32); fse->st_atime = filetime_to_time_t(&(fdata->ftLastAccessTime));