pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


punchhole.ps1
Posted by Anonymous on Sat 8th Mar 2025 18:48
raw | new post

  1. # PowerShell function to punch a hole in a sparse file using DeviceIoControl() and FSCTL_SET_ZERO_DATA
  2. # Function name changed to Sparsefile-PunchHole as requested.
  3. # Function takes filename, hole offset, and hole length as arguments.
  4. # Uses sparsefile.dat in the current working directory (cwd) as test argument.
  5. # fsutil sparse setflag call REMOVED as requested by user
  6. # Debug output and enhanced CreateFile error checks REMOVED as requested
  7.  
  8. # Define necessary structures and constants for DeviceIoControl and FSCTL_SET_ZERO_DATA
  9. Add-Type -TypeDefinition @'
  10. using System;
  11. using System.Runtime.InteropServices;
  12.  
  13. public class SparseFileHole
  14. {
  15.     [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  16.     public static extern IntPtr CreateFile(
  17.         string filename,
  18.         [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
  19.         [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
  20.         IntPtr lpSecurityAttributes,
  21.         [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
  22.         [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
  23.         IntPtr hTemplateFile
  24.     );
  25.  
  26.     [DllImport("Kernel32.dll", SetLastError = true)]
  27.     [return: MarshalAs(UnmanagedType.Bool)]
  28.     public static extern bool DeviceIoControl(
  29.         IntPtr hDevice,
  30.         uint dwIoControlCode,
  31.         IntPtr lpInBuffer,
  32.         uint nInBufferSize,
  33.         IntPtr lpOutBuffer,
  34.         uint nOutBufferSize,
  35.         out uint lpBytesReturned,
  36.         IntPtr lpOverlapped
  37.     );
  38.  
  39.     [DllImport("Kernel32.dll", SetLastError = true)]
  40.     [return: MarshalAs(UnmanagedType.Bool)]
  41.     public static extern bool CloseHandle(IntPtr hObject);
  42.  
  43.     [DllImport("kernel32.dll", SetLastError = true)]
  44.     public static extern UInt32 GetLastError();
  45.  
  46.     public enum FileAccess : uint
  47.     {
  48.         GenericRead = 0x80000000,
  49.         GenericWrite = 0x40000000,
  50.         GenericExecute = 0x20000000,
  51.         GenericAll = 0x10000000
  52.     }
  53.  
  54.     public enum FileShare : uint
  55.     {
  56.         None = 0x00000000,
  57.         Read = 0x00000001,
  58.         Write = 0x00000002,
  59.         Delete = 0x00000004,
  60.         ReadWrite = Read | Write
  61.     }
  62.  
  63.     public enum FileMode : uint
  64.     {
  65.         New = 1,
  66.         CreateAlways = 2,
  67.         OpenExisting = 3,
  68.         OpenOrCreate = 4,
  69.         TruncateExisting = 5,
  70.         CreateNew = 6
  71.     }
  72.  
  73.     public enum FileAttributes : uint
  74.     {
  75.         Normal = 0x00000080,
  76.         SparseFile = 0x00000200,
  77.         FileFlagOverlapped = 0x40000000
  78.     }
  79.  
  80.     // **Corrected FSCTL_SET_ZERO_DATA control code: 0x000980C8**
  81.     public const uint FSCTL_SET_ZERO_DATA = 0x000980C8; // From winioctl.h (Corrected Value)
  82.     public const uint FILE_DEVICE_FILE_SYSTEM = 0x00000009;
  83.     public const uint METHOD_BUFFERED = 0x00000000;
  84.     public const uint FILE_ANY_ACCESS = 0x00000000;
  85.  
  86.     [StructLayout(LayoutKind.Sequential)]
  87.     public struct FILE_ZERO_DATA_INFORMATION
  88.     {
  89.         public long FileOffset; // LARGE_INTEGER - Start offset
  90.         public long BeyondFinalZero; // LARGE_INTEGER - End offset (offset of the first byte beyond the zeroed range)
  91.     }
  92. }
  93. '@
  94.  
  95. function Sparsefile-PunchHole {
  96.     [CmdletBinding()]
  97.     Param (
  98.         [Parameter(Mandatory=$true, Position=0)]
  99.         [string]
  100.         $Filename,
  101.  
  102.         [Parameter(Mandatory=$true, Position=1)]
  103.         [int]
  104.         $HoleOffset,
  105.  
  106.         [Parameter(Mandatory=$true, Position=2)]
  107.         [int]
  108.         $HoleLength
  109.     )
  110.  
  111.     Begin {
  112.         $FSCTL_SET_ZERO_DATA_CODE = [SparseFileHole]::FSCTL_SET_ZERO_DATA
  113.     }
  114.  
  115.     Process {
  116.         # 1. Define the file path from parameter
  117.         $FilePath = $Filename
  118.  
  119.         # 2. Open a handle to the file using CreateFile
  120.         $fileHandle = [SparseFileHole]::CreateFile(
  121.             $FilePath,
  122.             [SparseFileHole+FileAccess]::GenericWrite,
  123.             [SparseFileHole+FileShare]::None,
  124.             [IntPtr]::Zero,
  125.             [SparseFileHole+FileMode]::OpenExisting,
  126.             [SparseFileHole+FileAttributes]::FileFlagOverlapped,
  127.             [IntPtr]::Zero
  128.         )
  129.  
  130.         if ($fileHandle -eq [IntPtr]::Zero) {
  131.             # Get specific error code for CreateFile failure
  132.             $errorCodeCreateFile = [System.Runtime.InteropServices.Marshal]::GetLastError()
  133.             Write-Error "Failed to open file '$FilePath'. Error code: $($errorCodeCreateFile)"
  134.             return $false # Indicate failure
  135.         }
  136.  
  137.         # 3. Define the parameters for FILE_ZERO_DATA_INFORMATION
  138.         $startOffsetBytes = $HoleOffset
  139.         $holeLengthBytes = $HoleLength
  140.         $endOffsetBytes = $startOffsetBytes + $holeLengthBytes
  141.  
  142.         $zeroDataInfo = New-Object -TypeName SparseFileHole+FILE_ZERO_DATA_INFORMATION
  143.         $zeroDataInfo.FileOffset = $startOffsetBytes
  144.         $zeroDataInfo.BeyondFinalZero = $endOffsetBytes
  145.  
  146.         # Allocate memory and marshal the struct
  147.         $paramsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($zeroDataInfo))
  148.         [System.Runtime.InteropServices.Marshal]::StructureToPtr($zeroDataInfo, $paramsPtr, $false)
  149.  
  150.         # 4. Call DeviceIoControl with FSCTL_SET_ZERO_DATA
  151.         $bytesReturned = 0
  152.         $ioctlResult = [SparseFileHole]::DeviceIoControl(
  153.             $fileHandle,
  154.             $FSCTL_SET_ZERO_DATA_CODE,
  155.             $paramsPtr,
  156.             [System.Runtime.InteropServices.Marshal]::SizeOf($zeroDataInfo),
  157.             [IntPtr]::Zero,
  158.             0,
  159.             ([ref]$bytesReturned),
  160.             [IntPtr]::Zero
  161.         )
  162.  
  163.         # Get LastError after DeviceIoControl call
  164.         $lastErrorDeviceIoControl = [SparseFileHole]::GetLastError()
  165.  
  166.         # 5. Clean up allocated memory and close handle
  167.         [System.Runtime.InteropServices.Marshal]::FreeHGlobal($paramsPtr)
  168.         [SparseFileHole]::CloseHandle($fileHandle) | Out-Null
  169.  
  170.         if ($ioctlResult) {
  171.             Write-Verbose "Hole punched successfully in $($FilePath) from offset $($startOffsetBytes) with length $($holeLengthBytes)."
  172.             return $true # Indicate success
  173.         } else {
  174.             Write-Error "DeviceIoControl failed for file '$FilePath'. Error code: $($lastErrorDeviceIoControl)"
  175.             return $false # Indicate failure
  176.         }
  177.     }
  178. }
  179.  
  180. # Example Usage:
  181. $sparseFile = "sparsefile.dat" # Test argument: sparsefile.dat in the current working directory (cwd)
  182. $offset = 0
  183. $length = 65536
  184.  
  185. # **Important:** Make sure 'sparsefile.dat' exists in the current directory and is sparse
  186. if (-not (Test-Path -Path $sparseFile -PathType Leaf)) {
  187.     Write-Warning "Warning: '$sparseFile' not found in the current directory. Please create a sparse file named 'sparsefile.dat' in the current directory for testing."
  188. } else {
  189.     Write-Host "Attempting to punch a hole in file: $($sparseFile)"
  190.     $holeResult = Sparsefile-PunchHole -Filename $sparseFile -HoleOffset $offset -HoleLength $length
  191.  
  192.     if ($holeResult) {
  193.         Write-Host "Hole punching operation completed successfully."
  194.         Write-Host "You can verify the sparse file properties and hole using 'fsutil sparse queryrange $($sparseFile)' in an elevated command prompt."
  195.     } else {
  196.         Write-Host "Hole punching operation failed. See error messages above for details."
  197.     }
  198. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at