diff --git a/compat/mingw.c b/compat/mingw.c index 0f8fc0d8ad9c19..1fd660ba8207d4 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); filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim)); filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim)); @@ -2130,6 +2130,13 @@ int mingw_rename(const char *pold, const char *pnew) return 0; gle = GetLastError(); + if (gle == ERROR_ACCESS_DENIED && is_inside_windows_container()) { + /* Fall back to copy to destination & remove source */ + if (CopyFileW(wpold, wpnew, FALSE) && !mingw_unlink(pold)) + return 0; + gle = GetLastError(); + } + /* revert file attributes on failure */ if (attrs != INVALID_FILE_ATTRIBUTES) SetFileAttributesW(wpnew, attrs); @@ -3061,3 +3068,62 @@ const char *program_data_config(void) } return *path.buf ? path.buf : NULL; } + +/* + * Based on https://stackoverflow.com/questions/43002803 + * + * [HKLM\SYSTEM\CurrentControlSet\Services\cexecsvc] + * "DisplayName"="@%systemroot%\\system32\\cexecsvc.exe,-100" + * "ErrorControl"=dword:00000001 + * "ImagePath"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,00, + * 6f,00,74,00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00, + * 5c,00,63,00,65,00,78,00,65,00,63,00,73,00,76,00,63,00,2e,00,65,00,78,00, + * 65,00,00,00 + * "Start"=dword:00000002 + * "Type"=dword:00000010 + * "Description"="@%systemroot%\\system32\\cexecsvc.exe,-101" + * "ObjectName"="LocalSystem" + * "ServiceSidType"=dword:00000001 + */ +int is_inside_windows_container(void) +{ + static int inside_container = -1; /* -1 uninitialized */ + const char *key = "SYSTEM\\CurrentControlSet\\Services\\cexecsvc"; + HKEY handle = NULL; + + if (inside_container != -1) + return inside_container; + + inside_container = ERROR_SUCCESS == + RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &handle); + RegCloseKey(handle); + + return inside_container; +} + +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) { + 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; + if (!(attr & FILE_ATTRIBUTE_READONLY)) + fMode |= S_IWRITE; + return fMode; +} diff --git a/compat/mingw.h b/compat/mingw.h index 4849fc83046f7e..46b2ac27146aa0 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -673,3 +673,8 @@ static int mingw_main(c,v) * Used by Pthread API implementation for Windows */ extern int err_win_to_posix(DWORD winerr); + +/* + * Check current process is inside Windows Container. + */ +extern int is_inside_windows_container(void); diff --git a/compat/win32.h b/compat/win32.h index 671bcc81f93351..299f01bdf0f5a4 100644 --- a/compat/win32.h +++ b/compat/win32.h @@ -6,19 +6,7 @@ #include #endif -static inline int file_attr_to_st_mode (DWORD attr, DWORD tag) -{ - int fMode = S_IREAD; - if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && tag == IO_REPARSE_TAG_SYMLINK) - fMode |= S_IFLNK; - else if (attr & FILE_ATTRIBUTE_DIRECTORY) - fMode |= S_IFDIR; - else - fMode |= S_IFREG; - if (!(attr & FILE_ATTRIBUTE_READONLY)) - fMode |= S_IWRITE; - return fMode; -} +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 ff0bf80db31575..2f6a4f9eb80c77 100644 --- a/compat/win32/fscache.c +++ b/compat/win32/fscache.c @@ -150,8 +150,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); filetime_to_timespec(&(fdata->ftLastAccessTime), &(fse->st_atim));