]> granicus.if.org Git - vim/commitdiff
patch 8.2.2451: MS-Windows: Extended Attributes not preserved v8.2.2451
authorBram Moolenaar <Bram@vim.org>
Mon, 1 Feb 2021 19:50:37 +0000 (20:50 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 1 Feb 2021 19:50:37 +0000 (20:50 +0100)
Problem:    MS-Windows: Extended Attributes not preserved.
Solution:   Preserve Extended Attributes when writing a file. (Ken Takata,
            closes #7765)

src/os_win32.c
src/version.c

index 972f10da5c234aefb585cb49ca70be8623ebf061..7c2051f7f3971756dd4ae10332a847a0139dbbf0 100644 (file)
@@ -33,6 +33,7 @@
 // cproto fails on missing include files
 #ifndef PROTO
 # include <process.h>
+# include <winternl.h>
 #endif
 
 #undef chdir
@@ -7253,6 +7254,184 @@ copy_infostreams(char_u *from, char_u *to)
     vim_free(tow);
 }
 
+/*
+ * 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.
@@ -7263,6 +7442,7 @@ mch_copy_file_attribute(char_u *from, char_u *to)
 {
     // File streams only work on Windows NT and later.
     copy_infostreams(from, to);
+    copy_extattr(from, to);
     return 0;
 }
 
index 2ae792c87f1d14d07bde315c76e3d065d689295b..cc8c728f928185cc580a0384b6abaf430c379622 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2451,
 /**/
     2450,
 /**/