- # 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.