
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7555 8e941d3f-bd1b-0410-a28a-d453659cc2b4
972 lines
27 KiB
ObjectPascal
972 lines
27 KiB
ObjectPascal
unit uvirtuallayer;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, strutils,
|
|
uvirtuallayer_types, uvirtuallayer_stream;
|
|
|
|
type
|
|
|
|
{ TVirtualLayer }
|
|
|
|
TVirtualLayer=class(TObject)
|
|
private
|
|
FMountPoints: array of TVirtualMountPoint;
|
|
m_Critical: TRTLCriticalSection;
|
|
FFreeLayerStreamOnFree: Boolean;
|
|
function GetIsWritableMedia: Boolean;
|
|
function GetMountPath: UTF8String;
|
|
function GetRootLayer: TVirtualLayer;
|
|
procedure SetParentLayer(const AValue: TVirtualLayer);
|
|
function PathMountedInCurrentLayer(const APath: UTF8String): TVirtualLayer;
|
|
function BuildPathFromIndex(const ASplittedPath: TStringList; const Index: integer): UTF8String;
|
|
function FindMounted(const APath: UTF8String; out ARemainPath: UTF8String): TVirtualLayer; overload;
|
|
function FindMounted(const APath: UTF8String): TVirtualLayer; overload;
|
|
function PreviousPath(const APath: UTF8String): UTF8String;
|
|
function StripMountPoints(const APath: UTF8String): UTF8String;
|
|
|
|
protected
|
|
FParentLayer: TVirtualLayer;
|
|
FVirtualLayerStream: TStream;
|
|
FMountedInPath: UTF8String;
|
|
|
|
procedure SplitPath(const APath: UTF8String;const ASplittedPath: TStringList);
|
|
function FileToVirtualLayer(const ATargetFileName: UTF8String;out TargetVL: TVirtualLayer): Boolean;
|
|
function AcrossLayersCopy(const ASourceFileName,ATargetFileName: UTF8String): Boolean;
|
|
function AcrossLayersMove(const ASourceFileName,ATargetFileName: UTF8String): Boolean;
|
|
function NormalizePath(const APath: UTF8String): UTF8String;
|
|
function RemoveRootPathDelimiter(const APath: UTF8String): UTF8String;
|
|
function Initialize(): boolean; virtual;
|
|
|
|
//Functions to be implemented in specializations
|
|
function intfOpenFile(const AFileName: UTF8String; const AMode: cardinal): TvlHandle; virtual; abstract;
|
|
function intfCloseFile(const Handle: TvlHandle): Boolean; virtual; abstract;
|
|
function intfFindList(const APath: UTF8String; const AMask: UTF8String): TVirtualLayer_FolderList; virtual; abstract;
|
|
function intfSeek(const AHandle: TvlHandle; const APosition: int64; const Origin: word): int64; virtual; abstract;
|
|
function intfRead(const Handle: TvlHandle; const Buffer: PBYTE; const Size: int64): int64; virtual; abstract;
|
|
function intfWrite(const Handle: TvlHandle; const Buffer: PBYTE; const Size: int64): int64; virtual; abstract;
|
|
function intfGetFileSize(const AHandle: TvlHandle): int64; virtual; abstract;
|
|
function intfSetFileSize(const AHandle: TvlHandle; const ANewFileSize: int64): Boolean; virtual; abstract;
|
|
function intfDeleteFile(const AFileName: UTF8String): boolean; virtual; abstract;
|
|
function intfGetFreeSpace(const APath: UTF8String): int64; virtual; abstract;
|
|
function intfIsWritableMedia(): Boolean; virtual; abstract;
|
|
function intfMakeFolder(const AFolder: UTF8String): Boolean; virtual; abstract;
|
|
function intfRemoveFolder(const AFolder: UTF8String): Boolean; virtual; abstract;
|
|
function intfCopy(const ASourceFileName,ATargetFileName: UTF8String): Boolean; virtual;
|
|
function intfMove(const ASourceFileName,ATargetFileName: UTF8String): Boolean; virtual;
|
|
|
|
procedure Lock(); virtual;
|
|
procedure Unlock(); virtual;
|
|
|
|
public
|
|
function MakeFolder(const AFolder: UTF8String): Boolean;
|
|
function RemoveFolder(const AFolder: UTF8String): Boolean;
|
|
function DeleteFile(const AFileName: UTF8String): Boolean;
|
|
function Read(const Handle: TvlHandle; const Buffer: PBYTE; const Size: int64): int64;
|
|
function Write(const Handle: TvlHandle; const Buffer: PBYTE; const Size: int64): int64;
|
|
function FindList(const APath: UTF8String; const AMask: UTF8String): TVirtualLayer_FolderList;
|
|
function OpenFile(const FileName: UTF8String; const Mode: cardinal): TvlHandle;
|
|
function CloseFile(const Handle: TvlHandle): Boolean;
|
|
function GetFileSize(const AHandle: TvlHandle): int64;
|
|
function SetFileSize(const AHandle: TvlHandle; const ANewSize: int64): Boolean;
|
|
function Seek(const AHandle: TvlHandle; const APosition: int64; const Origin: Word): int64;
|
|
function FileExists(const AFileName: UTF8String): Boolean; virtual;
|
|
function MoveFile(const ASourceFileName,ATargetFileName: UTF8String): Boolean; virtual;
|
|
function CopyFile(const ASourceFileName,ATargetFileName: UTF8String): Boolean; virtual;
|
|
function GetFreeSpace(const APath: UTF8String): int64;
|
|
function FindFirst(const APath: String;const Attr: LongInt;out Rlst: sysutils.TSearchRec): LongInt;
|
|
function FindNext(var Rlst: sysutils.TSearchRec): LongInt;
|
|
procedure FindClose(Rlst: sysutils.TSearchRec);
|
|
|
|
procedure SplitFileNamePath(const AFullPath: UTF8String; out APath: UTF8String; out AFileName: UTF8String);
|
|
function PathToVirtualLayer(const APath: UTF8String): TVirtualLayer;
|
|
function CreateStream(const AFileName: UTF8String; const AMode: Cardinal): TVirtualLayer_Stream;
|
|
function Mount(const AMountPath: UTF8String; const AVirtualLayer: TVirtualLayer): Boolean;
|
|
function UnMount(const AMountPath: UTF8String; const FreeAssociatedVirtualLayer: Boolean=true): Boolean;
|
|
|
|
property FreeLayerStreamOnFree: Boolean read FFreeLayerStreamOnFree write FFreeLayerStreamOnFree;
|
|
property IsWritableMedia: Boolean read GetIsWritableMedia;
|
|
property MountPath: UTF8String read GetMountPath;
|
|
property RootLayer: TVirtualLayer read GetRootLayer;
|
|
property ParentLayer: TVirtualLayer read FParentLayer write SetParentLayer;
|
|
|
|
Constructor Create(const AVirtualLayerStream: TStream);
|
|
procedure PrepareDestroy(); virtual;
|
|
Destructor Destroy(); override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
type
|
|
TFileRecLocal = record
|
|
FL: TVirtualLayer_FolderList;
|
|
Attr: LongInt;
|
|
end;
|
|
PFileRecLocal=^TFileRecLocal;
|
|
|
|
{ TVirtualLayer }
|
|
|
|
procedure TVirtualLayer.SplitPath(const APath: UTF8String;const ASplittedPath: TStringList);
|
|
var
|
|
j,k: integer;
|
|
LB: integer;
|
|
Chunk: UTF8String = '';
|
|
ChunkC: integer;
|
|
begin
|
|
ASplittedPath.Clear;
|
|
LB:=1;
|
|
for j := 1 to Length(APath) do begin
|
|
if APath[j]='/' then begin
|
|
SetLength(Chunk,j-LB);
|
|
ChunkC:=1;
|
|
for k := LB to j-1 do begin
|
|
Chunk[ChunkC]:=APath[k];
|
|
inc(ChunkC);
|
|
end;
|
|
if Chunk<>'' Then begin
|
|
ASplittedPath.Add(Chunk);
|
|
end;
|
|
LB:=j+1;
|
|
end;
|
|
end;
|
|
if LB<Length(APath) then begin
|
|
ChunkC:=1;
|
|
SetLength(Chunk,Length(APath)-LB+1);
|
|
for k := LB to Length(APath) do begin
|
|
Chunk[ChunkC]:=APath[k];
|
|
inc(ChunkC);
|
|
end;
|
|
if Chunk<>'' Then begin
|
|
ASplittedPath.Add(Chunk);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TVirtualLayer.SetParentLayer(const AValue: TVirtualLayer);
|
|
begin
|
|
if AValue<>nil Then begin
|
|
DoneCriticalsection(m_Critical);
|
|
end;
|
|
FParentLayer:=AValue;
|
|
end;
|
|
|
|
function TVirtualLayer.GetIsWritableMedia: Boolean;
|
|
begin
|
|
Result:=false;
|
|
if intfIsWritableMedia() Then begin
|
|
//If this media is writable, all parents must be also.
|
|
if Assigned(FParentLayer) Then begin
|
|
if FParentLayer.IsWritableMedia Then begin
|
|
Result:=true;
|
|
end;
|
|
end else begin
|
|
Result:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.FileToVirtualLayer(const ATargetFileName: UTF8String;
|
|
out TargetVL: TVirtualLayer): Boolean;
|
|
var
|
|
FileName: UTF8String;
|
|
Path: UTF8String;
|
|
begin
|
|
TargetVL:=nil;
|
|
SplitFileNamePath(ATargetFileName, Path, FileName);
|
|
if FileName='' then begin
|
|
Result:=false;
|
|
exit;
|
|
end;
|
|
TargetVL:=FindMounted(Path);
|
|
if TargetVL=nil then begin
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
Result:=true;
|
|
end;
|
|
|
|
function TVirtualLayer.GetMountPath: UTF8String;
|
|
begin
|
|
if Assigned(FParentLayer) then begin
|
|
Result:=FParentLayer.MountPath+copy(FMountedInPath,2,Length(FMountedInPath));
|
|
end else begin
|
|
Result:='/';
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.GetRootLayer: TVirtualLayer;
|
|
var
|
|
PL: TVirtualLayer;
|
|
begin
|
|
if FParentLayer<>nil then begin
|
|
PL:=FParentLayer.GetRootLayer;
|
|
if PL=nil then begin
|
|
Result:=Self;
|
|
end else begin
|
|
Result:=PL;
|
|
end;
|
|
end else begin
|
|
Result:=Self;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.PathMountedInCurrentLayer(const APath: UTF8String): TVirtualLayer;
|
|
var
|
|
j: integer;
|
|
Count: integer;
|
|
begin
|
|
Result:=nil;
|
|
Lock();
|
|
Count:=Length(FMountPoints);
|
|
for j := 0 to Count-1 do begin
|
|
with FMountPoints[j] do begin
|
|
if MountPath=APath Then begin
|
|
Result:=TVirtualLayer(MountedVirtual);
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
Unlock();
|
|
end;
|
|
|
|
function TVirtualLayer.BuildPathFromIndex(const ASplittedPath: TStringList;
|
|
const Index: integer): UTF8String;
|
|
var
|
|
GluePath: UTF8String;
|
|
j: integer;
|
|
begin
|
|
GluePath:='';
|
|
for j := Index to ASplittedPath.Count-1 do begin
|
|
GluePath:=GluePath+'/'+ASplittedPath[j];
|
|
end;
|
|
Result:=GluePath;
|
|
end;
|
|
|
|
procedure TVirtualLayer.Lock();
|
|
begin
|
|
if Assigned(FParentLayer) then begin
|
|
FParentLayer.Lock();
|
|
end else begin
|
|
EnterCriticalsection(m_Critical);
|
|
end;
|
|
end;
|
|
|
|
procedure TVirtualLayer.Unlock();
|
|
begin
|
|
if Assigned(FParentLayer) then begin
|
|
FParentLayer.Unlock();
|
|
end else begin
|
|
LeaveCriticalsection(m_Critical);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.MakeFolder(const AFolder: UTF8String): Boolean;
|
|
var
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
begin
|
|
VL:=FindMounted(AFolder,RemainPath);
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.MakeFolder(RemainPath);
|
|
end else begin
|
|
Result:=intfMakeFolder(AFolder);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.RemoveFolder(const AFolder: UTF8String): Boolean;
|
|
var
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
begin
|
|
VL:=FindMounted(AFolder,RemainPath);
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.RemoveFolder(RemainPath);
|
|
end else begin
|
|
Result:=intfRemoveFolder(AFolder);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.DeleteFile(const AFileName: UTF8String): Boolean;
|
|
var
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
begin
|
|
VL:=FindMounted(AFileName,RemainPath);
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.DeleteFile(RemainPath);
|
|
end else begin
|
|
Result:=intfDeleteFile(AFileName);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.Read(const Handle: TvlHandle; const Buffer: PBYTE;
|
|
const Size: int64): int64;
|
|
var
|
|
vlHandleRecord: PvlHandleRecord;
|
|
begin
|
|
vlHandleRecord:=PvlHandleRecord(Handle);
|
|
Result:=TVirtualLayer(vlHandleRecord^.VirtualLayer).intfRead(vlHandleRecord^.Handle,Buffer,Size);
|
|
end;
|
|
|
|
function TVirtualLayer.Write(const Handle: TvlHandle; const Buffer: PBYTE;
|
|
const Size: int64): int64;
|
|
var
|
|
vlHandleRecord: PvlHandleRecord;
|
|
begin
|
|
vlHandleRecord:=PvlHandleRecord(Handle);
|
|
Result:=TVirtualLayer(vlHandleRecord^.VirtualLayer).intfWrite(vlHandleRecord^.Handle,Buffer,Size);
|
|
end;
|
|
|
|
function TVirtualLayer.FindList(const APath: UTF8String; const AMask: UTF8String
|
|
): TVirtualLayer_FolderList;
|
|
var
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
MyPath: UTF8String;
|
|
FullPath: UTF8String;
|
|
j: integer;
|
|
VI: TVirtualLayer_Item;
|
|
MountP: UTF8String;
|
|
Found: Boolean;
|
|
begin
|
|
FullPath:=NormalizePath(APath);
|
|
VL:=FindMounted(FullPath,RemainPath);
|
|
MyPath:=LeftStr(FullPath,Length(FullPath)-Length(RemainPath));
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.FindList(NormalizePath(RemainPath),AMask);
|
|
if Result<>nil Then begin
|
|
Result.AddInheritedPath(MyPath);
|
|
end;
|
|
end else begin
|
|
Result:=intfFindList(FullPath,AMask);
|
|
//Add mount points for this folder
|
|
for j := 0 to Length(FMountPoints)-1 do begin
|
|
MountP:=PreviousPath(FMountPoints[j].MountPath);
|
|
if MountP<>'' Then begin
|
|
if MountP=MyPath Then begin
|
|
VI:=TVirtualLayer_Item.Create;
|
|
VI.Name:=RightStr(FMountPoints[j].MountPath,Length(FMountPoints[j].MountPath)-Length(MountP));
|
|
VI.Name:=LeftStr(VI.Name,Length(VI.Name)-1);
|
|
VI.Size:=0;
|
|
VI.IsFolder:=true;
|
|
Result.Add(VI);
|
|
end;
|
|
end;
|
|
end;
|
|
//Verify ".." folder exists (previous folder)
|
|
//Unless the current folder is the Top one.
|
|
Found:=false;
|
|
for j := 0 to Result.Count-1 do begin
|
|
VI:=TVirtualLayer_Item(Result[j]);
|
|
if VI.Name='..' Then begin
|
|
Found:=True;
|
|
break;
|
|
end;
|
|
end;
|
|
if (not Found) and (FParentLayer<>nil) Then begin
|
|
VI:=TVirtualLayer_Item.Create;
|
|
VI.Name:='..';
|
|
VI.Size:=0;
|
|
VI.IsFolder:=true;
|
|
Result.Insert(0,VI);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.FindMounted(const APath: UTF8String; out ARemainPath: UTF8String): TVirtualLayer;
|
|
var
|
|
PathList: TStringList;
|
|
j: integer;
|
|
CurPath: UTF8String;
|
|
VirtualLayer: TVirtualLayer;
|
|
begin
|
|
ARemainPath:='';
|
|
Result:=nil;
|
|
PathList:=TStringList.Create;
|
|
SplitPath(NormalizePath(APath),PathList);
|
|
//Remove empty strings
|
|
for j := PathList.Count-1 downto 0 do begin
|
|
if PathList[j]='' Then begin
|
|
PathList.Delete(j);
|
|
end;
|
|
end;
|
|
Lock();
|
|
CurPath:='/';
|
|
for j := 0 to PathList.Count-1 do begin
|
|
CurPath:=NormalizePath(Curpath+PathList[j]);
|
|
VirtualLayer:=PathMountedInCurrentLayer(CurPath);
|
|
if VirtualLayer<>nil Then begin;
|
|
Result:=VirtualLayer;
|
|
ARemainPath:=BuildPathFromIndex(PathList,j+1);
|
|
break;
|
|
end;
|
|
end;
|
|
PathList.Free;
|
|
Unlock();
|
|
end;
|
|
|
|
function TVirtualLayer.FindMounted(const APath: UTF8String): TVirtualLayer;
|
|
var
|
|
Remain: UTF8String;
|
|
VL: TVirtualLayer;
|
|
Path: UTF8String;
|
|
begin
|
|
Remain:=APath;
|
|
VL:=nil;
|
|
Repeat
|
|
Result:=VL;
|
|
Path:=Remain;
|
|
VL:=FindMounted(Path,Remain);
|
|
until VL=nil;
|
|
end;
|
|
|
|
function TVirtualLayer.NormalizePath(const APath: UTF8String): UTF8String;
|
|
//It should also resolve /../ path expressions.
|
|
var
|
|
j: integer;
|
|
PL: TStringList;
|
|
Skip: Boolean;
|
|
begin
|
|
if RightStr(APath,1)<>'/' then begin
|
|
Result:=APath+'/';
|
|
end else begin
|
|
Result:=APath;
|
|
end;
|
|
if LeftStr(Result,1)<>'/' then begin
|
|
Result:='/'+Result;
|
|
end;
|
|
Result:=StringReplace(Result,'/./','/',[]);
|
|
if PosEx('../',Result)>0 then begin
|
|
//Dissasemble path and reassemble it to remove the /../ path
|
|
PL:=TStringList.Create;
|
|
SplitPath(Result,PL);
|
|
for j := PL.Count-1 downto 0 do begin
|
|
if PL[j]='' Then PL.Delete(j);
|
|
end;
|
|
Skip:=False;
|
|
for j := PL.Count-1 downto 0 do begin
|
|
if Skip then begin
|
|
PL.Delete(j);
|
|
Skip:=false;
|
|
end else begin
|
|
if PL[j]='..' Then begin
|
|
PL.Delete(j);
|
|
Skip:=true;
|
|
end;
|
|
end;
|
|
end;
|
|
Result:='/';
|
|
for j := 0 to PL.Count-1 do begin
|
|
Result:=Result+PL[j]+'/';
|
|
end;
|
|
PL.Free;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.RemoveRootPathDelimiter(const APath: UTF8String
|
|
): UTF8String;
|
|
begin
|
|
if Length(APath)>0 then begin
|
|
if APath[1]='/' then begin
|
|
Result:=Copy(APath,2,length(APath)-1);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.PreviousPath(const APath: UTF8String): UTF8String;
|
|
var
|
|
PathParts: TStringList;
|
|
j: integer;
|
|
begin
|
|
PathParts:=TStringList.Create;
|
|
SplitPath(APath,PathParts);
|
|
for j := PathParts.Count-1 downto 0 do begin
|
|
if PathParts[j]='' Then begin
|
|
PathParts.Delete(j);
|
|
end;
|
|
end;
|
|
Result:='/';
|
|
for j := 0 to PathParts.Count-2 do begin
|
|
Result:=Result+PathParts[j]+'/';
|
|
end;
|
|
PathParts.Free;
|
|
end;
|
|
|
|
function TVirtualLayer.StripMountPoints(const APath: UTF8String): UTF8String;
|
|
var
|
|
PathList: TStringList;
|
|
j: integer;
|
|
CurPath: UTF8String;
|
|
VirtualLayer: TVirtualLayer;
|
|
ARemainPath: UTF8String;
|
|
OtherPath: UTF8String;
|
|
begin
|
|
ARemainPath:='';
|
|
PathList:=TStringList.Create;
|
|
SplitPath(NormalizePath(APath),PathList);
|
|
//Remove empty strings
|
|
for j := PathList.Count-1 downto 0 do begin
|
|
if PathList[j]='' Then begin
|
|
PathList.Delete(j);
|
|
end;
|
|
end;
|
|
Lock();
|
|
CurPath:='/';
|
|
for j := 0 to PathList.Count-1 do begin
|
|
CurPath:=NormalizePath(Curpath+PathList[j]);
|
|
VirtualLayer:=PathMountedInCurrentLayer(CurPath);
|
|
if VirtualLayer<>nil Then begin;
|
|
ARemainPath:=BuildPathFromIndex(PathList,j+1);
|
|
OtherPath:=VirtualLayer.StripMountPoints(ARemainPath);
|
|
if OtherPath='' Then begin
|
|
Result:=ARemainPath;
|
|
end;
|
|
break;
|
|
end;
|
|
end;
|
|
PathList.Free;
|
|
Unlock();
|
|
end;
|
|
|
|
function TVirtualLayer.Initialize(): boolean;
|
|
begin
|
|
result:=true;
|
|
end;
|
|
|
|
function TVirtualLayer.intfCopy(const ASourceFileName,
|
|
ATargetFileName: UTF8String): Boolean;
|
|
var
|
|
L: TVirtualLayer;
|
|
begin
|
|
L:=RootLayer;
|
|
Result:=L.AcrossLayersCopy(MountPath+RemoveRootPathDelimiter(ASourceFileName),
|
|
MountPath+RemoveRootPathDelimiter(ATargetFileName));
|
|
end;
|
|
|
|
function TVirtualLayer.intfMove(const ASourceFileName,
|
|
ATargetFileName: UTF8String): Boolean;
|
|
begin
|
|
Result:=AcrossLayersMove(MountPath+RemoveRootPathDelimiter(ASourceFileName),
|
|
MountPath+RemoveRootPathDelimiter(ATargetFileName));
|
|
end;
|
|
|
|
function TVirtualLayer.AcrossLayersMove(const ASourceFileName,
|
|
ATargetFileName: UTF8String): Boolean;
|
|
begin
|
|
//By default move a file creating a new copy and deleting the old one.
|
|
//This function should be specialized for each layer.
|
|
if not intfCopy(ASourceFileName,ATargetFileName) then begin
|
|
DeleteFile(ATargetFileName);
|
|
result:=false;
|
|
end else begin
|
|
DeleteFile(ASourceFileName);
|
|
result:=true;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.AcrossLayersCopy(const ASourceFileName,
|
|
ATargetFileName: UTF8String): Boolean;
|
|
var
|
|
SourceHandle, TargetHandle: TvlHandle;
|
|
FileSize: int64;
|
|
Buffer: array [0..1023] of BYTE;
|
|
WriteBytes: int64;
|
|
begin
|
|
//By default copy a file.
|
|
//This function should be specialized for each layer.
|
|
Result:=true;
|
|
SourceHandle:=OpenFile(ASourceFileName,fmOpenRead);
|
|
if SourceHandle=nil Then begin
|
|
Result:=false;
|
|
Exit;
|
|
end;
|
|
TargetHandle:=OpenFile(ATargetFileName,fmCreate);
|
|
if TargetHandle=nil Then begin
|
|
CloseFile(SourceHandle);
|
|
Result:=false;
|
|
Exit;
|
|
end;
|
|
FileSize:=GetFileSize(SourceHandle);
|
|
while FileSize>0 do begin
|
|
if FileSize>1024 Then begin
|
|
if Read(SourceHandle,@Buffer[0],1024)<>1024 Then begin
|
|
Result:=false;
|
|
break;
|
|
end else begin
|
|
WriteBytes:=1024;
|
|
dec(FileSize,1024);
|
|
end;
|
|
end else begin
|
|
if Read(SourceHandle,@Buffer[0],FileSize)<>FileSize Then begin
|
|
Result:=false;
|
|
break;
|
|
end else begin
|
|
WriteBytes:=FileSize;
|
|
FileSize:=0;
|
|
end;
|
|
end;
|
|
if Write(TargetHandle,@Buffer[0],WriteBytes)<>WriteBytes Then begin
|
|
Result:=false;
|
|
break;
|
|
end;
|
|
end;
|
|
CloseFile(SourceHandle);
|
|
CloseFile(TargetHandle);
|
|
end;
|
|
|
|
function TVirtualLayer.MoveFile(const ASourceFileName,
|
|
ATargetFileName: UTF8String): Boolean;
|
|
var
|
|
SourceVL,TargetVL: TVirtualLayer;
|
|
begin
|
|
if not FileToVirtualLayer(ATargetFileName,TargetVL) Then begin
|
|
Result:=false;
|
|
exit;
|
|
end;
|
|
if not FileToVirtualLayer(ASourceFileName,SourceVL) Then begin
|
|
Result:=false;
|
|
exit;
|
|
end;
|
|
if SourceVL=TargetVL then begin
|
|
//Move in the same virtual layer. invoke intfmove.
|
|
Result:=SourceVL.intfMove(StripMountPoints(ASourceFileName),StripMountPoints(ATargetFileName));
|
|
end else begin
|
|
Result:=intfMove(ASourceFileName,ATargetFileName);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.CopyFile(const ASourceFileName, ATargetFileName: UTF8String
|
|
): Boolean;
|
|
var
|
|
SourceVL,TargetVL: TVirtualLayer;
|
|
begin
|
|
if not FileToVirtualLayer(ATargetFileName,TargetVL) Then begin
|
|
Result:=false;
|
|
exit;
|
|
end;
|
|
if not FileToVirtualLayer(ASourceFileName,SourceVL) Then begin
|
|
Result:=false;
|
|
exit;
|
|
end;
|
|
if SourceVL=TargetVL then begin
|
|
//Move in the same virtual layer. invoke intfmove.
|
|
Result:=SourceVL.intfCopy(StripMountPoints(ASourceFileName),StripMountPoints(ATargetFileName));
|
|
end else begin
|
|
Result:=AcrossLayersCopy(ASourceFileName,ATargetFileName);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.OpenFile(const FileName: UTF8String; const Mode: cardinal): TvlHandle;
|
|
var
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
vlHandleRecord: PvlHandleRecord;
|
|
Handle: TvlHandle;
|
|
begin
|
|
VL:=FindMounted(FileName,RemainPath);
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.OpenFile(RemainPath,Mode);
|
|
end else begin
|
|
vlHandleRecord:=nil;
|
|
Handle:=intfOpenFile(FileName,Mode);
|
|
if Handle=VL_INVALID_HANDLE then begin
|
|
Result:=VL_INVALID_HANDLE;
|
|
end else begin
|
|
GetMem(vlHandleRecord,sizeof(TvlHandleRecord));
|
|
vlHandleRecord^.Handle:=Handle;
|
|
vlHandleRecord^.VirtualLayer:=Self;
|
|
Result:=TvlHandle(vlHandleRecord);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.CloseFile(const Handle: TvlHandle): Boolean;
|
|
var
|
|
vlHandleRecord: PvlHandleRecord;
|
|
begin
|
|
vlHandleRecord:=PvlHandleRecord(Handle);
|
|
Result:=TVirtualLayer(vlHandleRecord^.VirtualLayer).intfCloseFile(vlHandleRecord^.Handle);
|
|
FreeMem(vlHandleRecord);
|
|
end;
|
|
|
|
function TVirtualLayer.GetFileSize(const AHandle: TvlHandle): int64;
|
|
var
|
|
vlHandleRecord: PvlHandleRecord;
|
|
begin
|
|
vlHandleRecord:=PvlHandleRecord(AHandle);
|
|
Result:=TVirtualLayer(vlHandleRecord^.VirtualLayer).intfGetFileSize(vlHandleRecord^.Handle);
|
|
end;
|
|
|
|
function TVirtualLayer.SetFileSize(const AHandle: TvlHandle;
|
|
const ANewSize: int64): Boolean;
|
|
var
|
|
vlHandleRecord: PvlHandleRecord;
|
|
begin
|
|
vlHandleRecord:=PvlHandleRecord(AHandle);
|
|
Result:=TVirtualLayer(vlHandleRecord^.VirtualLayer).intfSetFileSize(vlHandleRecord^.Handle,ANewSize);
|
|
end;
|
|
|
|
function TVirtualLayer.Seek(const AHandle: TvlHandle; const APosition: int64; const Origin: Word): int64;
|
|
var
|
|
vlHandleRecord: PvlHandleRecord;
|
|
begin
|
|
vlHandleRecord:=PvlHandleRecord(AHandle);
|
|
Result:=TVirtualLayer(vlHandleRecord^.VirtualLayer).intfSeek(vlHandleRecord^.Handle,APosition,Origin);
|
|
end;
|
|
|
|
function TVirtualLayer.FileExists(const AFileName: UTF8String): Boolean;
|
|
var
|
|
fl: TVirtualLayer_FolderList;
|
|
Path: UTF8String;
|
|
FileName: UTF8String;
|
|
j: integer;
|
|
begin
|
|
Result:=false;
|
|
SplitFileNamePath(AFileName,Path,FileName);
|
|
fl:=FindList(Path,FileName);
|
|
for j := 0 to FL.Count-1 do begin
|
|
if TVirtualLayer_Item(FL[j]).Name=FileName Then begin
|
|
Result:=true;
|
|
break;
|
|
end;
|
|
end;
|
|
fl.Free;
|
|
end;
|
|
|
|
procedure TVirtualLayer.SplitFileNamePath(const AFullPath: UTF8String; out
|
|
APath: UTF8String; out AFileName: UTF8String);
|
|
var
|
|
j: integer;
|
|
begin
|
|
APath:=AFullPath;
|
|
AFileName:='';
|
|
for j := Length(AFullPath) downto 1 do begin
|
|
if AFullPath[j]='/' Then begin
|
|
APath:=LeftStr(AFullPath,j);
|
|
AFileName:=RightStr(AFullPath,Length(AFullPath)-Length(APath));
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.Mount(const AMountPath: UTF8String;
|
|
const AVirtualLayer: TVirtualLayer): Boolean;
|
|
var
|
|
j: integer;
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
begin
|
|
if AVirtualLayer.FParentLayer<>nil Then begin
|
|
Result:=false;
|
|
Exit;
|
|
end;
|
|
Lock();
|
|
VL:=FindMounted(AMountPath,RemainPath);
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.Mount(RemainPath,AVirtualLayer);
|
|
end else begin
|
|
j:=Length(FMountPoints);
|
|
SetLength(FMountPoints,j+1);
|
|
FMountPoints[j].MountPath:=NormalizePath(AMountPath);
|
|
FMountPoints[j].MountedVirtual:=AVirtualLayer;
|
|
AVirtualLayer.SetParentLayer(Self);
|
|
AVirtualLayer.FMountedInPath:=FMountPoints[j].MountPath;
|
|
Result:=true;
|
|
end;
|
|
Unlock();
|
|
end;
|
|
|
|
function TVirtualLayer.UnMount(const AMountPath: UTF8String;
|
|
const FreeAssociatedVirtualLayer: Boolean): Boolean;
|
|
var
|
|
j,k: integer;
|
|
VirtualLayer: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
begin
|
|
Result:=false;
|
|
Lock();
|
|
VirtualLayer:=FindMounted(AMountPath,RemainPath);
|
|
if Assigned(VirtualLayer) Then begin
|
|
Result:=VirtualLayer.UnMount(RemainPath,FreeAssociatedVirtualLayer);
|
|
end else begin
|
|
for j := 0 to Length(FMountPoints)-1 do begin
|
|
if FMountPoints[j].MountPath=AMountPath Then begin
|
|
VirtualLayer:=TVirtualLayer(FMountPoints[j].MountedVirtual);
|
|
for k := j+1 to Length(FMountPoints)-1 do begin
|
|
FMountPoints[k-1].MountPath:=FMountPoints[k].MountPath;
|
|
FMountPoints[k-1].MountedVirtual:=FMountPoints[k].MountedVirtual;
|
|
end;
|
|
SetLength(FMountPoints,Length(FMountPoints)-1);
|
|
if FreeAssociatedVirtualLayer Then begin
|
|
VirtualLayer.Free;
|
|
end;
|
|
Result:=true;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
Unlock();
|
|
end;
|
|
|
|
function TVirtualLayer.PathToVirtualLayer(const APath: UTF8String
|
|
): TVirtualLayer;
|
|
var
|
|
RemainPath: UTF8String;
|
|
begin
|
|
Result:=FindMounted(APath,RemainPath);
|
|
if Result=nil Then Result:=Self;
|
|
end;
|
|
|
|
function TVirtualLayer.CreateStream(const AFileName: UTF8String;
|
|
const AMode: Cardinal): TVirtualLayer_Stream;
|
|
begin
|
|
try
|
|
result:=TVirtualLayer_Stream.Create(Self,AFileName,AMode);
|
|
except
|
|
on E: EStreamError do begin
|
|
Result:=nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.GetFreeSpace(const APath: UTF8String): int64;
|
|
var
|
|
VL: TVirtualLayer;
|
|
RemainPath: UTF8String;
|
|
begin
|
|
VL:=FindMounted(APath,RemainPath);
|
|
if Assigned(VL) Then begin
|
|
Result:=VL.GetFreeSpace(RemainPath);
|
|
end else begin
|
|
Result:=intfGetFreeSpace(APath);
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.FindFirst(const APath: String; const Attr: LongInt; out
|
|
Rlst: sysutils.TSearchRec): LongInt;
|
|
var
|
|
FindL: TVirtualLayer_FolderList;
|
|
Path: UTF8String;
|
|
Mask: UTF8String;
|
|
LHandle: PFileRecLocal;
|
|
begin
|
|
Result:=-1;
|
|
with Rlst do begin
|
|
Time:=0;
|
|
Size:=0;
|
|
Attr:=0;
|
|
Name:='';
|
|
ExcludeAttr:=0;
|
|
end;
|
|
SplitFileNamePath(APath,Path,Mask);
|
|
FindL:=FindList(APath,Mask);
|
|
LHandle:=nil;
|
|
GetMem(LHandle,sizeof(TFileRecLocal));
|
|
LHandle^.FL:=FindL;
|
|
LHandle^.Attr:=Attr;
|
|
{$HINTS OFF}
|
|
{$ifdef UNIX}
|
|
Rlst.FindHandle:=LHandle;
|
|
{$else}
|
|
Rlst.FindHandle:=PtrUint(LHandle);
|
|
{$endif}
|
|
{$HINTS ON}
|
|
if Assigned(FindL) Then begin
|
|
if FindL.Count>0 Then begin
|
|
With FindL[0] do begin
|
|
Rlst.Name:=Name;
|
|
Rlst.Size:=Size;
|
|
if IsFolder or IsMountPoint Then begin
|
|
Rlst.Attr:=Rlst.Attr+faDirectory;
|
|
end;
|
|
end;
|
|
FindL.Delete(0);
|
|
Result:=0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TVirtualLayer.FindNext(var Rlst: sysutils.TSearchRec): LongInt;
|
|
var
|
|
FindL: TVirtualLayer_FolderList;
|
|
LHandle: PFileRecLocal;
|
|
begin
|
|
Result:=-1;
|
|
with Rlst do begin
|
|
Time:=0;
|
|
Size:=0;
|
|
Attr:=0;
|
|
Name:='';
|
|
ExcludeAttr:=0;
|
|
end;
|
|
//Hint non portable conversion, it should work, but not tested.
|
|
LHandle:={%H-}PFileRecLocal(PtrUInt(Rlst.FindHandle));
|
|
FindL:=TVirtualLayer_FolderList(LHandle^.FL);
|
|
if Assigned(FindL) Then begin
|
|
if FindL.Count>0 Then begin
|
|
With FindL[0] do begin
|
|
Rlst.Name:=Name;
|
|
Rlst.Size:=Size;
|
|
if IsFolder or IsMountPoint Then begin
|
|
Rlst.Attr:=Rlst.Attr or faDirectory;
|
|
end;
|
|
end;
|
|
FindL.Delete(0);
|
|
Result:=0;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TVirtualLayer.FindClose(Rlst: sysutils.TSearchRec);
|
|
var
|
|
FindL: TVirtualLayer_FolderList;
|
|
LHandle: PFileRecLocal;
|
|
begin
|
|
//Hint: non portable conversion. Not tested but it should work.
|
|
LHandle:={%H-}PFileRecLocal(PtrUInt(Rlst.FindHandle));
|
|
FindL:=TVirtualLayer_FolderList(LHandle^.FL);
|
|
if Assigned(FindL) Then FindL.Free;
|
|
FreeMem(LHandle);
|
|
end;
|
|
|
|
constructor TVirtualLayer.Create(const AVirtualLayerStream: TStream);
|
|
begin
|
|
InitCriticalSection(m_Critical);
|
|
FVirtualLayerStream:=AVirtualLayerStream;
|
|
if Assigned(FVirtualLayerStream) Then FVirtualLayerStream.Position:=0;
|
|
end;
|
|
|
|
procedure TVirtualLayer.PrepareDestroy();
|
|
begin
|
|
//Do nothing by default.
|
|
end;
|
|
|
|
destructor TVirtualLayer.Destroy();
|
|
var
|
|
j: integer;
|
|
begin
|
|
//Notify mounted layers to write anything they need to.
|
|
for j := Length(FMountPoints)-1 downto 0 do begin
|
|
TVirtualLayer(FMountPoints[j].MountedVirtual).PrepareDestroy();
|
|
end;
|
|
//Destroy mounted layers, they must also close related handles.
|
|
for j := Length(FMountPoints)-1 downto 0 do begin
|
|
TVirtualLayer(FMountPoints[j].MountedVirtual).Free;
|
|
end;
|
|
if FFreeLayerStreamOnFree Then begin
|
|
if Assigned(FVirtualLayerStream) Then begin
|
|
FVirtualLayerStream.Free;
|
|
end;
|
|
end;
|
|
if not Assigned(FParentLayer) then begin
|
|
DoneCriticalsection(m_Critical);
|
|
end;
|
|
inherited Destroy();
|
|
end;
|
|
|
|
end.
|
|
|