📄 jclntfs.pas
字号:
{**************************************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the }
{ License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF }
{ ANY KIND, either express or implied. See the License for the specific language governing rights }
{ and limitations under the License. }
{ }
{ The Original Code is JclNTFS.pas. }
{ }
{ The Initial Developer of the Original Code is Marcel van Brakel. Portions created by Marcel van }
{ Brakel are Copyright (C) Marcel van Brakel. All Rights Reserved. }
{ }
{ Contributor(s): }
{ Marcel van Brakel }
{ Robert Marquardt (marquardt) }
{ Robert Rossmair (rrossmair) }
{ Petr Vones (pvones) }
{ Oliver Schneider (assarbad) }
{ }
{**************************************************************************************************}
{ }
{ Contains routines to perform filesystem related tasks available only with NTFS. These are mostly }
{ relatively straightforward wrappers for various IOCTs related to compression, sparse files, }
{ reparse points, volume mount points and so forth. Note that some functions require NTFS 5 or }
{ higher! }
{ }
{**************************************************************************************************}
// Last modified: $Date: 2005/03/08 08:33:22 $
// For history see end of file
// Comments on Win9x compatibility of the functions used in this unit
// These stubs exist on Windows 95B already but all of them
// return ERROR_CALL_NOT_IMPLEMENTED:
// BackupSeek, BackupRead, BackupWrite
unit JclNTFS;
{$I jcl.inc}
{$I windowsonly.inc}
interface
uses
Windows, Classes,
JclBase, JclWin32;
// NTFS Exception
type
EJclNtfsError = class(EJclWin32Error);
// NTFS - Compression
type
TFileCompressionState = (fcNoCompression, fcDefaultCompression, fcLZNT1Compression);
function NtfsGetCompression(const FileName: string; var State: Short): Boolean; overload;
function NtfsGetCompression(const FileName: string): TFileCompressionState; overload;
function NtfsSetCompression(const FileName: string; const State: Short): Boolean;
procedure NtfsSetFileCompression(const FileName: string; const State: TFileCompressionState);
procedure NtfsSetDirectoryTreeCompression(const Directory: string; const State: TFileCompressionState);
procedure NtfsSetDefaultFileCompression(const Directory: string; const State: TFileCompressionState);
procedure NtfsSetPathCompression(const Path: string; const State: TFileCompressionState; Recursive: Boolean);
// NTFS - Sparse Files
type
TNtfsAllocRanges = record
Entries: Integer;
Data: PFileAllocatedRangeBuffer;
MoreData: Boolean;
end;
function NtfsSetSparse(const FileName: string): Boolean;
function NtfsZeroDataByHandle(const Handle: THandle; const First, Last: Int64): Boolean;
function NtfsZeroDataByName(const FileName: string; const First, Last: Int64): Boolean;
function NtfsQueryAllocRanges(const FileName: string; Offset, Count: Int64; var Ranges: TNtfsAllocRanges): Boolean;
function NtfsGetAllocRangeEntry(const Ranges: TNtfsAllocRanges; Index: Integer): TFileAllocatedRangeBuffer;
function NtfsSparseStreamsSupported(const Volume: string): Boolean;
function NtfsGetSparse(const FileName: string): Boolean;
// NTFS - Reparse Points
function NtfsDeleteReparsePoint(const FileName: string; ReparseTag: DWORD): Boolean;
function NtfsSetReparsePoint(const FileName: string; var ReparseData; Size: Longword): Boolean;
function NtfsGetReparsePoint(const FileName: string; var ReparseData: TReparseGuidDataBuffer): Boolean;
function NtfsGetReparseTag(const Path: string; var Tag: DWORD): Boolean;
function NtfsReparsePointsSupported(const Volume: string): Boolean;
function NtfsFileHasReparsePoint(const Path: string): Boolean;
// NTFS - Volume Mount Points
function NtfsIsFolderMountPoint(const Path: string): Boolean;
function NtfsMountDeviceAsDrive(const Device: string; Drive: Char): Boolean;
function NtfsMountVolume(const Volume: Char; const MountPoint: string): Boolean;
// NTFS - Change Journal
// NTFS - Opportunistic Locks
type
TOpLock = (olExclusive, olReadOnly, olBatch, olFilter);
function NtfsOpLockAckClosePending(Handle: THandle; Overlapped: TOverlapped): Boolean;
function NtfsOpLockBreakAckNo2(Handle: THandle; Overlapped: TOverlapped): Boolean;
function NtfsOpLockBreakAcknowledge(Handle: THandle; Overlapped: TOverlapped): Boolean;
function NtfsOpLockBreakNotify(Handle: THandle; Overlapped: TOverlapped): Boolean;
function NtfsRequestOpLock(Handle: THandle; Kind: TOpLock; Overlapped: TOverlapped): Boolean;
// Junction Points
function NtfsCreateJunctionPoint(const Source, Destination: string): Boolean;
function NtfsDeleteJunctionPoint(const Source: string): Boolean;
function NtfsGetJunctionPointDestination(const Source: string; var Destination: string): Boolean;
// Streams
type
TStreamId = (siInvalid, siStandard, siExtendedAttribute, siSecurity, siAlternate,
siHardLink, siProperty, siObjectIdentifier, siReparsePoints, siSparseFile);
TStreamIds = set of TStreamId;
TInternalFindStreamData = record
FileHandle: THandle;
Context: Pointer;
StreamIds: TStreamIds;
end;
TFindStreamData = record
Internal: TInternalFindStreamData;
Attributes: DWORD;
StreamID: TStreamId;
Name: WideString;
Size: Int64;
end;
function NtfsFindFirstStream(const FileName: string; StreamIds: TStreamIds; var Data: TFindStreamData): Boolean;
function NtfsFindNextStream(var Data: TFindStreamData): Boolean;
function NtfsFindStreamClose(var Data: TFindStreamData): Boolean;
// Hard links
function NtfsCreateHardLink(const LinkFileName, ExistingFileName: String): Boolean;
// ANSI-specific version
function NtfsCreateHardLinkA(const LinkFileName, ExistingFileName: AnsiString): Boolean;
// UNICODE-specific version
function NtfsCreateHardLinkW(const LinkFileName, ExistingFileName: WideString): Boolean;
type
TNtfsHardLinkInfo = record
LinkCount: Cardinal;
case Integer of
0: (
FileIndexHigh: Cardinal;
FileIndexLow: Cardinal);
1: (
FileIndex: Int64);
end;
function NtfsGetHardLinkInfo(const FileName: string; var Info: TNtfsHardLinkInfo): Boolean;
function NtfsFindHardLinks(const Path: string; const FileIndexHigh, FileIndexLow: Cardinal; const List: TStrings): Boolean;
function NtfsDeleteHardLinks(const FileName: string): Boolean;
implementation
uses
{$IFDEF FPC}
WinSysUt,
{$ENDIF FPC}
SysUtils, Hardlinks,
JclFileUtils, JclSysInfo, JclResources, JclSecurity;
//=== NTFS - Compression =====================================================
// Helper consts, helper types, helper routines
const
CompressionFormat: array [TFileCompressionState] of Short =
(
COMPRESSION_FORMAT_NONE,
COMPRESSION_FORMAT_DEFAULT,
COMPRESSION_FORMAT_LZNT1
);
// use IsDirectory(FileName) as array index
FileFlag: array [Boolean] of DWORD = (0, FILE_FLAG_BACKUP_SEMANTICS);
type
TStackFrame = packed record
CallersEBP: DWord;
CallerAddress: DWord;
end;
EJclInvalidArgument = class(EJclError);
{$STACKFRAMES OFF}
function CallersCallerAddress: Pointer;
asm
MOV EAX, [EBP]
MOV EAX, TStackFrame([EAX]).CallerAddress
end;
{$STACKFRAMES ON}
procedure ValidateArgument(Condition: Boolean; const Routine: string;
const Argument: string);
begin
if not Condition then
raise EJclInvalidArgument.CreateResFmt(@RsInvalidArgument, [Routine, Argument])
at CallersCallerAddress;
end;
{$IFNDEF STACKFRAMES_ON}
{$STACKFRAMES OFF}
{$ENDIF ~STACKFRAMES_ON}
function SetCompression(const FileName: string; const State: Short; FileFlag: DWORD): Boolean;
var
Handle: THandle;
BytesReturned: DWORD;
Buffer: Short;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ, nil, OPEN_EXISTING, FileFlag, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
Buffer := State;
Result := DeviceIoControl(Handle, FSCTL_SET_COMPRESSION, @Buffer,
SizeOf(Short), nil, 0, BytesReturned, nil);
finally
CloseHandle(Handle);
end
end;
function SetPathCompression(Dir: string; const Mask: string; const State: Short;
const SetDefault, Recursive: Boolean): Boolean;
var
FileName: string;
SearchRec: TSearchRec;
R: Integer;
begin
if SetDefault then
Result := SetCompression(Dir, State, FILE_FLAG_BACKUP_SEMANTICS)
else
Result := True;
if Result then
begin
Dir := PathAddSeparator(Dir);
if FindFirst(Dir + Mask, faAnyFile, SearchRec) = 0 then
try
repeat
if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
begin
FileName := Dir + SearchRec.Name;
if (SearchRec.Attr and faDirectory) = 0 then
Result := SetCompression(FileName, State, 0)
else
if Recursive then
Result := SetPathCompression(FileName, Mask, State, SetDefault, True);
if not Result then
Exit;
end;
R := FindNext(SearchRec);
until R <> 0;
Result := (R = ERROR_NO_MORE_FILES);
finally
SysUtils.FindClose(SearchRec);
end;
end;
end;
function NtfsGetCompression(const FileName: string; var State: Short): Boolean;
var
Handle: THandle;
BytesReturned: DWORD;
begin
Result := False;
Handle := CreateFile(PChar(FileName), 0, 0, nil, OPEN_EXISTING,
FileFlag[IsDirectory(FileName)], 0);
if Handle <> INVALID_HANDLE_VALUE then
try
Result := DeviceIoControl(Handle, FSCTL_GET_COMPRESSION, nil, 0, @State,
SizeOf(Short), BytesReturned, nil);
finally
CloseHandle(Handle);
end;
end;
function NtfsGetCompression(const FileName: string): TFileCompressionState;
var
State: Short;
begin
if not NtfsGetCompression(FileName, State) then
RaiseLastOSError;
case State of
COMPRESSION_FORMAT_NONE:
Result := fcNoCompression;
COMPRESSION_FORMAT_LZNT1:
Result := fcLZNT1Compression;
else
// (rom) very dubious.
Assert(False, 'TFileCompressionState requires expansion');
Result := TFileCompressionState(State);
end;
end;
function NtfsSetCompression(const FileName: string; const State: Short): Boolean;
begin
Result := SetCompression(FileName, State, FileFlag[IsDirectory(FileName)]);
end;
{$STACKFRAMES ON}
procedure NtfsSetFileCompression(const FileName: string; const State: TFileCompressionState);
begin
ValidateArgument(not IsDirectory(FileName), 'NtfsSetFileCompression', 'FileName');
if not SetCompression(FileName, CompressionFormat[State], 0) then
RaiseLastOSError;
end;
procedure NtfsSetDefaultFileCompression(const Directory: string; const State: TFileCompressionState);
begin
ValidateArgument(IsDirectory(Directory), 'NtfsSetDefaultFileCompression', 'Directory');
if not SetCompression(Directory, CompressionFormat[State], FILE_FLAG_BACKUP_SEMANTICS) then
RaiseLastOSError;
end;
procedure NtfsSetDirectoryTreeCompression(const Directory: string; const State: TFileCompressionState);
begin
ValidateArgument(IsDirectory(Directory), 'NtfsSetDirectoryTreeCompression', 'Directory');
if not SetPathCompression(Directory, '*', CompressionFormat[State], True, True) then
RaiseLastOSError;
end;
{$IFNDEF STACKFRAMES_ON}
{$STACKFRAMES OFF}
{$ENDIF ~STACKFRAMES_ON}
procedure NtfsSetPathCompression(const Path: string;
const State: TFileCompressionState; Recursive: Boolean);
var
Dir, Mask: string;
SetDefault: Boolean;
begin
SetDefault := IsDirectory(Path);
if SetDefault then
begin
Dir := Path;
Mask := '*';
end
else
begin
Dir := ExtractFilePath(Path);
Mask := ExtractFileName(Path);
if Mask = '' then
Mask := '*';
end;
if not SetPathCompression(Dir, Mask, CompressionFormat[State], SetDefault, Recursive) then
RaiseLastOSError;
end;
//=== NTFS - Sparse Files ====================================================
function NtfsSetSparse(const FileName: string): Boolean;
var
Handle: THandle;
BytesReturned: DWORD;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
Result := DeviceIoControl(Handle, FSCTL_SET_SPARSE, nil, 0, nil, 0, BytesReturned, nil);
finally
CloseHandle(Handle);
end;
end;
function NtfsZeroDataByHandle(const Handle: THandle; const First, Last: Int64): Boolean;
var
BytesReturned: DWORD;
ZeroDataInfo: TFileZeroDataInformation;
Info: TByHandleFileInformation;
begin
Result := False;
if Handle <> INVALID_HANDLE_VALUE then
begin
// Continue only if the file is a sparse file, this avoids the overhead
// associated with an IOCTL when the file isn't even a sparse file.
GetFileInformationByHandle(Handle, Info);
Result := (Info.dwFileAttributes and FILE_ATTRIBUTE_SPARSE_FILE) <> 0;
if Result then
begin
ZeroDataInfo.FileOffset.QuadPart := First;
ZeroDataInfo.BeyondFinalZero.QuadPart := Last;
Result := DeviceIoControl(Handle, FSCTL_SET_ZERO_DATA, @ZeroDataInfo,
SizeOf(ZeroDataInfo), nil, 0, BytesReturned, nil);
end;
end;
end;
function NtfsZeroDataByName(const FileName: string; const First, Last: Int64): Boolean;
var
Handle: THandle;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -