Skip to content

Commit 0da04fb

Browse files
dschoGit for Windows Build Agent
authored andcommitted
Merge branch 'wsl-file-mode-bits'
This patch introduces support to set special NTFS attributes that are interpreted by the Windows Subsystem for Linux as file mode bits, UID and GID. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 210de4c + 2f07ee7 commit 0da04fb

File tree

7 files changed

+178
-2
lines changed

7 files changed

+178
-2
lines changed

Documentation/config/core.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,3 +755,9 @@ core.maxTreeDepth::
755755
tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
756756
to allow Git to abort cleanly, and should not generally need to
757757
be adjusted. The default is 4096.
758+
759+
core.WSLCompat::
760+
Tells Git whether to enable wsl compatibility mode.
761+
The default value is false. When set to true, Git will set the mode
762+
bits of the file in the way of wsl, so that the executable flag of
763+
files can be set or read correctly.

compat/mingw.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "win32/fscache.h"
2525
#include "../attr.h"
2626
#include "../string-list.h"
27+
#include "win32/wsl.h"
2728

2829
#define HCAST(type, handle) ((type)(intptr_t)handle)
2930

@@ -811,6 +812,11 @@ int mingw_open (const char *filename, int oflags, ...)
811812

812813
fd = open_fn(wfilename, oflags, mode);
813814

815+
if ((oflags & O_CREAT) && fd >= 0 && are_wsl_compatible_mode_bits_enabled()) {
816+
_mode_t wsl_mode = S_IFREG | (mode&0777);
817+
set_wsl_mode_bits_by_handle((HANDLE)_get_osfhandle(fd), wsl_mode);
818+
}
819+
814820
if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
815821
DWORD attrs = GetFileAttributesW(wfilename);
816822
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
@@ -1098,6 +1104,11 @@ int mingw_lstat(const char *file_name, struct stat *buf)
10981104
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
10991105
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
11001106
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
1107+
if (S_ISREG(buf->st_mode) &&
1108+
are_wsl_compatible_mode_bits_enabled()) {
1109+
copy_wsl_mode_bits_from_disk(wfilename, -1,
1110+
&buf->st_mode);
1111+
}
11011112
return 0;
11021113
}
11031114

@@ -1149,6 +1160,8 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
11491160
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
11501161
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
11511162
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
1163+
if (are_wsl_compatible_mode_bits_enabled())
1164+
get_wsl_mode_bits_by_handle(hnd, &buf->st_mode);
11521165
return 0;
11531166
}
11541167

compat/win32/fscache.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "config.h"
99
#include "../../mem-pool.h"
1010
#include "ntifs.h"
11+
#include "wsl.h"
1112

1213
static volatile long initialized;
1314
static DWORD dwTlsIndex;
@@ -237,6 +238,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
237238
&(fse->u.s.st_mtim));
238239
filetime_to_timespec((FILETIME *)&(fdata->CreationTime),
239240
&(fse->u.s.st_ctim));
241+
if (fdata->EaSize > 0 && are_wsl_compatible_mode_bits_enabled()) {
242+
copy_wsl_mode_bits_from_disk(fdata->FileName,
243+
fdata->FileNameLength / sizeof(wchar_t), &fse->st_mode);
244+
}
240245

241246
return fse;
242247
}

