# HG changeset patch # User Bram Moolenaar # Date 1612209603 -3600 # Node ID 7237ed5f89bdae8884f71a89194c690757d48f22 # Parent 8103ec7fda363d0e048e16ca6af13892a1488817 patch 8.2.2451: MS-Windows: Extended Attributes not preserved Commit: https://github.com/vim/vim/commit/e7bebc495d4014d7bc81f863939c35268cb8e97d Author: Bram Moolenaar Date: Mon Feb 1 20:50:37 2021 +0100 patch 8.2.2451: MS-Windows: Extended Attributes not preserved Problem: MS-Windows: Extended Attributes not preserved. Solution: Preserve Extended Attributes when writing a file. (Ken Takata, closes #7765) diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -33,6 +33,7 @@ // cproto fails on missing include files #ifndef PROTO # include +# include #endif #undef chdir @@ -7254,6 +7255,184 @@ copy_infostreams(char_u *from, char_u *t } /* + * ntdll.dll definitions + */ +#define FileEaInformation 7 +#ifndef STATUS_SUCCESS +# define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) +#endif + +typedef struct _FILE_FULL_EA_INFORMATION_ { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION_, *PFILE_FULL_EA_INFORMATION_; + +typedef struct _FILE_EA_INFORMATION_ { + ULONG EaSize; +} FILE_EA_INFORMATION_, *PFILE_EA_INFORMATION_; + +typedef NTSTATUS (NTAPI *PfnNtOpenFile)( + PHANDLE FileHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG ShareAccess, + ULONG OpenOptions); +typedef NTSTATUS (NTAPI *PfnNtClose)( + HANDLE Handle); +typedef NTSTATUS (NTAPI *PfnNtSetEaFile)( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length); +typedef NTSTATUS (NTAPI *PfnNtQueryEaFile)( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + BOOLEAN ReturnSingleEntry, + PVOID EaList, + ULONG EaListLength, + PULONG EaIndex, + BOOLEAN RestartScan); +typedef NTSTATUS (NTAPI *PfnNtQueryInformationFile)( + HANDLE FileHandle, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID FileInformation, + ULONG Length, + FILE_INFORMATION_CLASS FileInformationClass); +typedef VOID (NTAPI *PfnRtlInitUnicodeString)( + PUNICODE_STRING DestinationString, + PCWSTR SourceString); + +PfnNtOpenFile pNtOpenFile = NULL; +PfnNtClose pNtClose = NULL; +PfnNtSetEaFile pNtSetEaFile = NULL; +PfnNtQueryEaFile pNtQueryEaFile = NULL; +PfnNtQueryInformationFile pNtQueryInformationFile = NULL; +PfnRtlInitUnicodeString pRtlInitUnicodeString = NULL; + +/* + * Load ntdll.dll functions. + */ + static BOOL +load_ntdll(void) +{ + static int loaded = -1; + + if (loaded == -1) + { + HMODULE hNtdll = GetModuleHandle("ntdll.dll"); + if (hNtdll != NULL) + { + pNtOpenFile = (PfnNtOpenFile) GetProcAddress(hNtdll, "NtOpenFile"); + pNtClose = (PfnNtClose) GetProcAddress(hNtdll, "NtClose"); + pNtSetEaFile = (PfnNtSetEaFile) + GetProcAddress(hNtdll, "NtSetEaFile"); + pNtQueryEaFile = (PfnNtQueryEaFile) + GetProcAddress(hNtdll, "NtQueryEaFile"); + pNtQueryInformationFile = (PfnNtQueryInformationFile) + GetProcAddress(hNtdll, "NtQueryInformationFile"); + pRtlInitUnicodeString = (PfnRtlInitUnicodeString) + GetProcAddress(hNtdll, "RtlInitUnicodeString"); + } + if (pNtOpenFile == NULL + || pNtClose == NULL + || pNtSetEaFile == NULL + || pNtQueryEaFile == NULL + || pNtQueryInformationFile == NULL + || pRtlInitUnicodeString == NULL) + loaded = FALSE; + else + loaded = TRUE; + } + return (BOOL) loaded; +} + +/* + * Copy extended attributes (EA) from file "from" to file "to". + */ + static void +copy_extattr(char_u *from, char_u *to) +{ + char_u *fromf = NULL; + char_u *tof = NULL; + WCHAR *fromw = NULL; + WCHAR *tow = NULL; + UNICODE_STRING u; + HANDLE h; + OBJECT_ATTRIBUTES oa; + IO_STATUS_BLOCK iosb; + FILE_EA_INFORMATION_ eainfo = {0}; + void *ea = NULL; + + if (!load_ntdll()) + return; + + // Convert the file names to the fully qualified object names. + fromf = alloc(STRLEN(from) + 5); + tof = alloc(STRLEN(to) + 5); + if (fromf == NULL || tof == NULL) + goto theend; + STRCPY(fromf, "\\??\\"); + STRCAT(fromf, from); + STRCPY(tof, "\\??\\"); + STRCAT(tof, to); + + // Convert the names to wide characters. + fromw = enc_to_utf16(fromf, NULL); + tow = enc_to_utf16(tof, NULL); + if (fromw == NULL || tow == NULL) + goto theend; + + // Get the EA. + pRtlInitUnicodeString(&u, fromw); + InitializeObjectAttributes(&oa, &u, 0, NULL, NULL); + if (pNtOpenFile(&h, FILE_READ_EA, &oa, &iosb, 0, + FILE_NON_DIRECTORY_FILE) != STATUS_SUCCESS) + goto theend; + pNtQueryInformationFile(h, &iosb, &eainfo, sizeof(eainfo), + FileEaInformation); + if (eainfo.EaSize != 0) + { + ea = alloc(eainfo.EaSize); + if (ea != NULL) + { + if (pNtQueryEaFile(h, &iosb, ea, eainfo.EaSize, FALSE, + NULL, 0, NULL, TRUE) != STATUS_SUCCESS) + { + vim_free(ea); + ea = NULL; + } + } + } + pNtClose(h); + + // Set the EA. + if (ea != NULL) + { + pRtlInitUnicodeString(&u, tow); + InitializeObjectAttributes(&oa, &u, 0, NULL, NULL); + if (pNtOpenFile(&h, FILE_WRITE_EA, &oa, &iosb, 0, + FILE_NON_DIRECTORY_FILE) != STATUS_SUCCESS) + goto theend; + + pNtSetEaFile(h, &iosb, ea, eainfo.EaSize); + pNtClose(h); + } + +theend: + vim_free(fromf); + vim_free(tof); + vim_free(fromw); + vim_free(tow); + vim_free(ea); +} + +/* * Copy file attributes from file "from" to file "to". * For Windows NT and later we copy info streams. * Always returns zero, errors are ignored. @@ -7263,6 +7442,7 @@ mch_copy_file_attribute(char_u *from, ch { // File streams only work on Windows NT and later. copy_infostreams(from, to); + copy_extattr(from, to); return 0; } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2451, +/**/ 2450, /**/ 2449,