Skip to content

Commit

Permalink
mingw: Windows Docker volumes are *not* symbolic links
Browse files Browse the repository at this point in the history
... even if they may look like them.

As looking up the target of the "symbolic link" (just to see whether it
starts with `/ContainerMappedDirectories/`) is pretty expensive, we
do it when we can be *really* sure that there is a possibility that this
might be the case.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: JiSeop Moon <zcube@zcube.kr>
  • Loading branch information
dscho committed Sep 22, 2022
1 parent 39c16ac commit 7da6e7b
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 8 deletions.
25 changes: 19 additions & 6 deletions compat/mingw.c
Expand Up @@ -1012,7 +1012,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,
reparse_tag);
reparse_tag, file_name);
buf->st_size = S_ISLNK(buf->st_mode) ? link_len :
fdata.nFileSizeLow | (((off_t) fdata.nFileSizeHigh) << 32);
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
Expand Down Expand Up @@ -1063,7 +1063,7 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
buf->st_gid = 0;
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_dev = buf->st_rdev = 0; /* not used by Git */
Expand Down Expand Up @@ -3907,12 +3907,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;
Expand Down
2 changes: 1 addition & 1 deletion compat/win32.h
Expand Up @@ -6,7 +6,7 @@
#include <windows.h>
#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)
{
Expand Down
24 changes: 23 additions & 1 deletion compat/win32/fscache.c
Expand Up @@ -200,8 +200,30 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
fdata->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ?
fdata->EaSize : 0;

/*
* On certain Windows versions, host directories mapped into
* Windows Containers ("Volumes", see https://docs.docker.com/storage/volumes/)
* look like symbolic links, but their targets are paths that
* are valid only in kernel mode.
*
* Let's work around this by detecting that situation and
* telling Git that these are *not* symbolic links.
*/
if (fse->reparse_tag == 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->dirent.d_name, list->len);
buf[list->len] = '/';
off = list->len + 1;
}
memcpy(buf + off, fse->dirent.d_name, fse->len);
buf[off + fse->len] = '\0';
}

fse->st_mode = file_attr_to_st_mode(fdata->FileAttributes,
fdata->EaSize);
fdata->EaSize, buf);
fse->dirent.d_type = S_ISREG(fse->st_mode) ? DT_REG :
S_ISDIR(fse->st_mode) ? DT_DIR : DT_LNK;
fse->u.s.st_size = S_ISLNK(fse->st_mode) ? MAX_LONG_PATH :
Expand Down

0 comments on commit 7da6e7b

Please sign in to comment.