- # PowerShell function to punch a hole in a sparse file using DeviceIoControl() and FSCTL_SET_ZERO_DATA
 - # Function name changed to Sparsefile-PunchHole as requested.
 - # Function takes filename, hole offset, and hole length as arguments.
 - # Uses sparsefile.dat in the current working directory (cwd) as test argument.
 - # fsutil sparse setflag call REMOVED as requested by user
 - # Debug output and enhanced CreateFile error checks REMOVED as requested
 - # Define necessary structures and constants for DeviceIoControl and FSCTL_SET_ZERO_DATA
 - Add-Type -TypeDefinition @'
 - using System;
 - using System.Runtime.InteropServices;
 - public class SparseFileHole
 - {
 - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
 - public static extern IntPtr CreateFile(
 - string filename,
 - [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
 - [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
 - IntPtr lpSecurityAttributes,
 - [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
 - [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
 - IntPtr hTemplateFile
 - );
 - [DllImport("Kernel32.dll", SetLastError = true)]
 - [return: MarshalAs(UnmanagedType.Bool)]
 - public static extern bool DeviceIoControl(
 - IntPtr hDevice,
 - uint dwIoControlCode,
 - IntPtr lpInBuffer,
 - uint nInBufferSize,
 - IntPtr lpOutBuffer,
 - uint nOutBufferSize,
 - out uint lpBytesReturned,
 - IntPtr lpOverlapped
 - );
 - [DllImport("Kernel32.dll", SetLastError = true)]
 - [return: MarshalAs(UnmanagedType.Bool)]
 - public static extern bool CloseHandle(IntPtr hObject);
 - [DllImport("kernel32.dll", SetLastError = true)]
 - public static extern UInt32 GetLastError();
 - public enum FileAccess : uint
 - {
 - GenericRead = 0x80000000,
 - GenericWrite = 0x40000000,
 - GenericExecute = 0x20000000,
 - GenericAll = 0x10000000
 - }
 - public enum FileShare : uint
 - {
 - None = 0x00000000,
 - Read = 0x00000001,
 - Write = 0x00000002,
 - Delete = 0x00000004,
 - ReadWrite = Read | Write
 - }
 - public enum FileMode : uint
 - {
 - New = 1,
 - CreateAlways = 2,
 - OpenExisting = 3,
 - OpenOrCreate = 4,
 - TruncateExisting = 5,
 - CreateNew = 6
 - }
 - public enum FileAttributes : uint
 - {
 - Normal = 0x00000080,
 - SparseFile = 0x00000200,
 - FileFlagOverlapped = 0x40000000
 - }
 - // **Corrected FSCTL_SET_ZERO_DATA control code: 0x000980C8**
 - public const uint FSCTL_SET_ZERO_DATA = 0x000980C8; // From winioctl.h (Corrected Value)
 - public const uint FILE_DEVICE_FILE_SYSTEM = 0x00000009;
 - public const uint METHOD_BUFFERED = 0x00000000;
 - public const uint FILE_ANY_ACCESS = 0x00000000;
 - [StructLayout(LayoutKind.Sequential)]
 - public struct FILE_ZERO_DATA_INFORMATION
 - {
 - public long FileOffset; // LARGE_INTEGER - Start offset
 - public long BeyondFinalZero; // LARGE_INTEGER - End offset (offset of the first byte beyond the zeroed range)
 - }
 - }
 - '@
 - function Sparsefile-PunchHole {
 - [CmdletBinding()]
 - Param (
 - [Parameter(Mandatory=$true, Position=0)]
 - [string]
 - $Filename,
 - [Parameter(Mandatory=$true, Position=1)]
 - [int]
 - $HoleOffset,
 - [Parameter(Mandatory=$true, Position=2)]
 - [int]
 - $HoleLength
 - )
 - Begin {
 - $FSCTL_SET_ZERO_DATA_CODE = [SparseFileHole]::FSCTL_SET_ZERO_DATA
 - }
 - Process {
 - # 1. Define the file path from parameter
 - $FilePath = $Filename
 - # 2. Open a handle to the file using CreateFile
 - $fileHandle = [SparseFileHole]::CreateFile(
 - $FilePath,
 - [SparseFileHole+FileAccess]::GenericWrite,
 - [SparseFileHole+FileShare]::None,
 - [IntPtr]::Zero,
 - [SparseFileHole+FileMode]::OpenExisting,
 - [SparseFileHole+FileAttributes]::FileFlagOverlapped,
 - [IntPtr]::Zero
 - )
 - if ($fileHandle -eq [IntPtr]::Zero) {
 - # Get specific error code for CreateFile failure
 - $errorCodeCreateFile = [System.Runtime.InteropServices.Marshal]::GetLastError()
 - Write-Error "Failed to open file '$FilePath'. Error code: $($errorCodeCreateFile)"
 - return $false # Indicate failure
 - }
 - # 3. Define the parameters for FILE_ZERO_DATA_INFORMATION
 - $startOffsetBytes = $HoleOffset
 - $holeLengthBytes = $HoleLength
 - $endOffsetBytes = $startOffsetBytes + $holeLengthBytes
 - $zeroDataInfo = New-Object -TypeName SparseFileHole+FILE_ZERO_DATA_INFORMATION
 - $zeroDataInfo.FileOffset = $startOffsetBytes
 - $zeroDataInfo.BeyondFinalZero = $endOffsetBytes
 - # Allocate memory and marshal the struct
 - $paramsPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($zeroDataInfo))
 - [System.Runtime.InteropServices.Marshal]::StructureToPtr($zeroDataInfo, $paramsPtr, $false)
 - # 4. Call DeviceIoControl with FSCTL_SET_ZERO_DATA
 - $bytesReturned = 0
 - $ioctlResult = [SparseFileHole]::DeviceIoControl(
 - $fileHandle,
 - $FSCTL_SET_ZERO_DATA_CODE,
 - $paramsPtr,
 - [System.Runtime.InteropServices.Marshal]::SizeOf($zeroDataInfo),
 - [IntPtr]::Zero,
 - 0,
 - ([ref]$bytesReturned),
 - [IntPtr]::Zero
 - )
 - # Get LastError after DeviceIoControl call
 - $lastErrorDeviceIoControl = [SparseFileHole]::GetLastError()
 - # 5. Clean up allocated memory and close handle
 - [System.Runtime.InteropServices.Marshal]::FreeHGlobal($paramsPtr)
 - [SparseFileHole]::CloseHandle($fileHandle) | Out-Null
 - if ($ioctlResult) {
 - Write-Verbose "Hole punched successfully in $($FilePath) from offset $($startOffsetBytes) with length $($holeLengthBytes)."
 - return $true # Indicate success
 - } else {
 - Write-Error "DeviceIoControl failed for file '$FilePath'. Error code: $($lastErrorDeviceIoControl)"
 - return $false # Indicate failure
 - }
 - }
 - }
 - # Example Usage:
 - $sparseFile = "sparsefile.dat" # Test argument: sparsefile.dat in the current working directory (cwd)
 - $offset = 0
 - $length = 65536
 - # **Important:** Make sure 'sparsefile.dat' exists in the current directory and is sparse
 - if (-not (Test-Path -Path $sparseFile -PathType Leaf)) {
 - Write-Warning "Warning: '$sparseFile' not found in the current directory. Please create a sparse file named 'sparsefile.dat' in the current directory for testing."
 - } else {
 - Write-Host "Attempting to punch a hole in file: $($sparseFile)"
 - $holeResult = Sparsefile-PunchHole -Filename $sparseFile -HoleOffset $offset -HoleLength $length
 - if ($holeResult) {
 - Write-Host "Hole punching operation completed successfully."
 - Write-Host "You can verify the sparse file properties and hole using 'fsutil sparse queryrange $($sparseFile)' in an elevated command prompt."
 - } else {
 - Write-Host "Hole punching operation failed. See error messages above for details."
 - }
 - }
 
punchhole.ps1
Posted by Anonymous on Sat 8th Mar 2025 18:48
raw | new post
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.
 nrubsig.kpaste.net RSS