From 3f583916719eeef598d10a5d4e14ef14f008243b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 11 May 2021 07:36:37 -0700 Subject: Merge Dtf --- src/samples/Dtf/Tools/SfxCA/Extract.cpp | 282 ++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 src/samples/Dtf/Tools/SfxCA/Extract.cpp (limited to 'src/samples/Dtf/Tools/SfxCA/Extract.cpp') diff --git a/src/samples/Dtf/Tools/SfxCA/Extract.cpp b/src/samples/Dtf/Tools/SfxCA/Extract.cpp new file mode 100644 index 00000000..171cf52f --- /dev/null +++ b/src/samples/Dtf/Tools/SfxCA/Extract.cpp @@ -0,0 +1,282 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +//--------------------------------------------------------------------- +// CABINET EXTRACTION +//--------------------------------------------------------------------- + +// Globals make this code unsuited for multhreaded use, +// but FDI doesn't provide any other way to pass context. + +// Handle to the FDI (cab extraction) engine. Need access to this in a callback. +static HFDI g_hfdi; + +// FDI is not unicode-aware, so avoid passing these paths through the callbacks. +static const wchar_t* g_szExtractDir; +static const wchar_t* g_szCabFile; + +// Offset into the source file where the cabinet really starts. +// Used to trick FDI into extracting from a concatenated cabinet. +static int g_lCabOffset; + +// Use the secure CRT version of _wsopen if available. +#ifdef __GOT_SECURE_LIB__ +#define _wsopen__s(hf,file,oflag,shflag,pmode) _wsopen_s(&hf,file,oflag,shflag,pmode) +#else +#define _wsopen__s(hf,file,oflag,shflag,pmode) hf = _wsopen(file,oflag,shflag,pmode) +#endif + +/// +/// FDI callback to open a cabinet file. +/// +/// Name of the file to be opened. This parameter +/// is ignored since with our limited use this method is only ever called +/// to open the main cabinet file. +/// Type of operations allowed. +/// Permission setting. +/// Integer file handle, or -1 if the file could not be opened. +/// +/// To support reading from a cabinet that is concatenated onto +/// another file, this function first searches for the offset of the cabinet, +/// then saves that offset for use in recalculating later seeks. +/// +static FNOPEN(CabOpen) +{ + UNREFERENCED_PARAMETER(pszFile); + int hf; + _wsopen__s(hf, g_szCabFile, oflag, _SH_DENYWR, pmode); + if (hf != -1) + { + FDICABINETINFO cabInfo; + int length = _lseek(hf, 0, SEEK_END); + for(int offset = 0; offset < length; offset += 256) + { + if (_lseek(hf, offset, SEEK_SET) != offset) break; + if (FDIIsCabinet(g_hfdi, hf, &cabInfo)) + { + g_lCabOffset = offset; + _lseek(hf, offset, SEEK_SET); + return hf; + } + } + _close(hf); + } + return -1; +} + +/// +/// FDI callback to seek within a file. +/// +/// File handle. +/// Seek distance +/// Whether to seek relative to the +/// beginning, current position, or end of the file. +/// Resultant position within the cabinet. +/// +/// To support reading from a cabinet that is concatenated onto +/// another file, this function recalculates seeks based on the +/// offset that was determined when the cabinet was opened. +/// +static FNSEEK(CabSeek) +{ + if (seektype == SEEK_SET) dist += g_lCabOffset; + int pos = _lseek((int) hf, dist, seektype); + pos -= g_lCabOffset; + return pos; +} + +/// +/// Ensures a directory and its parent directory path exists. +/// +/// Directory path, not including file name. +/// 0 if the directory exists or was successfully created, else nonzero. +/// +/// This function modifies characters in szDirPath, but always restores them +/// regardless of error condition. +/// +static int EnsureDirectoryExists(__inout_z wchar_t* szDirPath) +{ + int ret = 0; + if (!::CreateDirectoryW(szDirPath, NULL)) + { + UINT err = ::GetLastError(); + if (err != ERROR_ALREADY_EXISTS) + { + // Directory creation failed for some reason other than already existing. + // Try to create the parent directory first. + wchar_t* szLastSlash = NULL; + for (wchar_t* sz = szDirPath; *sz; sz++) + { + if (*sz == L'\\') + { + szLastSlash = sz; + } + } + if (szLastSlash) + { + // Temporarily take one directory off the path and recurse. + *szLastSlash = L'\0'; + ret = EnsureDirectoryExists(szDirPath); + *szLastSlash = L'\\'; + + // Try to create the directory if all parents are created. + if (ret == 0 && !::CreateDirectoryW(szDirPath, NULL)) + { + err = ::GetLastError(); + if (err != ERROR_ALREADY_EXISTS) + { + ret = -1; + } + } + } + else + { + ret = -1; + } + } + } + return ret; +} + +/// +/// Ensures a file's directory and its parent directory path exists. +/// +/// Path including file name. +/// 0 if the file's directory exists or was successfully created, else nonzero. +/// +/// This function modifies characters in szFilePath, but always restores them +/// regardless of error condition. +/// +static int EnsureFileDirectoryExists(__inout_z wchar_t* szFilePath) +{ + int ret = 0; + wchar_t* szLastSlash = NULL; + for (wchar_t* sz = szFilePath; *sz; sz++) + { + if (*sz == L'\\') + { + szLastSlash = sz; + } + } + if (szLastSlash) + { + *szLastSlash = L'\0'; + ret = EnsureDirectoryExists(szFilePath); + *szLastSlash = L'\\'; + } + return ret; +} + +/// +/// FDI callback for handling files in the cabinet. +/// +/// Type of notification. +/// Structure containing data about the notification. +/// +/// Refer to fdi.h for more comments on this notification callback. +/// +static FNFDINOTIFY(CabNotification) +{ + // fdintCOPY_FILE: + // Called for each file that *starts* in the current cabinet, giving + // the client the opportunity to request that the file be copied or + // skipped. + // Entry: + // pfdin->psz1 = file name in cabinet + // pfdin->cb = uncompressed size of file + // pfdin->date = file date + // pfdin->time = file time + // pfdin->attribs = file attributes + // pfdin->iFolder = file's folder index + // Exit-Success: + // Return non-zero file handle for destination file; FDI writes + // data to this file use the PFNWRITE function supplied to FDICreate, + // and then calls fdintCLOSE_FILE_INFO to close the file and set + // the date, time, and attributes. + // Exit-Failure: + // Returns 0 => Skip file, do not copy + // Returns -1 => Abort FDICopy() call + if (fdint == fdintCOPY_FILE) + { + size_t cchFile = MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, NULL, 0); + size_t cchFilePath = wcslen(g_szExtractDir) + 1 + cchFile; + wchar_t* szFilePath = (wchar_t*) _alloca((cchFilePath + 1) * sizeof(wchar_t)); + if (szFilePath == NULL) return -1; + StringCchCopyW(szFilePath, cchFilePath + 1, g_szExtractDir); + StringCchCatW(szFilePath, cchFilePath + 1, L"\\"); + MultiByteToWideChar(CP_UTF8, 0, pfdin->psz1, -1, + szFilePath + cchFilePath - cchFile, (int) cchFile + 1); + int hf = -1; + if (EnsureFileDirectoryExists(szFilePath) == 0) + { + _wsopen__s(hf, szFilePath, + _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL, + _SH_DENYWR, _S_IREAD | _S_IWRITE); + } + return hf; + } + + // fdintCLOSE_FILE_INFO: + // Called after all of the data has been written to a target file. + // This function must close the file and set the file date, time, + // and attributes. + // Entry: + // pfdin->psz1 = file name in cabinet + // pfdin->hf = file handle + // pfdin->date = file date + // pfdin->time = file time + // pfdin->attribs = file attributes + // pfdin->iFolder = file's folder index + // pfdin->cb = Run After Extract (0 - don't run, 1 Run) + // Exit-Success: + // Returns TRUE + // Exit-Failure: + // Returns FALSE, or -1 to abort + else if (fdint == fdintCLOSE_FILE_INFO) + { + _close((int) pfdin->hf); + return TRUE; + } + return 0; +} + +/// +/// Extracts all contents of a cabinet file to a directory. +/// +/// Path to the cabinet file to be extracted. +/// The cabinet may actually start at some offset within the file, +/// as long as that offset is a multiple of 256. +/// Directory where files are to be extracted. +/// This directory must already exist, but should be empty. +/// 0 if the cabinet was extracted successfully, +/// or an error code if any error occurred. +/// +/// The extraction will not overwrite any files in the destination +/// directory; extraction will be interrupted and fail if any files +/// with the same name already exist. +/// +int ExtractCabinet(const wchar_t* szCabFile, const wchar_t* szExtractDir) +{ + ERF erf; + // Most of the FDI callbacks can be handled by existing CRT I/O functions. + // For our functionality we only need to handle the open and seek callbacks. + HFDI hfdi = FDICreate((PFNALLOC) malloc, (PFNFREE) free, CabOpen, + (PFNREAD) _read, (PFNWRITE) _write, (PFNCLOSE) _close, + CabSeek, cpu80386, &erf); + if (hfdi != NULL) + { + g_hfdi = hfdi; + g_szCabFile = szCabFile; + g_szExtractDir = szExtractDir; + char szEmpty[1] = {0}; + if (FDICopy(hfdi, szEmpty, szEmpty, 0, CabNotification, NULL, NULL)) + { + FDIDestroy(hfdi); + return 0; + } + FDIDestroy(hfdi); + } + + return erf.erfOper; +} -- cgit v1.2.3-55-g6feb