MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.IO;5using System.Runtime.InteropServices;6 7using Microsoft.Win32.SafeHandles;8 9namespace System.Management.Automation.Internal10{11 internal class CabinetExtractor : ICabinetExtractor12 {13 /// <summary>14 /// GC handle which prevents garbage collector from collecting this delegate.15 /// </summary>16 private CabinetNativeApi.FdiAllocDelegate _allocDelegate;17 private GCHandle _fdiAllocHandle;18 private CabinetNativeApi.FdiFreeDelegate _freeDelegate;19 private GCHandle _fdiFreeHandle;20 private CabinetNativeApi.FdiOpenDelegate _openDelegate;21 private GCHandle _fdiOpenHandle;22 private CabinetNativeApi.FdiReadDelegate _readDelegate;23 private GCHandle _fdiReadHandle;24 private CabinetNativeApi.FdiWriteDelegate _writeDelegate;25 private GCHandle _fdiWriteHandle;26 private CabinetNativeApi.FdiCloseDelegate _closeDelegate;27 private GCHandle _fdiCloseHandle;28 private CabinetNativeApi.FdiSeekDelegate _seekDelegate;29 private GCHandle _fdiSeekHandle;30 private CabinetNativeApi.FdiNotifyDelegate _notifyDelegate;31 private GCHandle _fdiNotifyHandle;32 33 internal CabinetNativeApi.FdiContextHandle fdiContext; // HFDI34 35 internal CabinetExtractor()36 {37 CabinetNativeApi.FdiERF err = new CabinetNativeApi.FdiERF();38 39 populateDelegates();40 41 // marshal the delegate to a unmanaged function pointer so that AppDomain reference is stored correctly.42 fdiContext = CabinetNativeApi.FDICreate(43 Marshal.GetFunctionPointerForDelegate(_allocDelegate),44 Marshal.GetFunctionPointerForDelegate(_freeDelegate),45 Marshal.GetFunctionPointerForDelegate(_openDelegate),46 Marshal.GetFunctionPointerForDelegate(_readDelegate),47 Marshal.GetFunctionPointerForDelegate(_writeDelegate),48 Marshal.GetFunctionPointerForDelegate(_closeDelegate),49 Marshal.GetFunctionPointerForDelegate(_seekDelegate),50 CabinetNativeApi.FdiCreateCpuType.Cpu80386,51 err);52 }53 54 #region IDisposable Methods55 56 /// <summary>57 /// Flag: Has Dispose already been called?58 /// </summary>59 private bool _disposed = false;60 61 /// <summary>62 /// Protected implementation of Dispose pattern.63 /// </summary>64 /// <param name="disposing"></param>65 protected override void Dispose(bool disposing)66 {67 if (_disposed)68 {69 return;70 }71 72 // Free managed objects within 'if (disposing)' if needed73 fdiContext?.Dispose();74 // Free unmanaged objects here75 this.CleanUpDelegates();76 77 _disposed = true;78 79 // Call base class implementation in case it has resources to release80 base.Dispose(disposing);81 }82 83 /// <summary>84 /// Finalizes an instance of the <see cref="CabinetExtractor"/> class.85 /// </summary>86 ~CabinetExtractor()87 {88 Dispose(false);89 }90 91 #endregion92 93 internal override bool Extract(string cabinetName, string srcPath, string destPath)94 {95 IntPtr nativeDestPath = Marshal.StringToHGlobalAnsi(destPath);96 97 bool result = CabinetNativeApi.FDICopy(98 fdiContext,99 cabinetName,100 srcPath,101 0, // Not used102 Marshal.GetFunctionPointerForDelegate(_notifyDelegate),103 IntPtr.Zero,104 nativeDestPath);105 106 Marshal.FreeHGlobal(nativeDestPath);107 108 return result;109 }110 111 /// <summary>112 /// Creates delegates for the FDI* operation functions.113 /// </summary>114 private void populateDelegates()115 {116 // if a delegate is re-located by a garbage collection, it will not affect117 // the underlaying managed callback, so Alloc is used to add a reference118 // to the delegate, allowing relocation of the delegate, but preventing119 // disposal. Using GCHandle without pinning reduces fragmentation potential120 // of the managed heap.121 _allocDelegate = new CabinetNativeApi.FdiAllocDelegate(CabinetNativeApi.FdiAlloc);122 _fdiAllocHandle = GCHandle.Alloc(_allocDelegate);123 124 _freeDelegate = new CabinetNativeApi.FdiFreeDelegate(CabinetNativeApi.FdiFree);125 _fdiFreeHandle = GCHandle.Alloc(_freeDelegate);126 127 _openDelegate = new CabinetNativeApi.FdiOpenDelegate(CabinetNativeApi.FdiOpen);128 _fdiOpenHandle = GCHandle.Alloc(_openDelegate);129 130 _readDelegate = new CabinetNativeApi.FdiReadDelegate(CabinetNativeApi.FdiRead);131 _fdiReadHandle = GCHandle.Alloc(_readDelegate);132 133 _writeDelegate = new CabinetNativeApi.FdiWriteDelegate(CabinetNativeApi.FdiWrite);134 _fdiWriteHandle = GCHandle.Alloc(_writeDelegate);135 136 _closeDelegate = new CabinetNativeApi.FdiCloseDelegate(CabinetNativeApi.FdiClose);137 _fdiCloseHandle = GCHandle.Alloc(_closeDelegate);138 139 _seekDelegate = new CabinetNativeApi.FdiSeekDelegate(CabinetNativeApi.FdiSeek);140 _fdiSeekHandle = GCHandle.Alloc(_seekDelegate);141 142 _notifyDelegate = new CabinetNativeApi.FdiNotifyDelegate(CabinetNativeApi.FdiNotify);143 _fdiNotifyHandle = GCHandle.Alloc(_notifyDelegate);144 }145 146 /// <summary>147 /// Frees all the delegate handles.148 /// </summary>149 private void CleanUpDelegates()150 {151 // Free GCHandles so that the memory they point to may be unpinned (garbage collected)152 if (_fdiAllocHandle.IsAllocated)153 {154 _fdiAllocHandle.Free();155 _fdiFreeHandle.Free();156 _fdiOpenHandle.Free();157 _fdiReadHandle.Free();158 _fdiWriteHandle.Free();159 _fdiCloseHandle.Free();160 _fdiSeekHandle.Free();161 _fdiNotifyHandle.Free();162 }163 }164 }165 166 // CabinetExtractor loader implementation167 internal class CabinetExtractorLoader : ICabinetExtractorLoader168 {169 private static CabinetExtractor s_extractorInstance;170 private static CabinetExtractorLoader s_instance;171 private static double s_created = 0;172 173 internal static CabinetExtractorLoader GetInstance()174 {175 if (System.Threading.Interlocked.CompareExchange(ref s_created, 1, 0) == 0)176 {177 s_instance = new CabinetExtractorLoader();178 s_extractorInstance = new CabinetExtractor();179 }180 181 return s_instance;182 }183 184 internal override ICabinetExtractor GetCabinetExtractor()185 {186 return s_extractorInstance;187 }188 }189 190 internal static class CabinetNativeApi191 {192 #region Delegates and function definitions193 194 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]195 internal delegate IntPtr FdiAllocDelegate(int size);196 197 internal static IntPtr FdiAlloc(int size)198 {199 try200 {201 return Marshal.AllocHGlobal(size);202 }203 catch (OutOfMemoryException)204 {205 return IntPtr.Zero;206 }207 }208 209 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]210 internal delegate void FdiFreeDelegate(IntPtr memblock);211 212 internal static void FdiFree(IntPtr memblock)213 {214 Marshal.FreeHGlobal(memblock);215 }216 217 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]218 internal delegate IntPtr FdiOpenDelegate(219 [MarshalAs(UnmanagedType.LPStr)] string filename,220 int oflag,221 int pmode);222 223 internal static IntPtr FdiOpen(string filename, int oflag, int pmode)224 {225 FileMode mode = CabinetNativeApi.ConvertOpflagToFileMode(oflag);226 227 FileAccess access = CabinetNativeApi.ConvertPermissionModeToFileAccess(pmode);228 FileShare share = CabinetNativeApi.ConvertPermissionModeToFileShare(pmode);229 230 // This method is used for opening the cab file as well as saving the extracted files.231 // When we are opening the cab file we only need read permissions.232 // We force read permissions so that non-elevated users can extract cab files.233 if (mode == FileMode.Open || mode == FileMode.OpenOrCreate)234 {235 access = FileAccess.Read;236 share = FileShare.Read;237 }238 239 try240 {241 FileStream stream = new FileStream(filename, mode, access, share);242 243 if (stream == null)244 {245 return new IntPtr(-1);246 }247 248 return GCHandle.ToIntPtr(GCHandle.Alloc(stream));249 }250 catch (IOException)251 {252 return new IntPtr(-1);253 }254 }255 256 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]257 internal delegate int FdiReadDelegate(258 IntPtr fp,259 [In, Out]260 [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2, ArraySubType = UnmanagedType.U1)]261 byte[] buffer,262 int count);263 264 internal static int FdiRead(IntPtr fp, byte[] buffer, int count)265 {266 GCHandle handle = GCHandle.FromIntPtr(fp);267 FileStream stream = (FileStream)handle.Target;268 269 int numCharactersRead = 0;270 try271 {272 numCharactersRead = stream.Read(buffer, 0, count);273 }274 catch (ArgumentNullException) { numCharactersRead = -1; }275 catch (ArgumentOutOfRangeException) { numCharactersRead = -1; }276 catch (NotSupportedException) { numCharactersRead = -1; }277 catch (IOException) { numCharactersRead = -1; }278 catch (ArgumentException) { numCharactersRead = -1; }279 catch (ObjectDisposedException) { numCharactersRead = -1; }280 281 return numCharactersRead;282 }283 284 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]285 internal delegate int FdiWriteDelegate(286 IntPtr fp,287 [In]288 [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2, ArraySubType = UnmanagedType.U1)]289 byte[] buffer,290 int count);291 292 internal static int FdiWrite(IntPtr fp, byte[] buffer, int count)293 {294 GCHandle handle = GCHandle.FromIntPtr(fp);295 FileStream stream = (FileStream)handle.Target;296 297 int numCharactersWritten = 0;298 try299 {300 stream.Write(buffer, 0, count);301 numCharactersWritten = count; // Write doesn't return the number of bytes written. Per MSDN, if it succeeds, it will have written count bytes.302 }303 catch (ArgumentNullException) { numCharactersWritten = -1; }304 catch (ArgumentOutOfRangeException) { numCharactersWritten = -1; }305 catch (NotSupportedException) { numCharactersWritten = -1; }306 catch (IOException) { numCharactersWritten = -1; }307 catch (ArgumentException) { numCharactersWritten = -1; }308 catch (ObjectDisposedException) { numCharactersWritten = -1; }309 310 return numCharactersWritten;311 }312 313 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]314 internal delegate int FdiCloseDelegate(IntPtr fp);315 316 internal static int FdiClose(IntPtr fp)317 {318 GCHandle handle = GCHandle.FromIntPtr(fp);319 FileStream stream = (FileStream)handle.Target;320 321 if (stream == null)322 {323 return -1;324 }325 else326 {327 stream.Dispose();328 return 0;329 }330 }331 332 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]333 internal delegate int FdiSeekDelegate(IntPtr fp, int offset, int origin);334 335 internal static int FdiSeek(IntPtr fp, int offset, int origin)336 {337 GCHandle handle = GCHandle.FromIntPtr(fp);338 FileStream stream = (FileStream)handle.Target;339 340 SeekOrigin seekOrigin = CabinetNativeApi.ConvertOriginToSeekOrigin(origin);341 long status = 0;342 try343 {344 status = stream.Seek(offset, seekOrigin);345 }346 catch (NotSupportedException) { status = -1; }347 catch (IOException) { status = -1; }348 catch (ArgumentException) { status = -1; }349 catch (ObjectDisposedException) { status = -1; }350 351 return (int)status;352 }353 354 [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]355 internal delegate IntPtr FdiNotifyDelegate(FdiNotificationType fdint, FdiNotification fdin);356 357 // Handles FDI notification358 internal static IntPtr FdiNotify(FdiNotificationType fdint, FdiNotification fdin)359 {360 switch (fdint)361 {362 case FdiNotificationType.FdintCOPY_FILE:363 {364 // TODO: Should I catch exceptions for the new functions?365 366 // Copy target directory367 string destPath = Marshal.PtrToStringAnsi(fdin.pv);368 369 // Split the path to a filename and path370 string fileName = Path.GetFileName(fdin.psz1);371 string remainingPsz1Path = Path.GetDirectoryName(fdin.psz1);372 destPath = Path.Combine(destPath, remainingPsz1Path);373 374 Directory.CreateDirectory(destPath); // Creates all intermediate directories if necessary.375 376 // Create the file377 string absoluteFilePath = Path.Combine(destPath, fileName);378 return CabinetNativeApi.FdiOpen(absoluteFilePath, (int)OpFlags.Create, (int)(PermissionMode.Read | PermissionMode.Write)); // TODO: OK to ignore _O_SEQUENTIAL, WrOnly, and _O_BINARY?379 }380 case FdiNotificationType.FdintCLOSE_FILE_INFO:381 {382 // Close the file383 CabinetNativeApi.FdiClose(fdin.hf);384 385 // Set the file attributes386 string destPath = Marshal.PtrToStringAnsi(fdin.pv);387 string absoluteFilePath = Path.Combine(destPath, fdin.psz1);388 389 IntPtr hFile = PlatformInvokes.CreateFile(390 absoluteFilePath,391 PlatformInvokes.FileDesiredAccess.GenericRead | PlatformInvokes.FileDesiredAccess.GenericWrite,392 PlatformInvokes.FileShareMode.Read,393 IntPtr.Zero,394 PlatformInvokes.FileCreationDisposition.OpenExisting,395 PlatformInvokes.FileAttributes.Normal,396 IntPtr.Zero);397 398 if (hFile != IntPtr.Zero)399 {400 PlatformInvokes.FILETIME ftFile = new PlatformInvokes.FILETIME();401 if (PlatformInvokes.DosDateTimeToFileTime(fdin.date, fdin.time, ftFile))402 {403 PlatformInvokes.FILETIME ftLocal = new PlatformInvokes.FILETIME();404 if (PlatformInvokes.LocalFileTimeToFileTime(ftFile, ftLocal))405 {406 PlatformInvokes.SetFileTime(hFile, ftLocal, null, ftLocal);407 }408 }409 410 PlatformInvokes.CloseHandle(hFile);411 }412 413 PlatformInvokes.SetFileAttributesW(414 absoluteFilePath,415 (PlatformInvokes.FileAttributes)fdin.attribs & (PlatformInvokes.FileAttributes.ReadOnly | PlatformInvokes.FileAttributes.Hidden | PlatformInvokes.FileAttributes.System | PlatformInvokes.FileAttributes.Archive));416 417 // Call notification function418 return new IntPtr(1);419 }420 }421 422 return new IntPtr(0);423 }424 425 #endregion426 427 #region Helper methods for non-trivial conversions428 429 /// <summary>430 /// Converts an unmanaged define into a known managed value.431 /// </summary>432 /// <param name="origin">Defined in stdio.h.</param>433 /// <returns>The appropriate System.IO.SeekOrigin value.</returns>434 internal static SeekOrigin ConvertOriginToSeekOrigin(int origin)435 {436 switch (origin)437 {438 case 0x0: // SEEK_SET439 return SeekOrigin.Begin;440 case 0x1: // SEEK_CUR441 return SeekOrigin.Current;442 case 0x2: // SEEK_END443 return SeekOrigin.End;444 default:445 return SeekOrigin.Current;446 }447 }448 449 /// <summary>450 /// Converts an unmanaged define into a known managed type.451 /// </summary>452 /// <param name="oflag">Operation mode defined in fcntl.h.</param>453 /// <returns>The appropriate System.IO.FileMode type.</returns>454 internal static FileMode ConvertOpflagToFileMode(int oflag)455 {456 // Note: This is not done in a switch because the order of tests matters.457 458 if ((oflag & (int)(OpFlags.Create | OpFlags.Excl)) == (int)(OpFlags.Create | OpFlags.Excl))459 {460 return FileMode.CreateNew;461 }462 else if ((oflag & (int)(OpFlags.Create | OpFlags.Truncate)) == (int)(OpFlags.Create | OpFlags.Truncate))463 {464 return FileMode.OpenOrCreate;465 }466 else if ((oflag & (int)OpFlags.Append) != 0)467 {468 return FileMode.Append;469 }470 else if ((oflag & (int)OpFlags.Create) != 0)471 {472 return FileMode.Create;473 }474 else if ((oflag & (int)OpFlags.RdWr) != 0)475 {476 return FileMode.Open;477 }478 else if ((oflag & (int)OpFlags.Truncate) != 0)479 {480 return FileMode.Truncate;481 }482 else483 {484 return FileMode.OpenOrCreate; // This seemed the safest way to handled unrecognized types485 }486 }487 488 /// <summary>489 /// Converts an unmanaged define into a known managed type.490 /// </summary>491 /// <param name="pmode">Permission mode defined in stat.h.</param>492 /// <returns>The appropriate System.IO.FileAccess type.</returns>493 internal static FileAccess ConvertPermissionModeToFileAccess(int pmode)494 {495 // Note: This is not done in a switch because the order of tests matters.496 497 if ((pmode & (int)(PermissionMode.Read | PermissionMode.Write)) == (int)(PermissionMode.Read | PermissionMode.Write))498 {499 return FileAccess.ReadWrite;500 }501 else if ((pmode & (int)PermissionMode.Read) != 0)502 {503 return FileAccess.Read;504 }505 else if ((pmode & (int)PermissionMode.Write) != 0)506 {507 return FileAccess.Write;508 }509 else510 {511 return FileAccess.Read;512 }513 }514 515 /// <summary>516 /// Converts an unmanaged define into a known managed type.517 /// </summary>518 /// <param name="pmode">Permission mode defined in stat.h.</param>519 /// <returns>The appropriate System.IO.FileShare type.</returns>520 internal static FileShare ConvertPermissionModeToFileShare(int pmode)521 {522 // Note: This is not done in a switch because the order of tests matters.523 524 if ((pmode & (int)(PermissionMode.Read | PermissionMode.Write)) == (int)(PermissionMode.Read | PermissionMode.Write))525 {526 return FileShare.ReadWrite;527 }528 else if ((pmode & (int)PermissionMode.Read) != 0)529 {530 return FileShare.Read;531 }532 else if ((pmode & (int)PermissionMode.Write) != 0)533 {534 return FileShare.Write;535 }536 else537 {538 return FileShare.Read;539 }540 }541 542 #endregion543 544 #region IO classes, structures, and enums545 546 [Flags]547 internal enum PermissionMode : int548 {549 None = 0x0000,550 Write = 0x0080,551 Read = 0x0100552 }553 554 [Flags]555 internal enum OpFlags : int556 {557 RdOnly = 0x0000,558 WrOnly = 0x0001,559 RdWr = 0x0002,560 Append = 0x0008,561 Create = 0x0100,562 Truncate = 0x0200,563 Excl = 0x0400564 }565 566 internal enum FdiCreateCpuType : int567 {568 CpuUnknown = -1,569 Cpu80286 = 0,570 Cpu80386 = 1571 }572 573 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]574 internal class FdiNotification575 {576 internal int cb; // LONG577 internal string psz1; // char FAR *578 internal string psz2; // char FAR *579 internal string psz3; // char FAR *580 internal IntPtr pv; // void FAR * // In this case, it is the destination path581 internal IntPtr hf; // INT_PTR582 internal short date; // USHORT583 internal short time; // USHORT584 internal short attribs; // USHORT585 internal short setID; // USHORT586 internal short iCabinet; // USHORT587 internal short iFolder; // USHORT588 internal int fdie; // FDIERROR589 }590 591 internal enum FdiNotificationType : int592 {593 FdintCABINET_INFO = 0x0,594 FdintPARTIAL_FILE = 0x1,595 FdintCOPY_FILE = 0x2,596 FdintCLOSE_FILE_INFO = 0x3,597 FdintNEXT_CABINET = 0x4,598 FdintENUMERATE = 0x5599 }600 601 [StructLayout(LayoutKind.Sequential)]602 internal class FdiERF603 {604 internal int erfOper;605 internal int erfType;606 internal bool fError;607 }608 609 internal sealed class FdiContextHandle : SafeHandleZeroOrMinusOneIsInvalid610 {611 private FdiContextHandle()612 : base(true)613 {614 }615 616 protected override bool ReleaseHandle()617 {618 return CabinetNativeApi.FDIDestroy(this.handle);619 }620 }621 622 #endregion623 624 #region PInvoke Definitions625 626 /// <summary>627 /// Creates an FDI context.628 /// </summary>629 /// <param name="pfnalloc">_In_ PFNALLOC - Memory allocation delegate.</param>630 /// <param name="pfnfree">_In_ PFNFREE - Memory free delegate.</param>631 /// <param name="pfnopen">_In_ PFNOPEN - File open delegate.</param>632 /// <param name="pfnread">_In_ PFNREAD - File read delegate.</param>633 /// <param name="pfnwrite">_In_ PFNWRITE - File write delegate.</param>634 /// <param name="pfnclose">_In_ PFNCLOSE - File close delegate.</param>635 /// <param name="pfnseek">_In_ PFNSEEK - File seek delegate.</param>636 /// <param name="cpuType">_In_ int - CPU type.</param>637 /// <param name="erf">_Inout_ PERF - Error structure containing error information.</param>638 /// <returns></returns>639 [DllImport("cabinet.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]640 internal static extern FdiContextHandle FDICreate(641 IntPtr pfnalloc,642 IntPtr pfnfree,643 IntPtr pfnopen,644 IntPtr pfnread,645 IntPtr pfnwrite,646 IntPtr pfnclose,647 IntPtr pfnseek,648 CabinetNativeApi.FdiCreateCpuType cpuType,649 FdiERF erf);650 651 /// <summary>652 /// Extracts files from cabinets.653 /// </summary>654 /// <param name="hfdi">_In_ HFDI - A valid FDI context handle returned by FDICreate.</param>655 /// <param name="pszCabinet">_In_ LPSTR - The name of the cabinet file.</param>656 /// <param name="pszCabPath">_In_ LPSTR - The path to the cabinet file excluding the file name.</param>657 /// <param name="flags">_In_ int - Not defined.</param>658 /// <param name="pfnfdin">_In_ PFNFDINOTIFY - Pointer to the notification callback delegate.</param>659 /// <param name="pfnfdid">_In_ PFNFDIDECRYPT - Not used.</param>660 /// <param name="pvUser">_In_opt_ void FAR * - Path string passed to the notification function.</param>661 /// <returns></returns>662 [DllImport("cabinet.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, SetLastError = true, BestFitMapping = false)]663 internal static extern bool FDICopy(664 FdiContextHandle hfdi,665 [MarshalAs(UnmanagedType.LPStr)] string pszCabinet,666 [MarshalAs(UnmanagedType.LPStr)] string pszCabPath,667 int flags,668 IntPtr pfnfdin,669 IntPtr pfnfdid,670 IntPtr pvUser);671 672 /// <summary>673 /// Deletes an open FDI context.674 /// </summary>675 /// <param name="hfdi">_In_ HFDI - The FDI context handle to destroy.</param>676 /// <returns></returns>677 [DllImport("cabinet.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]678 internal static extern bool FDIDestroy(679 IntPtr hfdi);680 681 #endregion682 }683}684 