compat/win32/wsl.c

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#include "../../git-compat-util.h"
2+
#include "../win32.h"
3+
#include "../../repository.h"
4+
#include "config.h"
5+
#include "ntifs.h"
6+
#include "wsl.h"
7+
8+
int are_wsl_compatible_mode_bits_enabled(void)
9+
{
10+
/* default to `false` during initialization */
11+
static const int fallback = 0;
12+
static int enabled = -1;
13+
14+
if (enabled < 0) {
15+
/* avoid infinite recursion */
16+
if (!the_repository)
17+
return fallback;
18+
19+
if (the_repository->config &&
20+
the_repository->config->hash_initialized &&
21+
git_config_get_bool("core.wslcompat", &enabled) < 0)
22+
enabled = 0;
23+
}
24+
25+
return enabled < 0 ? fallback : enabled;
26+
}
27+
28+
int copy_wsl_mode_bits_from_disk(const wchar_t *wpath, ssize_t wpathlen,
29+
_mode_t *mode)
30+
{
31+
int ret = -1;
32+
HANDLE h;
33+
if (wpathlen >= 0) {
34+
/*
35+
* It's caller's duty to make sure wpathlen is reasonable so
36+
* it does not overflow.
37+
*/
38+
wchar_t *fn2 = (wchar_t*)alloca((wpathlen + 1) * sizeof(wchar_t));
39+
memcpy(fn2, wpath, wpathlen * sizeof(wchar_t));
40+
fn2[wpathlen] = 0;
41+
wpath = fn2;
42+
}
43+
h = CreateFileW(wpath, FILE_READ_EA | SYNCHRONIZE,
44+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
45+
NULL, OPEN_EXISTING,
46+
FILE_FLAG_BACKUP_SEMANTICS |
47+
FILE_FLAG_OPEN_REPARSE_POINT,
48+
NULL);
49+
if (h != INVALID_HANDLE_VALUE) {
50+
ret = get_wsl_mode_bits_by_handle(h, mode);
51+
CloseHandle(h);
52+
}
53+
return ret;
54+
}
55+
56+
#define LX_FILE_METADATA_HAS_UID 0x1
57+
#define LX_FILE_METADATA_HAS_GID 0x2
58+
#define LX_FILE_METADATA_HAS_MODE 0x4
59+
#define LX_FILE_METADATA_HAS_DEVICE_ID 0x8
60+
#define LX_FILE_CASE_SENSITIVE_DIR 0x10
61+
typedef struct _FILE_STAT_LX_INFORMATION {
62+
LARGE_INTEGER FileId;
63+
LARGE_INTEGER CreationTime;
64+
LARGE_INTEGER LastAccessTime;
65+
LARGE_INTEGER LastWriteTime;
66+
LARGE_INTEGER ChangeTime;
67+
LARGE_INTEGER AllocationSize;
68+
LARGE_INTEGER EndOfFile;
69+
uint32_t FileAttributes;
70+
uint32_t ReparseTag;
71+
uint32_t NumberOfLinks;
72+
ACCESS_MASK EffectiveAccess;
73+
uint32_t LxFlags;
74+
uint32_t LxUid;
75+
uint32_t LxGid;
76+
uint32_t LxMode;
77+
uint32_t LxDeviceIdMajor;
78+
uint32_t LxDeviceIdMinor;
79+
} FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
80+
81+
/*
82+
* This struct is extended from the original FILE_FULL_EA_INFORMATION of
83+
* Microsoft Windows.
84+
*/
85+
struct wsl_full_ea_info_t {
86+
uint32_t NextEntryOffset;
87+
uint8_t Flags;
88+
uint8_t EaNameLength;
89+
uint16_t EaValueLength;
90+
char EaName[7];
91+
char EaValue[4];
92+
char Padding[1];
93+
};
94+
95+
enum {
96+
FileStatLxInformation = 70,
97+
};
98+
__declspec(dllimport) NTSTATUS WINAPI
99+
NtQueryInformationFile(HANDLE FileHandle,
100+
PIO_STATUS_BLOCK IoStatusBlock,
101+
PVOID FileInformation, ULONG Length,
102+
uint32_t FileInformationClass);
103+
__declspec(dllimport) NTSTATUS WINAPI
104+
NtSetInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
105+
PVOID FileInformation, ULONG Length,
106+
uint32_t FileInformationClass);
107+
__declspec(dllimport) NTSTATUS WINAPI
108+
NtSetEaFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
109+
PVOID EaBuffer, ULONG EaBufferSize);
110+
111+
int set_wsl_mode_bits_by_handle(HANDLE h, _mode_t mode)
112+
{
113+
uint32_t value = mode;
114+
struct wsl_full_ea_info_t ea_info;
115+
IO_STATUS_BLOCK iob;
116+
/* mode should be valid to make WSL happy */
117+
assert(S_ISREG(mode) || S_ISDIR(mode));
118+
ea_info.NextEntryOffset = 0;
119+
ea_info.Flags = 0;
120+
ea_info.EaNameLength = 6;
121+
ea_info.EaValueLength = sizeof(value); /* 4 */
122+
strlcpy(ea_info.EaName, "$LXMOD", sizeof(ea_info.EaName));
123+
memcpy(ea_info.EaValue, &value, sizeof(value));
124+
ea_info.Padding[0] = 0;
125+
return NtSetEaFile(h, &iob, &ea_info, sizeof(ea_info));
126+
}
127+
128+
int get_wsl_mode_bits_by_handle(HANDLE h, _mode_t *mode)
129+
{
130+
FILE_STAT_LX_INFORMATION fxi;
131+
IO_STATUS_BLOCK iob;
132+
if (NtQueryInformationFile(h, &iob, &fxi, sizeof(fxi),
133+
FileStatLxInformation) == 0) {
134+
if (fxi.LxFlags & LX_FILE_METADATA_HAS_MODE)
135+
*mode = (_mode_t)fxi.LxMode;
136+
return 0;
137+
}
138+
return -1;
139+
}

compat/win32/wsl.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef COMPAT_WIN32_WSL_H
2+
#define COMPAT_WIN32_WSL_H
3+
4+
int are_wsl_compatible_mode_bits_enabled(void);
5+
6+
int copy_wsl_mode_bits_from_disk(const wchar_t *wpath, ssize_t wpathlen,
7+
_mode_t *mode);
8+
9+
int get_wsl_mode_bits_by_handle(HANDLE h, _mode_t *mode);
10+
int set_wsl_mode_bits_by_handle(HANDLE h, _mode_t mode);
11+
12+
#endif

config.mak.uname

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ endif
488488
compat/win32/path-utils.o \
489489
compat/win32/pthread.o compat/win32/syslog.o \
490490
compat/win32/trace2_win32_process_info.o \
491-
compat/win32/dirent.o compat/win32/fscache.o
491+
compat/win32/dirent.o compat/win32/fscache.o compat/win32/wsl.o
492492
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DDETECT_MSYS_TTY -DENSURE_MSYSTEM_IS_SET -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
493493
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO
494494
# invalidcontinue.obj allows Git's source code to close the same file
@@ -679,7 +679,7 @@ ifeq ($(uname_S),MINGW)
679679
compat/win32/flush.o \
680680
compat/win32/path-utils.o \
681681
compat/win32/pthread.o compat/win32/syslog.o \
682-
compat/win32/dirent.o compat/win32/fscache.o
682+
compat/win32/dirent.o compat/win32/fscache.o compat/win32/wsl.o
683683
BASIC_CFLAGS += -DWIN32
684684
EXTLIBS += -lws2_32
685685
GITLIBS += git.res

contrib/buildsystems/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
306306
compat/win32/syslog.c
307307
compat/win32/trace2_win32_process_info.c
308308
compat/win32/dirent.c
309+
compat/win32/wsl.c
309310
compat/nedmalloc/nedmalloc.c
310311
compat/strdup.c
311312
compat/win32/fscache.c)

0 commit comments

Comments
 (0)