📄 jclntfs.pas
字号:
if Handle <> INVALID_HANDLE_VALUE then
try
Result := NtfsZeroDataByHandle(Handle, First, Last);
finally
CloseHandle(Handle);
end;
end;
function NtfsGetAllocRangeEntry(const Ranges: TNtfsAllocRanges;
Index: Integer): TFileAllocatedRangeBuffer;
var
Offset: Longint;
begin
Assert((Index >= 0) and (Index < Ranges.Entries));
Offset := Longint(Ranges.Data) + Index * SizeOf(TFileAllocatedRangeBuffer);
Result := PFileAllocatedRangeBuffer(Offset)^;
end;
function __QueryAllocRanges(const Handle: THandle; const Offset, Count: Int64;
var Ranges: PFileAllocatedRangeBuffer; var MoreData: Boolean; var Size: Cardinal): Boolean;
var
BytesReturned: DWORD;
SearchRange: TFileAllocatedRangeBuffer;
BufferSize: Cardinal;
begin
SearchRange.FileOffset.QuadPart := Offset;
SearchRange.Length.QuadPart := Count;
BufferSize := 4 * 64 * SizeOf(TFileAllocatedRangeBuffer);
Ranges := AllocMem(BufferSize);
Result := DeviceIoControl(Handle, FSCTL_QUERY_ALLOCATED_RANGES, @SearchRange,
SizeOf(SearchRange), Ranges, BufferSize, BytesReturned, nil);
MoreData := GetLastError = ERROR_MORE_DATA;
if MoreData then
Result := True;
Size := BytesReturned;
if BytesReturned = 0 then
begin
FreeMem(Ranges);
Ranges := nil;
end;
end;
function NtfsQueryAllocRanges(const FileName: string; Offset, Count: Int64;
var Ranges: TNtfsAllocRanges): Boolean;
var
Handle: THandle;
CurrRanges: PFileAllocatedRangeBuffer;
R, MoreData: Boolean;
Size: Cardinal;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
R := __QueryAllocRanges(Handle, Offset, Count, CurrRanges, MoreData, Size);
Ranges.MoreData := MoreData;
Result := R;
if R then
begin
Ranges.Entries := Size div SizeOf(TFileAllocatedRangeBuffer);
Ranges.Data := CurrRanges;
end
else
begin
Ranges.Entries := 0;
Ranges.Data := nil;
end;
finally
CloseHandle(Handle);
end;
end;
function NtfsSparseStreamsSupported(const Volume: string): Boolean;
begin
Result := fsSupportsSparseFiles in GetVolumeFileSystemFlags(Volume);
end;
function NtfsGetSparse(const FileName: string): Boolean;
var
Handle: THandle;
Info: TByHandleFileInformation;
begin
Result := False;
Handle := CreateFile(PChar(FileName), 0, FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
GetFileInformationByHandle(Handle, Info);
Result := (Info.dwFileAttributes and FILE_ATTRIBUTE_SPARSE_FILE) <> 0;
finally
CloseHandle(Handle);
end;
end;
//=== NTFS - Reparse Points ==================================================
function NtfsGetReparseTag(const Path: string; var Tag: DWORD): Boolean;
var
SearchRec: TSearchRec;
begin
Result := NtfsFileHasReparsePoint(Path);
if Result then
begin
Result := FindFirst(Path, faAnyFile, SearchRec) = 0;
if Result then
begin
// Check if file has a reparse point
Result := ((SearchRec.Attr and FILE_ATTRIBUTE_REPARSE_POINT) <> 0);
// If so the dwReserved0 field contains the reparse tag
if Result then
Tag := SearchRec.FindData.dwReserved0;
FindClose(SearchRec);
end;
end;
end;
function NtfsReparsePointsSupported(const Volume: string): Boolean;
begin
Result := fsSupportsReparsePoints in GetVolumeFileSystemFlags(Volume);
end;
function NtfsFileHasReparsePoint(const Path: string): Boolean;
var
Attr: DWORD;
begin
Result := False;
Attr := GetFileAttributes(PChar(Path));
if Attr <> DWORD(-1) then
Result := (Attr and FILE_ATTRIBUTE_REPARSE_POINT) <> 0;
end;
function NtfsDeleteReparsePoint(const FileName: string; ReparseTag: DWORD): Boolean;
var
Handle: THandle;
BytesReturned: DWORD;
ReparseData: TReparseGuidDataBuffer;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
FillChar(ReparseData, SizeOf(ReparseData), #0);
ReparseData.ReparseTag := ReparseTag;
Result := DeviceIoControl(Handle, FSCTL_DELETE_REPARSE_POINT, @ReparseData,
REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, nil, 0, BytesReturned, nil);
finally
CloseHandle(Handle);
end;
end;
function NtfsSetReparsePoint(const FileName: string; var ReparseData; Size: Longword): Boolean;
var
Handle: THandle;
BytesReturned: DWORD;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
Result := DeviceIoControl(Handle, FSCTL_SET_REPARSE_POINT, @ReparseData,
Size, nil, 0, BytesReturned, nil);
finally
CloseHandle(Handle);
end;
end;
function NtfsGetReparsePoint(const FileName: string; var ReparseData: TReparseGuidDataBuffer): Boolean;
var
Handle: THandle;
BytesReturned: DWORD;
LastError: DWORD;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT, 0);
LastError := GetLastError;
if Handle <> INVALID_HANDLE_VALUE then
try
Result := DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT, nil, 0, @ReparseData,
ReparseData.ReparseDataLength + SizeOf(ReparseData), BytesReturned, nil);
if not Result then
begin
ReparseData.ReparseDataLength := BytesReturned;
LastError := GetLastError;
end;
finally
CloseHandle(Handle);
SetLastError(LastError);
end;
end;
//=== NTFS - Volume Mount Points =============================================
function NtfsIsFolderMountPoint(const Path: string): Boolean;
var
Tag: DWORD;
begin
Result := NtfsGetReparseTag(Path, Tag);
if Result then
Result := (Tag = IO_REPARSE_TAG_MOUNT_POINT);
end;
function NtfsMountDeviceAsDrive(const Device: string; Drive: Char): Boolean;
const
DDD_FLAGS = DDD_RAW_TARGET_PATH or DDD_REMOVE_DEFINITION or DDD_EXACT_MATCH_ON_REMOVE;
var
DriveStr: string;
VolumeName: string;
begin
// To create a mount point we must obtain a unique volume name first. To obtain
// a unique volume name the drive must exist. Therefore we must temporarily
// create a symbolic link for the drive using DefineDosDevice.
DriveStr := Drive + ':';
Result := DefineDosDevice(DDD_RAW_TARGET_PATH, PChar(DriveStr), PChar(Device));
if Result then
begin
SetLength(VolumeName, 1024);
Result := RtdlGetVolumeNameForVolumeMountPoint(PChar(DriveStr + '\'),
PChar(VolumeName), 1024);
// Attempt to delete the symbolic link, if it fails then don't attempt to
// set the mountpoint either but raise an exception instead, there's something
// seriously wrong so let's try to control the damage done already :)
if not DefineDosDevice(DDD_FLAGS, PChar(DriveStr), PChar(Device)) then
raise EJclNtfsError.CreateRes(@RsNtfsUnableToDeleteSymbolicLink);
if Result then
Result := RtdlSetVolumeMountPoint(PChar(DriveStr + '\'), PChar(VolumeName));
end;
end;
function NtfsMountVolume(const Volume: Char; const MountPoint: string): Boolean;
var
VolumeName: string;
VolumeStr: string;
begin
SetLength(VolumeName, 1024);
VolumeStr := Volume + ':\';
Result := RtdlGetVolumeNameForVolumeMountPoint(PChar(VolumeStr), PChar(VolumeName), 1024);
if Result then
begin
if not JclFileUtils.DirectoryExists(MountPoint) then
JclFileUtils.ForceDirectories(MountPoint);
Result := RtdlSetVolumeMountPoint(PChar(MountPoint), PChar(VolumeName));
end;
end;
//=== NTFS - Change Journal ==================================================
//=== NTFS - Opportunistic Locks =============================================
function NtfsOpLockAckClosePending(Handle: THandle; Overlapped: TOverlapped): Boolean;
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(Handle, FSCTL_OPBATCH_ACK_CLOSE_PENDING, nil, 0, nil,
0, BytesReturned, @Overlapped);
end;
function NtfsOpLockBreakAckNo2(Handle: THandle; Overlapped: TOverlapped): Boolean;
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(Handle, FSCTL_OPLOCK_BREAK_ACK_NO_2, nil, 0, nil, 0,
BytesReturned, @Overlapped);
end;
function NtfsOpLockBreakAcknowledge(Handle: THandle; Overlapped: TOverlapped): Boolean;
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(Handle, FSCTL_OPLOCK_BREAK_ACKNOWLEDGE, nil, 0, nil,
0, BytesReturned, @Overlapped);
Result := Result or (GetLastError = ERROR_IO_PENDING);
end;
function NtfsOpLockBreakNotify(Handle: THandle; Overlapped: TOverlapped): Boolean;
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(Handle, FSCTL_OPLOCK_BREAK_NOTIFY, nil, 0, nil, 0,
BytesReturned, @Overlapped);
end;
function NtfsRequestOpLock(Handle: THandle; Kind: TOpLock; Overlapped: TOverlapped): Boolean;
const
IoCodes: array [TOpLock] of Cardinal = (
FSCTL_REQUEST_OPLOCK_LEVEL_1, FSCTL_REQUEST_OPLOCK_LEVEL_2,
FSCTL_REQUEST_BATCH_OPLOCK, FSCTL_REQUEST_FILTER_OPLOCK);
var
BytesReturned: Cardinal;
begin
Result := DeviceIoControl(Handle, IoCodes[Kind], nil, 0, nil, 0, BytesReturned, @Overlapped);
Result := Result or (GetLastError = ERROR_IO_PENDING);
end;
//=== Junction Points ========================================================
type
TReparseDataBufferOverlay = record
case Boolean of
False:
(Reparse: TReparseDataBuffer;);
True:
(Buffer: array [0..MAXIMUM_REPARSE_DATA_BUFFER_SIZE] of Char;);
end;
function IsReparseTagValid(Tag: DWORD): Boolean;
begin
Result := (Tag and (not IO_REPARSE_TAG_VALID_VALUES) = 0) and
(Tag > IO_REPARSE_TAG_RESERVED_RANGE);
end;
function NtfsCreateJunctionPoint(const Source, Destination: string): Boolean;
var
Dest: array [0..1024] of Char; // Writable copy of Destination
DestW: WideString; // Unicode version of Dest
FullDir: array [0..1024] of Char;
FilePart: PChar;
ReparseData: TReparseDataBufferOverlay;
NameLength: Longword;
begin
Result := False;
// For some reason the destination string must be prefixed with \??\ otherwise
// the IOCTL will fail, ensure it's there.
if Copy(Destination, 1, 3) = '\??' then
StrPCopy(Dest, Destination)
else
begin
// Make sure Destination is a directory or again, the IOCTL will fail.
if (GetFullPathName(PChar(Destination), 1024, FullDir, FilePart) = 0) or
(GetFileAttributes(FullDir) = DWORD(-1)) then
begin
SetLastError(ERROR_PATH_NOT_FOUND);
Exit;
end;
StrPCopy(Dest, '\??\' + Destination);
end;
FillChar(ReparseData, SizeOf(ReparseData), #0);
NameLength := StrLen(Dest) * SizeOf(WideChar);
ReparseData.Reparse.ReparseTag := IO_REPARSE_TAG_MOUNT_POINT;
ReparseData.Reparse.ReparseDataLength := NameLength + 12;
ReparseData.Reparse.SubstituteNameLength := NameLength;
ReparseData.Reparse.PrintNameOffset := NameLength + 2;
// Not the most elegant way to copy an AnsiString into an Unicode buffer but
// let's avoid dependencies on JclUnicode.pas (adds significant resources).
DestW := WideString(Dest);
Move(DestW[1], ReparseData.Reparse.PathBuffer, Length(DestW) * SizeOf(WideChar));
Result := NtfsSetReparsePoint(Source, ReparseData.Reparse,
ReparseData.Reparse.ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE);
end;
function NtfsDeleteJunctionPoint(const Source: string): Boolean;
begin
Result := NtfsDeleteReparsePoint(Source, IO_REPARSE_TAG_MOUNT_POINT);
end;
function NtfsGetJunctionPointDestination(const Source: string; var Destination: string): Boolean;
var
Handle: THandle;
ReparseData: TReparseDataBufferOverlay;
BytesReturned: DWORD;
begin
Result := False;
if NtfsFileHasReparsePoint(Source) then
begin
Handle := CreateFile(PChar(Source), GENERIC_READ, 0, nil,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS or FILE_FLAG_OPEN_REPARSE_POINT, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
if DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT, nil, 0, @ReparseData,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE, BytesReturned, nil) {and
IsReparseTagValid(ReparseData.Reparse.ReparseTag) then}
then
begin
if BytesReturned >= ReparseData.Reparse.SubstituteNameLength + SizeOf(WideChar) then
begin
SetLength(Destination, (ReparseData.Reparse.SubstituteNameLength div SizeOf(WideChar)) + 1);
WideCharToMultiByte(CP_THREAD_ACP, 0, ReparseData.Reparse.PathBuffer,
(ReparseData.Reparse.SubstituteNameLength div SizeOf(WCHAR)) + 1,
PChar(Destination), Length(Destination), nil, nil);
Result := True;
end;
end;
finally
CloseHandle(Handle);
end
end;
end;
//=== Streams ================================================================
// FindStream is an internal helper routine for NtfsFindFirstStream and
// NtfsFindNextStream. It uses the backup API to enumerate the streams in an
// NTFS file and returns when it either finds a stream that matches the filter
// specified in the Data parameter or hits EOF. Details are returned through
// the Data parameter and success/failure as the Boolean result value.
function FindStream(var Data: TFindStreamData): Boolean;
var
Header: TWin32StreamId;
BytesToRead, BytesRead: DWORD;
BytesToSeek: TULargeInteger;
Hi, Lo: DWORD;
FoundStream: Boolean;
StreamName: PWideChar;
begin
Result := False;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -