зеркало из https://github.com/microsoft/git.git
mingw: optionally enable wsl compability file mode bits
The Windows Subsystem for Linux (WSL) version 2 allows to use `chmod` on NTFS volumes provided that they are mounted with metadata enabled (see https://devblogs.microsoft.com/commandline/chmod-chown-wsl-improvements/ for details), for example: $ chmod 0755 /mnt/d/test/a.sh In order to facilitate better collaboration between the Windows version of Git and the WSL version of Git, we can make the Windows version of Git also support reading and writing NTFS file modes in a manner compatible with WSL. Since this slightly slows down operations where lots of files are created (such as an initial checkout), this feature is only enabled when `core.WSLCompat` is set to true. Note that you also have to set `core.fileMode=true` in repositories that have been initialized without enabling WSL compatibility. There are several ways to enable metadata loading for NTFS volumes in WSL, one of which is to modify `/etc/wsl.conf` by adding: ``` [automount] enabled = true options = "metadata,umask=027,fmask=117" ``` And reboot WSL. It can also be enabled temporarily by this incantation: $ sudo umount /mnt/c && sudo mount -t drvfs C: /mnt/c -o metadata,uid=1000,gid=1000,umask=22,fmask=111 It's important to note that this modification is compatible with, but does not depend on WSL. The helper functions in this commit can operate independently and functions normally on devices where WSL is not installed or properly configured. Signed-off-by: xungeng li <xungeng@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Родитель
62cb5ce1c2
Коммит
8cd2d55ce8
|
@ -755,3 +755,9 @@ core.maxTreeDepth::
|
|||
tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe
|
||||
to allow Git to abort cleanly, and should not generally need to
|
||||
be adjusted. The default is 4096.
|
||||
|
||||
core.WSLCompat::
|
||||
Tells Git whether to enable wsl compatibility mode.
|
||||
The default value is false. When set to true, Git will set the mode
|
||||
bits of the file in the way of wsl, so that the executable flag of
|
||||
files can be set or read correctly.
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "win32/fscache.h"
|
||||
#include "../attr.h"
|
||||
#include "../string-list.h"
|
||||
#include "win32/wsl.h"
|
||||
|
||||
#define HCAST(type, handle) ((type)(intptr_t)handle)
|
||||
|
||||
|
@ -809,6 +810,11 @@ int mingw_open (const char *filename, int oflags, ...)
|
|||
|
||||
fd = open_fn(wfilename, oflags, mode);
|
||||
|
||||
if ((oflags & O_CREAT) && fd >= 0 && are_wsl_compatible_mode_bits_enabled()) {
|
||||
_mode_t wsl_mode = S_IFREG | (mode&0777);
|
||||
set_wsl_mode_bits_by_handle((HANDLE)_get_osfhandle(fd), wsl_mode);
|
||||
}
|
||||
|
||||
if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
|
||||
DWORD attrs = GetFileAttributesW(wfilename);
|
||||
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
|
||||
|
@ -1096,6 +1102,11 @@ int mingw_lstat(const char *file_name, struct stat *buf)
|
|||
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
||||
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
|
||||
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
||||
if (S_ISREG(buf->st_mode) &&
|
||||
are_wsl_compatible_mode_bits_enabled()) {
|
||||
copy_wsl_mode_bits_from_disk(wfilename, -1,
|
||||
&buf->st_mode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1147,6 +1158,8 @@ static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
|
|||
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
||||
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
|
||||
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
||||
if (are_wsl_compatible_mode_bits_enabled())
|
||||
get_wsl_mode_bits_by_handle(hnd, &buf->st_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "config.h"
|
||||
#include "../../mem-pool.h"
|
||||
#include "ntifs.h"
|
||||
#include "wsl.h"
|
||||
|
||||
static volatile long initialized;
|
||||
static DWORD dwTlsIndex;
|
||||
|
@ -215,6 +216,10 @@ static struct fsentry *fseentry_create_entry(struct fscache *cache,
|
|||
&(fse->u.s.st_mtim));
|
||||
filetime_to_timespec((FILETIME *)&(fdata->CreationTime),
|
||||
&(fse->u.s.st_ctim));
|
||||
if (fdata->EaSize > 0 && are_wsl_compatible_mode_bits_enabled()) {
|
||||
copy_wsl_mode_bits_from_disk(fdata->FileName,
|
||||
fdata->FileNameLength / sizeof(wchar_t), &fse->st_mode);
|
||||
}
|
||||
|
||||
return fse;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
#include "../../git-compat-util.h"
|
||||
#include "../win32.h"
|
||||
#include "../../repository.h"
|
||||
#include "config.h"
|
||||
#include "ntifs.h"
|
||||
#include "wsl.h"
|
||||
|
||||
int are_wsl_compatible_mode_bits_enabled(void)
|
||||
{
|
||||
/* default to `false` during initialization */
|
||||
static const int fallback = 0;
|
||||
static int enabled = -1;
|
||||
|
||||
if (enabled < 0) {
|
||||
/* avoid infinite recursion */
|
||||
if (!the_repository)
|
||||
return fallback;
|
||||
|
||||
if (the_repository->config &&
|
||||
the_repository->config->hash_initialized &&
|
||||
git_config_get_bool("core.wslcompat", &enabled) < 0)
|
||||
enabled = 0;
|
||||
}
|
||||
|
||||
return enabled < 0 ? fallback : enabled;
|
||||
}
|
||||
|
||||
int copy_wsl_mode_bits_from_disk(const wchar_t *wpath, ssize_t wpathlen,
|
||||
_mode_t *mode)
|
||||
{
|
||||
int ret = -1;
|
||||
HANDLE h;
|
||||
if (wpathlen >= 0) {
|
||||
/*
|
||||
* It's caller's duty to make sure wpathlen is reasonable so
|
||||
* it does not overflow.
|
||||
*/
|
||||
wchar_t *fn2 = (wchar_t*)alloca((wpathlen + 1) * sizeof(wchar_t));
|
||||
memcpy(fn2, wpath, wpathlen * sizeof(wchar_t));
|
||||
fn2[wpathlen] = 0;
|
||||
wpath = fn2;
|
||||
}
|
||||
h = CreateFileW(wpath, FILE_READ_EA | SYNCHRONIZE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS |
|
||||
FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL);
|
||||
if (h != INVALID_HANDLE_VALUE) {
|
||||
ret = get_wsl_mode_bits_by_handle(h, mode);
|
||||
CloseHandle(h);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define LX_FILE_METADATA_HAS_UID 0x1
|
||||
#define LX_FILE_METADATA_HAS_GID 0x2
|
||||
#define LX_FILE_METADATA_HAS_MODE 0x4
|
||||
#define LX_FILE_METADATA_HAS_DEVICE_ID 0x8
|
||||
#define LX_FILE_CASE_SENSITIVE_DIR 0x10
|
||||
typedef struct _FILE_STAT_LX_INFORMATION {
|
||||
LARGE_INTEGER FileId;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
uint32_t FileAttributes;
|
||||
uint32_t ReparseTag;
|
||||
uint32_t NumberOfLinks;
|
||||
ACCESS_MASK EffectiveAccess;
|
||||
uint32_t LxFlags;
|
||||
uint32_t LxUid;
|
||||
uint32_t LxGid;
|
||||
uint32_t LxMode;
|
||||
uint32_t LxDeviceIdMajor;
|
||||
uint32_t LxDeviceIdMinor;
|
||||
} FILE_STAT_LX_INFORMATION, *PFILE_STAT_LX_INFORMATION;
|
||||
|
||||
/*
|
||||
* This struct is extended from the original FILE_FULL_EA_INFORMATION of
|
||||
* Microsoft Windows.
|
||||
*/
|
||||
struct wsl_full_ea_info_t {
|
||||
uint32_t NextEntryOffset;
|
||||
uint8_t Flags;
|
||||
uint8_t EaNameLength;
|
||||
uint16_t EaValueLength;
|
||||
char EaName[7];
|
||||
char EaValue[4];
|
||||
char Padding[1];
|
||||
};
|
||||
|
||||
enum {
|
||||
FileStatLxInformation = 70,
|
||||
};
|
||||
__declspec(dllimport) NTSTATUS WINAPI
|
||||
NtQueryInformationFile(HANDLE FileHandle,
|
||||
PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation, ULONG Length,
|
||||
uint32_t FileInformationClass);
|
||||
__declspec(dllimport) NTSTATUS WINAPI
|
||||
NtSetInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID FileInformation, ULONG Length,
|
||||
uint32_t FileInformationClass);
|
||||
__declspec(dllimport) NTSTATUS WINAPI
|
||||
NtSetEaFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock,
|
||||
PVOID EaBuffer, ULONG EaBufferSize);
|
||||
|
||||
int set_wsl_mode_bits_by_handle(HANDLE h, _mode_t mode)
|
||||
{
|
||||
uint32_t value = mode;
|
||||
struct wsl_full_ea_info_t ea_info;
|
||||
IO_STATUS_BLOCK iob;
|
||||
/* mode should be valid to make WSL happy */
|
||||
assert(S_ISREG(mode) || S_ISDIR(mode));
|
||||
ea_info.NextEntryOffset = 0;
|
||||
ea_info.Flags = 0;
|
||||
ea_info.EaNameLength = 6;
|
||||
ea_info.EaValueLength = sizeof(value); /* 4 */
|
||||
strlcpy(ea_info.EaName, "$LXMOD", sizeof(ea_info.EaName));
|
||||
memcpy(ea_info.EaValue, &value, sizeof(value));
|
||||
ea_info.Padding[0] = 0;
|
||||
return NtSetEaFile(h, &iob, &ea_info, sizeof(ea_info));
|
||||
}
|
||||
|
||||
int get_wsl_mode_bits_by_handle(HANDLE h, _mode_t *mode)
|
||||
{
|
||||
FILE_STAT_LX_INFORMATION fxi;
|
||||
IO_STATUS_BLOCK iob;
|
||||
if (NtQueryInformationFile(h, &iob, &fxi, sizeof(fxi),
|
||||
FileStatLxInformation) == 0) {
|
||||
if (fxi.LxFlags & LX_FILE_METADATA_HAS_MODE)
|
||||
*mode = (_mode_t)fxi.LxMode;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef COMPAT_WIN32_WSL_H
|
||||
#define COMPAT_WIN32_WSL_H
|
||||
|
||||
int are_wsl_compatible_mode_bits_enabled(void);
|
||||
|
||||
int copy_wsl_mode_bits_from_disk(const wchar_t *wpath, ssize_t wpathlen,
|
||||
_mode_t *mode);
|
||||
|
||||
int get_wsl_mode_bits_by_handle(HANDLE h, _mode_t *mode);
|
||||
int set_wsl_mode_bits_by_handle(HANDLE h, _mode_t mode);
|
||||
|
||||
#endif
|
|
@ -488,7 +488,7 @@ endif
|
|||
compat/win32/path-utils.o \
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/trace2_win32_process_info.o \
|
||||
compat/win32/dirent.o compat/win32/fscache.o
|
||||
compat/win32/dirent.o compat/win32/fscache.o compat/win32/wsl.o
|
||||
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\"
|
||||
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO
|
||||
# invalidcontinue.obj allows Git's source code to close the same file
|
||||
|
@ -679,7 +679,7 @@ ifeq ($(uname_S),MINGW)
|
|||
compat/win32/flush.o \
|
||||
compat/win32/path-utils.o \
|
||||
compat/win32/pthread.o compat/win32/syslog.o \
|
||||
compat/win32/dirent.o compat/win32/fscache.o
|
||||
compat/win32/dirent.o compat/win32/fscache.o compat/win32/wsl.o
|
||||
BASIC_CFLAGS += -DWIN32
|
||||
EXTLIBS += -lws2_32
|
||||
GITLIBS += git.res
|
||||
|
|
|
@ -306,6 +306,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
|||
compat/win32/syslog.c
|
||||
compat/win32/trace2_win32_process_info.c
|
||||
compat/win32/dirent.c
|
||||
compat/win32/wsl.c
|
||||
compat/nedmalloc/nedmalloc.c
|
||||
compat/strdup.c
|
||||
compat/win32/fscache.c)
|
||||
|
|
Загрузка…
Ссылка в новой задаче