(* Tux Commander - UVFSCore - VFS Core utilities and management Copyright (C) 2007 Tomas Bzatek Check for updates on tuxcmd.sourceforge.net This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) unit UVFSCore; interface uses GTKForms, ULibc, Classes, uVFSprototypes, UEngines, UCoreUtils; type TVFSPlugin = class private FVFSAllocNeeded: TVFSAllocNeeded; FVFSInit: TVFSInit; FVFSDestroy: TVFSDestroy; FVFSVersion: TVFSVersion; FVFSGetInfo: TVFSGetInfo; FVFSOpen: TVFSOpen; FVFSClose: TVFSClose; FVFSListFirst: TVFSListFirst; FVFSListNext: TVFSListNext; FVFSListClose: TVFSListClose; FVFSChangeDir: TVFSChangeDir; FVFSGetPath: TVFSGetPath; FVFSGetPrefix: TVFSGetPrefix; FVFSGetFileSystemSize: TVFSGetFileSystemSize; FVFSGetFileSystemFree: TVFSGetFileSystemFree; FVFSLogin: TVFSLogin; FVFSFileExists: TVFSFileExists; FVFSFileInfo: TVFSFileInfo; FVFSMkDir: TVFSMkDir; FVFSRemove: TVFSRemove; FVFSRename: TVFSRename; FVFSMakeSymLink: TVFSMakeSymLink; FVFSChmod: TVFSChmod; FVFSChown: TVFSChown; FVFSChangeTimes: TVFSChangeTimes; FVFSGetDirSize: TVFSGetDirSize; FVFSBreakGetDirSize: TVFSBreakGetDirSize; FVFSOpenFile: TVFSOpenFile; FVFSReadFile: TVFSReadFile; FVFSWriteFile: TVFSWriteFile; FVFSCloseFile: TVFSCloseFile; FVFSFileSeek: TVFSFileSeek; FVFSSetBlockSize: TVFSSetBlockSize; FVFSCopyOut: TVFSCopyOut; FVFSCopyIn: TVFSCopyIn; FVFSIsOnSameFS: TVFSIsOnSameFS; FVFSTwoSameFiles: TVFSTwoSameFiles; FVFSGetExts: TVFSGetExts; FVFSGetServices: TVFSGetServices; FVFSSetPassword: TVFSSetPassword; FVFSGetPasswordRequired: TVFSGetPasswordRequired; public ModuleHandle: Pointer; FullPath: string; // module path Extensions, Services: TOpenStringArray; // the list of the extensions plugin can handle constructor Create(PluginHandle: Pointer); destructor Destroy; override; function VFSVersion: integer; function VFSName: PChar; function VFSDescription: PChar; function VFSAbout: PChar; function VFSCopyright: PChar; end; TVFSEngine = class(TPanelEngine) private FGlobs: Pointer; FSourcePlugin: TVFSPlugin; FBlockSize: Cardinal; FProgressFunc: TEngineProgressFunc; FSenderThread: Pointer; BreakProcessingKind: integer; FPassword: string; procedure SetFPassword(Value: string); public ArchiveMode: boolean; constructor Create(SourcePlugin: TVFSPlugin); function VFSOpenURI(OpenFile: string): boolean; function VFSOpenEx(OpenFile: string): TVFSResult; function VFSClose: boolean; destructor Destroy; override; function GetListing(var List: TList; const AddDotFiles: boolean): integer; override; function GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; override; function ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; override; function ExplicitChDir(const NewPath: string): integer; override; function GetFileSystemSize: Int64; override; function GetFileSystemSize(const APath: string): Int64; override; function GetFileSystemFree: Int64; override; function GetFileSystemFree(const APath: string): Int64; override; function MakeDir(const NewDir: string): integer; override; function GetDirSize(APath: string): Int64; override; function Remove(APath: string): integer; override; procedure FillDirFiles(APath: string; List: TList; ALevel: word); override; function GetFileInfoSL(APath: string): PDataItemSL; override; function FileExists(const FileName: string; const Use_lstat: boolean = False): Boolean; override; function DirectoryExists(const FileName: string; const Use_lstat: boolean = False): Boolean; override; function MakeSymLink(const NewFileName, PointTo: string): integer; override; function Chmod(const FileName: string; const Mode: integer): integer; override; function Chown(const FileName: string; const UID, GID: integer): integer; override; procedure BreakProcessing(ProcessingKind: integer); override; function RenameFile(SourceFile, DestFile: string): integer; override; function ChangeTimes(APath: string; mtime, atime: Int64): integer; override; procedure GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); override; function OpenFile(const APath: string; Mode: integer; var Error: integer): TEngineFileDes; override; function ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; var Error: integer): integer; override; function WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; var Error: integer): integer; override; function CloseFile(const FileDescriptor: TEngineFileDes): integer; override; function FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; var Error: integer): Int64; override; function IsOnROMedium(const FileName: string): boolean; override; function FileCanRun(const FileName: string): boolean; override; function GetPath: string; override; procedure SetPath(Value: string); override; function GetPrefix: string; override; function Login(Username, Password: string): integer; override; function GetBlockSize: Cardinal; override; procedure SetBlockSize(Value: Cardinal); override; function CopyFileIn(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; override; function CopyFileOut(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; override; function IsOnSameFS(const Path1, Path2: string): boolean; override; function TwoSameFiles(const Path1, Path2: string): boolean; override; function SetPassword(Password: string): integer; function GetPasswordRequired: boolean; published property Path: string read GetPath write SetPath; property BlockSize: Cardinal read GetBlockSize write SetBlockSize; property Password: string read FPassword write SetFPassword; end; procedure DoInitPlugins; var PluginList: TList; implementation uses SysUtils, UConfig, ULocale; const ConstGlobalModulePath1 = '/usr/lib/tuxcmd'; ConstGlobalModulePath2 = '/usr/local/lib/tuxcmd'; ConstLocalModulePath1 = '~/.tuxcmd/plugins'; ConstLocalModulePath2 = './plugins'; ConstLocalModulePath3 = '../lib/tuxcmd'; ConstVFSVersionRequired = 2; var BinaryPath: string; procedure VFSLogFunc(s: PChar); cdecl; begin DebugMsg([' ### VFS ###: ', s]); end; (********************************************************************************************************************************) constructor TVFSPlugin.Create(PluginHandle: Pointer); begin inherited Create; ModuleHandle := PluginHandle; // Find the symbols @FVFSAllocNeeded := dlsym(ModuleHandle, 'VFSAllocNeeded'); @FVFSInit := dlsym(ModuleHandle, 'VFSInit'); @FVFSDestroy := dlsym(ModuleHandle, 'VFSDestroy'); @FVFSVersion := dlsym(ModuleHandle, 'VFSVersion'); @FVFSGetInfo := dlsym(ModuleHandle, 'VFSGetInfo'); @FVFSOpen := dlsym(ModuleHandle, 'VFSOpen'); @FVFSClose := dlsym(ModuleHandle, 'VFSClose'); @FVFSListFirst := dlsym(ModuleHandle, 'VFSListFirst'); @FVFSListNext := dlsym(ModuleHandle, 'VFSListNext'); @FVFSListClose := dlsym(ModuleHandle, 'VFSListClose'); @FVFSGetPath := dlsym(ModuleHandle, 'VFSGetPath'); @FVFSChangeDir := dlsym(ModuleHandle, 'VFSChangeDir'); @FVFSGetPrefix := dlsym(ModuleHandle, 'VFSGetPrefix'); @FVFSGetFileSystemSize := dlsym(ModuleHandle, 'VFSGetFileSystemSize'); @FVFSGetFileSystemFree := dlsym(ModuleHandle, 'VFSGetFileSystemFree'); @FVFSLogin := dlsym(ModuleHandle, 'VFSLogin'); @FVFSFileExists := dlsym(ModuleHandle, 'VFSFileExists'); @FVFSFileInfo := dlsym(ModuleHandle, 'VFSFileInfo'); @FVFSMkDir := dlsym(ModuleHandle, 'VFSMkDir'); @FVFSRemove := dlsym(ModuleHandle, 'VFSRemove'); @FVFSRename := dlsym(ModuleHandle, 'VFSRename'); @FVFSMakeSymLink := dlsym(ModuleHandle, 'VFSMakeSymLink'); @FVFSChmod := dlsym(ModuleHandle, 'VFSChmod'); @FVFSChown := dlsym(ModuleHandle, 'VFSChown'); @FVFSChangeTimes := dlsym(ModuleHandle, 'VFSChangeTimes'); @FVFSGetDirSize := dlsym(ModuleHandle, 'VFSGetDirSize'); @FVFSBreakGetDirSize := dlsym(ModuleHandle, 'VFSBreakGetDirSize'); @FVFSCopyOut := dlsym(ModuleHandle, 'VFSCopyOut'); @FVFSCopyIn := dlsym(ModuleHandle, 'VFSCopyIn'); @FVFSOpenFile := dlsym(ModuleHandle, 'VFSOpenFile'); @FVFSReadFile := dlsym(ModuleHandle, 'VFSReadFile'); @FVFSWriteFile := dlsym(ModuleHandle, 'VFSWriteFile'); @FVFSCloseFile := dlsym(ModuleHandle, 'VFSCloseFile'); @FVFSFileSeek := dlsym(ModuleHandle, 'VFSFileSeek'); @FVFSSetBlockSize := dlsym(ModuleHandle, 'VFSSetBlockSize'); @FVFSIsOnSameFS := dlsym(ModuleHandle, 'VFSIsOnSameFS'); @FVFSTwoSameFiles := dlsym(ModuleHandle, 'VFSTwoSameFiles'); @FVFSGetExts := dlsym(ModuleHandle, 'VFSGetExts'); @FVFSGetServices := dlsym(ModuleHandle, 'VFSGetServices'); @FVFSSetPassword := dlsym(ModuleHandle, 'VFSSetPassword'); @FVFSGetPasswordRequired := dlsym(ModuleHandle, 'VFSGetPasswordRequired'); // Initialize the extensions list SetLength(Extensions, 0); SetLength(Services, 0); if @FVFSGetExts <> nil then ParseString(FVFSGetExts, ';', Extensions); if @FVFSGetServices <> nil then ParseString(FVFSGetServices, ';', Services); end; destructor TVFSPlugin.Destroy; begin if ModuleHandle <> nil then dlclose(ModuleHandle); inherited Destroy; end; (********************************************************************************************************************************) function TVFSPlugin.VFSVersion: integer; begin if @FVFSVersion <> nil then Result := FVFSVersion else Result := -1; end; function TVFSPlugin.VFSName: PChar; var Info: TVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := Info.Name; end else Result := nil; end; function TVFSPlugin.VFSDescription: PChar; var Info: TVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := Info.Description; end else Result := nil; end; function TVFSPlugin.VFSAbout: PChar; var Info: TVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := Info.About; end else Result := nil; end; function TVFSPlugin.VFSCopyright: PChar; var Info: TVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := Info.Copyright; end else Result := nil; end; (********************************************************************************************************************************) (********************************************************************************************************************************) constructor TVFSEngine.Create(SourcePlugin: TVFSPlugin); begin inherited Create; FSourcePlugin := SourcePlugin; FBlockSize := 65536; ArchiveMode := False; BreakProcessingKind := 0; if @FSourcePlugin.FVFSAllocNeeded <> nil then begin FGlobs := malloc(FSourcePlugin.FVFSAllocNeeded); memset(FGlobs, 0, FSourcePlugin.FVFSAllocNeeded); end else begin FGlobs := malloc(SizeOf(FGlobs)); memset(FGlobs, 0, SizeOf(FGlobs)); end; { DebugMsg(['sizeof(TVFSItem) = ', sizeof(TVFSItem)]); DebugMsg(['sizeof(TVFSItem.sFileName) = ', sizeof(TVFSItem.sFileName)]); DebugMsg(['sizeof(TVFSItem.iSize) = ', sizeof(TVFSItem.iSize)]); DebugMsg(['sizeof(TVFSItem.m_time) = ', sizeof(TVFSItem.m_time)]); DebugMsg(['sizeof(TVFSItem.a_time) = ', sizeof(TVFSItem.a_time)]); DebugMsg(['sizeof(TVFSItem.c_time) = ', sizeof(TVFSItem.c_time)]); DebugMsg(['sizeof(TVFSItem.iMode) = ', sizeof(TVFSItem.iMode)]); DebugMsg(['sizeof(TVFSItem.sLinkTo) = ', sizeof(TVFSItem.sLinkTo)]); DebugMsg(['sizeof(TVFSItem.iUID) = ', sizeof(TVFSItem.iUID)]); DebugMsg(['sizeof(TVFSItem.iGID) = ', sizeof(TVFSItem.iGID)]); DebugMsg(['sizeof(TVFSItem.ItemType) = ', sizeof(TVFSItem.ItemType)]); } if @FSourcePlugin.FVFSInit <> nil then FSourcePlugin.FVFSInit(FGlobs, @VFSLogFunc); FPassword := ''; end; function TVFSEngine.VFSOpenURI(OpenFile: string): boolean; begin Result := False; if @FSourcePlugin.FVFSOpen <> nil then Result := FSourcePlugin.FVFSOpen(FGlobs, PChar(OpenFile)) = cVFS_OK; end; function TVFSEngine.VFSOpenEx(OpenFile: string): TVFSResult; begin Result := cVFS_OK; if @FSourcePlugin.FVFSOpen <> nil then Result := FSourcePlugin.FVFSOpen(FGlobs, PChar(OpenFile)); end; function TVFSEngine.VFSClose: boolean; begin Result := False; if @FSourcePlugin.FVFSClose <> nil then Result := FSourcePlugin.FVFSClose(FGlobs) = cVFS_OK; end; destructor TVFSEngine.Destroy; begin try if @FSourcePlugin.FVFSDestroy <> nil then FSourcePlugin.FVFSDestroy(FGlobs); libc_free(FGlobs); except on E: Exception do DebugMsg(['*** TVFSEngine.Destroy() -Exception: ', E.Message]); end; end; function TVFSEngine.GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; var P: PVFSItem; Item: PDataItem; i, Res: integer; begin DebugMsg(['^^VFS (II): GetListing begin']); Result := 0; try if @FSourcePlugin.FVFSListFirst = nil then Exit; P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); // DebugMsg(['Item = ', Int64(P)]); // DebugMsg(['FVFSListFirst']); Res := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), P); if Res <> cVFS_OK then begin FSourcePlugin.FVFSListClose(FGlobs); if Res = cVFS_Not_More_Files then Result := 0 else Result := Res; libc_free(P); Exit; end; repeat // DebugMsg(['begin--']); if AddDotFiles or (not ((Length(P^.sFileName) > 1) and (P^.sFileName[0] = '.') and (P^.sFileName[1] <> '.'))) then begin // DebugMsg(['Checkpoint 1']); Item := malloc(SizeOf(TDataItem)); memset(Item, 0, SizeOf(TDataItem)); // DebugMsg(['Checkpoint 2']); for i := 0 to Length(Item^.ColumnData) - 1 do Item^.ColumnData[i] := nil; // DebugMsg(['Checkpoint 3']); with Item^ do try FName := strdup(P^.sFileName); FDisplayName := StrToUTF8(P^.sFileName); if P^.sLinkTo <> nil then begin LnkPointTo := strdup(P^.sLinkTo); DebugMsg(['LnkPointTo = ', P^.sLinkTo]); end else LnkPointTo := nil; Mode := P^.iMode; // DebugMsg(['Checkpoint 4']); IsDotFile := (Length(FName) > 1) and (FName[0] = '.') and (FName[1] <> '.'); IsDir := TVFSItemType(P^.ItemType) = vDirectory; IsLnk := TVFSItemType(P^.ItemType) = vSymlink; IsBlk := TVFSItemType(P^.ItemType) = vBlockdev; IsChr := TVFSItemType(P^.ItemType) = vChardev; IsFIFO := TVFSItemType(P^.ItemType) = vFifo; IsSock := TVFSItemType(P^.ItemType) = vSock; // DebugMsg(['Checkpoint 5']); ModifyTime := P^.m_time; // DebugMsg(['Returned datetime: ', Longword(P^.m_time)]); // DebugMsg(['Checkpoint 6']); UID := P^.iUID; // DebugMsg(['Checkpoint 7']); GID := P^.iGID; // DebugMsg(['Checkpoint 8']); UpDir := False; // DebugMsg(['Checkpoint 9']); Selected := False; // DebugMsg(['Checkpoint 10']); Size := P^.iSize; // DebugMsg(['Checkpoint 11']); List.Add(Item); // DebugMsg(['Checkpoint 12']); except on E: Exception do DebugMsg(['^^VFS (EE): GetListing: Item-Exception: ', E.Message]); end; end; // of if AddDotFiles libc_free(P); // Not needed - just zero-erase the memory // DebugMsg(['Checkpoint 13']); P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); // DebugMsg(['Item = ', Int64(P)]); // DebugMsg(['Checkpoint 14']); // DebugMsg(['FVFSListNext --begin']); Res := FSourcePlugin.FVFSListNext(FGlobs, PChar(APath), P); // DebugMsg(['FVFSListNext --end']); // Sleep(500); until (Res <> cVFS_OK) or (BreakProcessingKind = 2); if BreakProcessingKind <> 0 then DebugMsg(['^^VFS (WW): GetListing: stopped by BreakProcessing']); libc_free(P); FSourcePlugin.FVFSListClose(FGlobs); if Res <> cVFS_Not_More_Files then Result := Res; except on E: Exception do DebugMsg(['^^VFS (EE): GetListing: Exception: ', E.Message]); end; BreakProcessingKind := 0; DebugMsg(['^^VFS (II): GetListing end.']); end; function TVFSEngine.GetListing(var List: TList; const AddDotFiles: boolean): integer; begin Result := GetListing(List, AddDotFiles, GetPath); end; function TVFSEngine.ExplicitChDir(const NewPath: string): integer; begin Result := libc_chdir(PChar(NewPath)); if Result <> 0 then Result := errno; end; function TVFSEngine.GetFileSystemSize: Int64; begin Result := GetFileSystemSize(GetPath); end; function TVFSEngine.GetFileSystemSize(const APath: string): Int64; begin if @FSourcePlugin.FVFSGetFileSystemSize <> nil then Result := FSourcePlugin.FVFSGetFileSystemSize(FGlobs, PChar(APath)) else Result := 0; end; function TVFSEngine.GetFileSystemFree: Int64; begin Result := GetFileSystemFree(GetPath); end; function TVFSEngine.GetFileSystemFree(const APath: string): Int64; begin if @FSourcePlugin.FVFSGetFileSystemFree <> nil then Result := FSourcePlugin.FVFSGetFileSystemFree(FGlobs, PChar(APath)) else Result := 0; end; procedure TVFSEngine.GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); begin FSSize := GetFileSystemSize(APath); FSFree := GetFileSystemFree(APath); FSName := 'plugin'; end; function TVFSEngine.IsOnROMedium(const FileName: string): boolean; begin Result := True; end; function TVFSEngine.FileCanRun(const FileName: string): boolean; var Item: PDataItemSL; begin Item := GetFileInfoSL(FileName); Result := Assigned(Item) and Item^.IsExecutable; FreeDataItem(Item); end; function TVFSEngine.GetPrefix: string; begin if @FSourcePlugin.FVFSGetPrefix <> nil then Result := URIHidePassword(FSourcePlugin.FVFSGetPrefix(FGlobs)) else Result := 'VFS'; end; function TVFSEngine.GetPath: string; begin if @FSourcePlugin.FVFSGetPath <> nil then Result := FSourcePlugin.FVFSGetPath(FGlobs) else Result := '/'; end; function TVFSEngine.ChangeDir(const NewPath: string; const ShowProgress: boolean = True): integer; begin DebugMsg(['^^VFS (II): ChangeDir begin']); Result := 0; try Result := FSourcePlugin.FVFSChangeDir(FGlobs, PChar(NewPath)); // Sleep(3000); except on E: Exception do DebugMsg(['^^VFS (EE): ChangeDir: Exception: ', E.Message]); end; DebugMsg(['^^VFS (II): ChangeDir end.']); end; procedure TVFSEngine.SetPath(Value: string); begin ChangeDir(Value); end; function TVFSEngine.Login(Username, Password: string): integer; begin if @FSourcePlugin.FVFSLogin <> nil then Result := FSourcePlugin.FVFSLogin(FGlobs, PChar(Username), PChar(Password)) else Result := cVFS_OK; end; function TVFSEngine.FileExists(const FileName: string; const Use_lstat: boolean = False): Boolean; begin if @FSourcePlugin.FVFSFileExists <> nil then Result := FSourcePlugin.FVFSFileExists(FGlobs, PChar(FileName), Use_lstat) else Result := False; end; function TVFSEngine.DirectoryExists(const FileName: string; const Use_lstat: boolean = False): Boolean; var P: PVFSItem; Res: integer; begin if @FSourcePlugin.FVFSFileExists <> nil then begin Result := FSourcePlugin.FVFSFileExists(FGlobs, PChar(FileName), Use_lstat); if Result and (@FSourcePlugin.FVFSFileInfo <> nil) then begin P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(FileName), P); if (Res <> cVFS_OK) or (P = nil) or (TVFSItemType(P^.ItemType) <> vDirectory) then Result := False; libc_free(P); end; end else Result := False; end; function TVFSEngine.GetFileInfoSL(APath: string): PDataItemSL; var P: PVFSItem; Item: PDataItemSL; Res: integer; begin Result := nil; if @FSourcePlugin.FVFSFileInfo = nil then Exit; P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P); if Res <> cVFS_OK then begin DebugMsg(['*** VFSFileInfo(', APath, ') failed. Code = ', Res]); Exit; end; try Item := malloc(SizeOf(TDataItemSL)); memset(Item, 0, SizeOf(TDataItemSL)); with Item^ do begin FName := strdup(P^.sFileName); FDisplayName := StrToUTF8(P^.sFileName); if P^.sLinkTo <> nil then LnkPointTo := strdup(P^.sLinkTo) else LnkPointTo := nil; ADestination := nil; Stage1 := True; Level := 0; IsDir := TVFSItemType(P^.ItemType) = vDirectory; IsLnk := TVFSItemType(P^.ItemType) = vSymlink; ForceMove := False; {***} IsOnRO := True; IsExecutable := P^.iMode and S_IXUSR = S_IXUSR; Mode := P^.iMode; ModifyTime := P^.m_time; mtime := P^.m_time; atime := P^.a_time; UID := P^.iUID; GID := P^.iGID; Size := P^.iSize; libc_free(P); end; Result := Item; except on E: Exception do DebugMsg(['*** TVFSEngine.GetFileInfoSL(APath=', APath, ') -Exception: ', E.Message]); end; end; function TVFSEngine.MakeDir(const NewDir: string): integer; begin if @FSourcePlugin.FVFSMkDir <> nil then Result := FSourcePlugin.FVFSMkDir(FGlobs, PChar(NewDir)) else Result := cVFS_Failed; end; function TVFSEngine.Remove(APath: string): integer; begin if @FSourcePlugin.FVFSRemove <> nil then Result := FSourcePlugin.FVFSRemove(FGlobs, PChar(APath)) else Result := cVFS_Failed; end; function TVFSEngine.RenameFile(SourceFile, DestFile: string): integer; begin if @FSourcePlugin.FVFSRename <> nil then Result := FSourcePlugin.FVFSRename(FGlobs, PChar(SourceFile), PChar(DestFile)) else Result := cVFS_Failed; end; procedure TVFSEngine.FillDirFiles(APath: string; List: TList; ALevel: word); var Item: PDataItemSL; i, Res: integer; FilesList: TList; LocalList: TStringList; P: PVFSItem; procedure AddEntry(FPath: string; AddCurrDirStage, AStage1: boolean); begin Item := malloc(SizeOf(TDataItemSL)); memset(Item, 0, SizeOf(TDataItemSL)); with Item^ do begin // AName := malloc(Length(FPath) + 1); // memset(AName, 0, Length(FPath) + 1); FName := strdup(PChar(FPath)); FDisplayName := StrToUTF8(PChar(FPath)); if P^.sLinkTo <> nil then LnkPointTo := strdup(P^.sLinkTo) else LnkPointTo := nil; ADestination := nil; Stage1 := AStage1; IsDir := TVFSItemType(P^.ItemType) = vDirectory; IsLnk := TVFSItemType(P^.ItemType) = vSymlink; if IsLnk and AddCurrDirStage then DebugMsg(['*** Assertion failed AddEntry: Item^.IsLnk = True']); ForceMove := False; {***} IsOnRO := True; IsExecutable := AddCurrDirStage or (P^.iMode and S_IXUSR = S_IXUSR); Mode := P^.iMode; ModifyTime := P^.m_time; mtime := P^.m_time; atime := P^.a_time; UID := P^.iUID; GID := P^.iGID; Size := P^.iSize; Level := ALevel + Ord(not AddCurrDirStage); end; if AddCurrDirStage then List.Add(Item) else FilesList.Add(Item); end; begin if not Assigned(List) then Exit; FilesList := TList.Create; LocalList := TStringList.Create; try try P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P); if Res <> cVFS_OK then DebugMsg(['*** FillDirFiles - VFSFileInfo(', APath, ') failed. Code = ', Res]); AddEntry(APath, True, True); libc_free(P); APath := IncludeTrailingPathDelimiter(APath); if @FSourcePlugin.FVFSChangeDir <> nil then Res := FSourcePlugin.FVFSChangeDir(FGlobs, PChar(APath)) else Exit; if Res <> 0 then Exit; if @FSourcePlugin.FVFSListFirst = nil then Exit; P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), P); if Res <> cVFS_OK then begin FSourcePlugin.FVFSListClose(FGlobs); libc_free(P); Exit; end; repeat if TVFSItemType(P^.ItemType) = vDirectory then LocalList.Add(APath + String(P^.sFileName)) else AddEntry(APath + String(P^.sFileName), False, True); libc_free(P); P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSListNext(FGlobs, PChar(GetPath), P); until (Res <> cVFS_OK); libc_free(P); FSourcePlugin.FVFSListClose(FGlobs); if LocalList.Count > 0 then for i := 0 to LocalList.Count - 1 do FillDirFiles(LocalList[i], List, ALevel + 1); if FilesList.Count > 0 then for i := 0 to FilesList.Count - 1 do List.Add(FilesList[i]); except on E: Exception do DebugMsg(['*** TVFSEngine.FillDirFiles(APath=', APath, ', Level=', ALevel, ') -Exception: ', E.Message]); end; finally P := malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P); if Res <> cVFS_OK then DebugMsg(['*** FillDirFiles - VFSFileInfo(', APath, ') failed. Code = ', Res]); AddEntry(APath, True, False); libc_free(P); LocalList.Free; FilesList.Free; end; end; function TVFSEngine.MakeSymLink(const NewFileName, PointTo: string): integer; begin if @FSourcePlugin.FVFSMakeSymLink <> nil then Result := FSourcePlugin.FVFSMakeSymLink(FGlobs, PChar(NewFileName), PChar(PointTo)) else Result := cVFS_Failed; end; function TVFSEngine.Chmod(const FileName: string; const Mode: integer): integer; begin if @FSourcePlugin.FVFSChmod <> nil then Result := FSourcePlugin.FVFSChmod(FGlobs, PChar(FileName), Mode) else Result := cVFS_Failed; end; function TVFSEngine.Chown(const FileName: string; const UID, GID: integer): integer; begin if @FSourcePlugin.FVFSChown <> nil then Result := FSourcePlugin.FVFSChown(FGlobs, PChar(FileName), UID, GID) else Result := cVFS_Failed; end; function TVFSEngine.ChangeTimes(APath: string; mtime, atime: Int64): integer; begin if @FSourcePlugin.FVFSChangeTimes <> nil then Result := FSourcePlugin.FVFSChangeTimes(FGlobs, PChar(APath), mtime, atime) else Result := cVFS_Failed; end; function TVFSEngine.GetDirSize(APath: string): Int64; begin if @FSourcePlugin.FVFSGetDirSize <> nil then Result := FSourcePlugin.FVFSGetDirSize(FGlobs, PChar(APath)) else Result := 0; end; procedure TVFSEngine.BreakProcessing(ProcessingKind: integer); begin case ProcessingKind of 1: if @FSourcePlugin.FVFSBreakGetDirSize <> nil then FSourcePlugin.FVFSBreakGetDirSize(FGlobs); end; end; function TVFSEngine.GetBlockSize: Cardinal; begin Result := FBlockSize; end; procedure TVFSEngine.SetBlockSize(Value: Cardinal); begin if @FSourcePlugin.FVFSSetBlockSize <> nil then FSourcePlugin.FVFSSetBlockSize(FGlobs, Value); end; (********************************************************************************************************************************) function TVFSEngine.IsOnSameFS(const Path1, Path2: string): boolean; begin if @FSourcePlugin.FVFSIsOnSameFS <> nil then Result := FSourcePlugin.FVFSIsOnSameFS(FGlobs, PChar(Path1), PChar(Path2)) else Result := True; end; function TVFSEngine.OpenFile(const APath: string; Mode: integer; var Error: integer): TEngineFileDes; var i: integer; begin if @FSourcePlugin.FVFSOpenFile <> nil then begin Result := FSourcePlugin.FVFSOpenFile(FGlobs, PChar(APath), Mode, @i); Error := i; end else Result := nil; end; function TVFSEngine.ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; var Error: integer): integer; var i: integer; begin if @FSourcePlugin.FVFSReadFile <> nil then begin Result := FSourcePlugin.FVFSReadFile(FGlobs, FileDescriptor, Buffer, ABlockSize, @i); Error := i; end else Result := -1; end; function TVFSEngine.WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; var Error: integer): integer; var i: integer; begin if @FSourcePlugin.FVFSWriteFile <> nil then begin Result := FSourcePlugin.FVFSWriteFile(FGlobs, FileDescriptor, Buffer, BytesCount, @i); Error := i; end else Result := -1; end; function TVFSEngine.CloseFile(const FileDescriptor: TEngineFileDes): integer; begin if @FSourcePlugin.FVFSCloseFile <> nil then Result := FSourcePlugin.FVFSCloseFile(FGlobs, FileDescriptor) else Result := 0; end; function TVFSEngine.FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; var Error: integer): Int64; var i: integer; begin if @FSourcePlugin.FVFSFileSeek <> nil then begin Result := FSourcePlugin.FVFSFileSeek(FGlobs, FileDescriptor, AbsoluteOffset, @i); Error := i; end else Result := -1; end; function TVFSEngine.TwoSameFiles(const Path1, Path2: string): boolean; begin if @FSourcePlugin.FVFSTwoSameFiles <> nil then Result := FSourcePlugin.FVFSTwoSameFiles(FGlobs, PChar(Path1), PChar(Path2)) else Result := False; end; (********************************************************************************************************************************) function VFSCopyCallBackFunc(iPos, iMax:Int64; data: Pointer): LongBool; cdecl; begin // DebugMsg(['VFSCopyCallBackFunc called (iPos = ', iPos, ', iMax = ', iMax, ')']); Result := True; if not Assigned(data) then Exit; if Assigned(TVFSEngine(data).FProgressFunc) then try Result := TVFSEngine(data).FProgressFunc(TVFSEngine(data).FSenderThread, iPos); except on E: Exception do DebugMsg(['*** Exception raised in VFSCopyCallBackFunc(iPos=', iPos, ', iMax=', iMax, ', data=', data, '): (', E.ClassName, '): ', E.Message]); end; end; function TVFSEngine.CopyFileOut(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; var Res: TVFSResult; begin try if @FSourcePlugin.FVFSCopyOut <> nil then begin FSenderThread := Sender; FProgressFunc := ProgressFunc; // DebugMsg(['0 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); // DebugMsg([' Pointer(FGlobs) = 0x', IntToHex(Int64(FGlobs), 16), ', Pointer(@NewVFSCopyCallBackFunc) = 0x', IntToHex(Int64(@NewVFSCopyCallBackFunc), 16), ', Pointer(Self) = 0x', IntToHex(Int64(Self), 16)]); try Res := FSourcePlugin.FVFSCopyOut(FGlobs, PChar(SourceFile), PChar(DestFile), @VFSCopyCallBackFunc, Self, Append); except on E: Exception do begin DebugMsg(['*** Exception raised in TVFSEngine.CopyFileOut(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); Res := cVFS_WriteErr; end; end; // DebugMsg(['1 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); Result := Res = cVFS_OK; if (Res <> cVFS_OK) and Assigned(ErrorFunc) then case Res of cVFS_ReadErr: Result := ErrorFunc(Sender, 6, 0, SourceFile); cVFS_WriteErr: Result := ErrorFunc(Sender, 7, 0, DestFile); cVFS_mallocFailed: ErrorFunc(Sender, 1, 0, SourceFile); cVFS_Cancelled: Result := False; end; end else Result := False; except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.CopyFileOut(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); end; end; function TVFSEngine.CopyFileIn(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; var Res: TVFSResult; begin try if @FSourcePlugin.FVFSCopyIn <> nil then begin FSenderThread := Sender; FProgressFunc := ProgressFunc; try Res := FSourcePlugin.FVFSCopyIn(FGlobs, PChar(SourceFile), PChar(DestFile), @VFSCopyCallBackFunc, Self, Append); except on E: Exception do begin DebugMsg(['*** Exception raised in TVFSEngine.CopyFileIn(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); Res := cVFS_WriteErr; end; end; Result := Res = cVFS_OK; if (Res <> cVFS_OK) and Assigned(ErrorFunc) then case Res of cVFS_ReadErr: Result := ErrorFunc(Sender, 6, 0, SourceFile); cVFS_WriteErr: Result := ErrorFunc(Sender, 7, 0, DestFile); cVFS_mallocFailed: ErrorFunc(Sender, 1, 0, SourceFile); cVFS_Cancelled: Result := False; end; end else Result := False; except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.CopyFileIn(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TVFSEngine.SetPassword(Password: string): integer; begin if @FSourcePlugin.FVFSSetPassword <> nil then Result := FSourcePlugin.FVFSSetPassword(FGlobs, PChar(Password)) else Result := 0; FPassword := Password; end; function TVFSEngine.GetPasswordRequired: boolean; begin if @FSourcePlugin.FVFSGetPasswordRequired <> nil then Result := FSourcePlugin.FVFSGetPasswordRequired(FGlobs) else Result := False; end; procedure TVFSEngine.SetFPassword(Value: string); begin SetPassword(Value); end; (********************************************************************************************************************************) (********************************************************************************************************************************) function GetBinaryPath: string; var i: integer; Buf: array[0..1024] of char; begin Result := ParamStr(0); // Fallback try DebugMsg(['Trying to determine binary path...']); DebugMsg(['Fallback path = ', Result]); Buf[0] := #0; FillChar(Buf, 1025, 0); i := readlink(PChar(Format('/proc/%d/exe', [getpid])), @Buf, 1024); if i < 1 then begin DebugMsg(['Something went wrong during readlink: ', string(strerror(errno))]); Exit; end; Buf[i] := #0; Result := string(PChar(@Buf)); DebugMsg(['Path to executable = ', Result]); except on E: Exception do DebugMsg(['*** Exception raised in GetBinaryPath: (', E.ClassName, '): ', E.Message]); end; end; procedure QueryModules(APath: string); var Handle: PDIR; DirEnt: PDirent64; s: string; PluginItem: TVFSPlugin; ModuleHandler: Pointer; VFSVersionFunc: TVFSVersion; i: integer; Buf: PChar; b: boolean; begin if Length(APath) < 1 then Exit; try try APath := ReplaceStr(APath, '~', GetHomePath); DebugMsg(['------------------ Querying modules in ', APath, ' ---']); // First change to the program starting directory if APath[1] <> '/' then begin s := ExtractFilePath(BinaryPath); DebugMsg(['----> Changing dir to ', s]); i := libc_chdir(PChar(s)); if i <> 0 then DebugMsg(['Something went wrong during chdir: ', string(strerror(errno))]); s := get_current_dir_name; DebugMsg([' [II] Changed dir to ', s]); end; // Change to the required directory s := APath; DebugMsg(['----> Changing dir to ', s]); i := libc_chdir(PChar(s)); if i <> 0 then begin DebugMsg(['Something went wrong during chdir: ', string(strerror(errno))]); Exit; end; s := get_current_dir_name; DebugMsg([' [II] Changed dir to ', s, ', trying to list the files...']); // Going to read the directory and search for the files... Handle := opendir(PChar(s)); if not Assigned(Handle) then begin DebugMsg(['Something went wrong during opendir: ', string(strerror(errno))]); Exit; end; repeat DirEnt := readdir64(Handle); if Assigned(DirEnt) then begin Buf := PChar(@DirEnt^.d_name[0]); if Assigned(Buf) and (strcmp(Buf, '.') <> 0) and (strcmp(Buf, '..') <> 0) then begin DebugMsg(['$$$ Found ', Buf, ', trying to load...']); // Try to find the plugin in the plugin list to prevent multiple plugin loading b := False; if PluginList.Count > 0 then for i := 0 to PluginList.Count - 1 do if CompareStr(TVFSPlugin(PluginList[i]).FullPath, IncludeTrailingPathDelimiter(s) + Buf) = 0 then begin b := True; Break; end; ModuleHandler := nil; if b then DebugMsg(['Module ', s, ' is already loaded --> skipping...']) else begin ModuleHandler := dlopen(PChar(IncludeTrailingPathDelimiter(s) + Buf), RTLD_LAZY); if ModuleHandler = nil then DebugMsg([' XXX Error loading module: ', dlerror]) else try @VFSVersionFunc := dlsym(ModuleHandler, 'VFSVersion'); if (@VFSVersionFunc <> nil) and (VFSVersionFunc >= ConstVFSVersionRequired) then begin PluginItem := TVFSPlugin.Create(ModuleHandler); PluginItem.FullPath := IncludeTrailingPathDelimiter(s) + Buf; PluginList.Add(PluginItem); end else DebugMsg([' $XXX: Error getting version info or version mismatch']); except end; end; end; end; until DirEnt = nil; closedir(Handle); except on E: Exception do DebugMsg(['*** Exception raised in QueryModules(APath = ', APath, '): (', E.ClassName, '): ', E.Message]); end; finally DebugMsg(['------------------ Done querying modules ---------------']); end; end; procedure UnloadModules; var i: integer; begin if Assigned(PluginList) and (PluginList.Count > 0) then for i := PluginList.Count - 1 downto 0 do try dlclose(TVFSPlugin(PluginList[i]).ModuleHandle); TVFSPlugin(PluginList[i]).Free; PluginList.Delete(i); except on E: Exception do DebugMsg(['*** Exception raised in UnloadModules(i = ', i, '): (', E.ClassName, '): ', E.Message]); end; end; procedure DoInitPlugins; begin PluginList := TList.Create; if not ParamDisablePlugins then begin BinaryPath := GetBinaryPath; QueryModules(ConstGlobalModulePath1); QueryModules(ConstGlobalModulePath2); QueryModules(ConstLocalModulePath1); QueryModules(ConstLocalModulePath2); QueryModules(ConstLocalModulePath3); end; end; initialization finalization UnloadModules; PluginList.Free; end.