To: vim-dev@vim.org Subject: Patch 6.2.463 (extra) Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit ------------ Patch 6.2.463 (extra) Problem: Win32: An NTFS file system may contain files with extra info streams. The current method to copy them creates one and then deletes it again. (Peter Toennies) Also, only three streams with hard coded names are copied. Solution: Use BackupRead() to check which info streams the original file contains and only copy these streams. Files: src/os_win32.c *** ../vim-6.2.462/src/os_win32.c Tue Apr 6 21:31:48 2004 --- src/os_win32.c Fri Apr 9 19:24:45 2004 *************** *** 4415,4523 **** #endif /* ! * SUB STREAM: * * NTFS can have sub streams for each file. Normal contents of file is ! * stored in main stream, and external contents (author information and * title and so on) can be stored in sub stream. After Windows 2000, user * can access and store those informations in sub streams via explorer's * property menuitem in right click menu. Those informations in sub streams ! * were lost when copy only main streams. So we have to copy sub streams. * ! * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp */ ! static char * ! get_infostream_name(char *name, char *substream) { ! int len; ! char *newname; ! ! len = strlen(name) + strlen(substream) + 2; ! newname = malloc(len); ! strcpy(newname, name); ! strcat(newname, ":"); ! strcat(newname, substream); ! return newname; ! } ! static int ! copy_substream(char *from, char *to, char *substream) ! { ! int retval = 0; ! HANDLE hFrom, hTo; ! char *from_info, *to_info; ! ! from_info = get_infostream_name(from, substream); ! to_info = get_infostream_name(to, substream); ! hFrom = CreateFile(from_info, GENERIC_READ, 0, NULL, OPEN_EXISTING, ! FILE_ATTRIBUTE_NORMAL, NULL); ! hTo = CreateFile(to_info, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, ! FILE_ATTRIBUTE_NORMAL, NULL); ! if (hFrom != INVALID_HANDLE_VALUE && hTo != INVALID_HANDLE_VALUE) { ! /* Just copy information */ ! DWORD dwCopy, dwSize, dwRead = 0, dwWrite = 0; ! char buf[4096]; ! ! retval = 1; ! dwCopy = GetFileSize(hFrom, NULL); ! while (dwCopy > 0) { ! dwSize = dwCopy > sizeof(buf) ? sizeof(buf) : dwCopy; ! if (!ReadFile(hFrom, buf, dwSize, &dwRead, NULL) ! || dwRead != dwSize ! || !WriteFile(hTo, buf, dwSize, &dwWrite, NULL) ! || dwWrite != dwSize) ! { ! retval = 0; break; - } - dwCopy -= dwSize; } - } - else if (hFrom == INVALID_HANDLE_VALUE && hTo != INVALID_HANDLE_VALUE) - { - /* No source information, delete destination */ CloseHandle(hTo); - hTo = INVALID_HANDLE_VALUE; - retval = DeleteFile(to_info); - retval = 1; - } - else if (hFrom == INVALID_HANDLE_VALUE && hTo == INVALID_HANDLE_VALUE) - { - retval = 1; } ! if (hFrom != INVALID_HANDLE_VALUE) ! CloseHandle(hFrom); ! if (hTo != INVALID_HANDLE_VALUE) ! CloseHandle(hTo); ! free(from_info); ! free(to_info); ! ! return 1; } ! static int copy_infostreams(char_u *from, char_u *to) { ! if (!copy_substream(from, to, "\05SummaryInformation")) ! goto FAILTOCOPY; ! if (!copy_substream(from, to, "\05DocumentSummaryInformation")) ! goto FAILTOCOPY; ! if (!copy_substream(from, to, "\05SebiesnrMkudrfcoIaamtykdDa")) ! goto FAILTOCOPY; ! return 1; ! FAILTOCOPY: ! return 0; } int mch_copy_file_attribute(char_u *from, char_u *to) { ! return copy_infostreams(from, to); } #if defined(MYRESETSTKOFLW) || defined(PROTO) --- 4415,4574 ---- #endif /* ! * SUB STREAM (aka info stream) handling: * * NTFS can have sub streams for each file. Normal contents of file is ! * stored in the main stream, and extra contents (author information and * title and so on) can be stored in sub stream. After Windows 2000, user * can access and store those informations in sub streams via explorer's * property menuitem in right click menu. Those informations in sub streams ! * were lost when copying only the main stream. So we have to copy sub ! * streams. * ! * Incomplete explanation: ! * http://msdn.microsoft.com/library/en-us/dnw2k/html/ntfs5.asp ! * More useful info and an example: ! * http://www.sysinternals.com/ntw2k/source/misc.shtml#streams */ ! /* ! * Copy info stream data "substream". Read from the file with BackupRead(sh) ! * and write to stream "substream" of file "to". ! * Errors are ignored. ! */ ! static void ! copy_substream(HANDLE sh, void *context, WCHAR *to, WCHAR *substream, long len) { ! HANDLE hTo; ! WCHAR *to_name; ! to_name = malloc((wcslen(to) + wcslen(substream) + 1) * sizeof(WCHAR)); ! wcscpy(to_name, to); ! wcscat(to_name, substream); ! hTo = CreateFileW(to_name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, ! FILE_ATTRIBUTE_NORMAL, NULL); ! if (hTo != INVALID_HANDLE_VALUE) { ! long done; ! DWORD todo; ! DWORD readcnt, written; ! char buf[4096]; ! ! /* Copy block of bytes at a time. Abort when something goes wrong. */ ! for (done = 0; done < len; done += written) { ! todo = len - done > sizeof(buf) ? sizeof(buf) : len - done; ! if (!BackupRead(sh, (LPBYTE)buf, todo, &readcnt, ! FALSE, FALSE, context) ! || readcnt != todo ! || !WriteFile(hTo, buf, todo, &written, NULL) ! || written != todo) break; } CloseHandle(hTo); } ! free(to_name); } ! /* ! * Copy info streams from file "from" to file "to". ! */ ! static void copy_infostreams(char_u *from, char_u *to) { ! WCHAR *fromw; ! WCHAR *tow; ! HANDLE sh; ! WIN32_STREAM_ID sid; ! int headersize; ! WCHAR streamname[_MAX_PATH]; ! DWORD readcount; ! void *context = NULL; ! DWORD lo, hi; ! int len; ! ! /* Convert the file names to wide characters. */ ! fromw = enc_to_ucs2(from, NULL); ! tow = enc_to_ucs2(to, NULL); ! if (fromw != NULL && tow != NULL) ! { ! /* Open the file for reading. */ ! sh = CreateFileW(fromw, GENERIC_READ, FILE_SHARE_READ, NULL, ! OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); ! if (sh != INVALID_HANDLE_VALUE) ! { ! /* Use BackupRead() to find the info streams. Repeat until we ! * have done them all.*/ ! for (;;) ! { ! /* Get the header to find the length of the stream name. If ! * the "readcount" is zero we have done all info streams. */ ! ZeroMemory(&sid, sizeof(WIN32_STREAM_ID)); ! headersize = (char *)&sid.cStreamName - (char *)&sid.dwStreamId; ! if (!BackupRead(sh, (LPBYTE)&sid, headersize, ! &readcount, FALSE, FALSE, &context) ! || readcount == 0) ! break; ! ! /* We only deal with streams that have a name. The normal ! * file data appears to be without a name, even though docs ! * suggest it is called "::$DATA". */ ! if (sid.dwStreamNameSize > 0) ! { ! /* Read the stream name. */ ! if (!BackupRead(sh, (LPBYTE)streamname, ! sid.dwStreamNameSize, ! &readcount, FALSE, FALSE, &context)) ! break; ! ! /* Copy an info stream with a name ":anything:$DATA". ! * Skip "::$DATA", it has no stream name (examples suggest ! * it might be used for the normal file contents). ! * Note that BackupRead() counts bytes, but the name is in ! * wide characters. */ ! len = readcount / sizeof(WCHAR); ! streamname[len] = 0; ! if (len > 7 && wcsicmp(streamname + len - 6, ! L":$DATA") == 0) ! { ! streamname[len - 6] = 0; ! copy_substream(sh, &context, tow, streamname, ! (long)sid.Size.LowPart); ! } ! } ! ! /* Advance to the next stream. We might try seeking too far, ! * but BackupSeek() doesn't skip over stream borders, thus ! * that's OK. */ ! (void)BackupSeek(sh, sid.Size.LowPart, sid.Size.HighPart, ! &lo, &hi, &context); ! } ! ! /* Clear the context. */ ! (void)BackupRead(sh, NULL, 0, &readcount, TRUE, FALSE, &context); ! ! CloseHandle(sh); ! } ! } ! vim_free(fromw); ! vim_free(tow); } + /* + * Copy file attributes from file "from" to file "to". + * For Windows NT and later we copy info streams. + * Always returns zero, errors are ignored. + */ int mch_copy_file_attribute(char_u *from, char_u *to) { ! /* File streams only work on Windows NT and later. */ ! PlatformId(); ! if (g_PlatformId == VER_PLATFORM_WIN32_NT) ! copy_infostreams(from, to); ! return 0; } #if defined(MYRESETSTKOFLW) || defined(PROTO) *** ../vim-6.2.462/src/version.c Fri Apr 9 19:30:09 2004 --- src/version.c Fri Apr 9 19:49:52 2004 *************** *** 639,640 **** --- 639,642 ---- { /* Add new patch number below this line */ + /**/ + 463, /**/ -- ARTHUR: What? BLACK KNIGHT: None shall pass. ARTHUR: I have no quarrel with you, good Sir knight, but I must cross this bridge. BLACK KNIGHT: Then you shall die. The Quest for the Holy Grail (Monty Python) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// Sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ Project leader for A-A-P -- http://www.A-A-P.org /// \\\ Buy at Amazon and help AIDS victims -- http://ICCF.nl/click1.html ///