(* 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, glib2, Classes, uVFSprototypes, UEngines, UCoreUtils; type TVFSPlugin = class private FVFSNew: TVFSNew; FVFSFree: TVFSFree; FVFSVersion: TVFSVersion; FVFSGetInfo: TVFSGetInfo; FVFSOpenArchive: TVFSOpenArchive; FVFSOpenURI: TVFSOpenURI; FVFSClose: TVFSClose; FVFSListFirst: TVFSListFirst; FVFSListNext: TVFSListNext; FVFSListClose: TVFSListClose; FVFSChangeDir: TVFSChangeDir; FVFSGetPath: TVFSGetPath; FVFSGetPathURI: TVFSGetPathURI; FVFSGetFileSystemInfo: TVFSGetFileSystemInfo; 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; FVFSCopyToLocal: TVFSCopyToLocal; FVFSCopyFromLocal: TVFSCopyFromLocal; FVFSIsOnSameFS: TVFSIsOnSameFS; FVFSTwoSameFiles: TVFSTwoSameFiles; FVFSGetArchiveExts: TVFSGetArchiveExts; FVFSGetNetworkServices: TVFSGetNetworkServices; FVFSGetPasswordRequired: TVFSGetPasswordRequired; FVFSSetCallbacks: TVFSSetCallbacks; FVFSResetPassword: TVFSResetPassword; FVFSStartCopyOperation: TVFSStartCopyOperation; FVFSStopCopyOperation: TVFSStopCopyOperation; function GetHandlesArchives: boolean; function GetHandlesNetwork: boolean; public ModuleHandle: Pointer; FullModulePath: 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 ModuleID: string; function ModuleName: string; function ModuleAbout: string; function ModuleCopyright: string; published property HandlesArchives: boolean read GetHandlesArchives; property HandlesNetwork: boolean read GetHandlesNetwork; end; TVFSEngine = class(TPanelEngine) private BreakProcessingType: integer; FGlobs: Pointer; FSourcePlugin: TVFSPlugin; FBlockSize: Cardinal; FArchiveMode: boolean; FArchivePath: string; function GetPluginID: string; function GetDataItemFromVFSItem(P: PVFSItem): PDataItem; public Password: string; PasswordUsed: boolean; RemoveFileOnClose: string; OpenedFromQuickConnect: boolean; CustomPluginIDSave: string; constructor Create(SourcePlugin: TVFSPlugin); destructor Destroy; override; function GetListing(List: TList; const APath: string; AddDotFiles, FollowSymlinks, AddFullPath: boolean): integer; override; function GetFileInfo(const APath: string; FollowSymlinks, AddFullPath: boolean): PDataItem; override; function ChangeDir(const NewPath: string): integer; override; function GetPath: string; override; procedure SetPath(Value: string); override; function GetDirSize(const APath: string): Int64; override; procedure BreakProcessing(ProcessingKind: integer); override; function FileExists(const FileName: string; FollowSymlinks: boolean): boolean; override; function DirectoryExists(const FileName: string; FollowSymlinks: boolean): boolean; override; procedure GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); override; function IsOnROMedium(const FileName: string): boolean; override; function FileCanRun(const FileName: string): boolean; override; function MakeDir(const NewDir: string): integer; override; function Remove(const APath: string): integer; override; function MakeSymLink(const NewFileName, PointTo: string): integer; override; function Chmod(const FileName: string; Mode: cuLong): integer; override; function Chown(const FileName: string; UID, GID: cuLong): integer; override; function RenameFile(const SourceFile, DestFile: string): integer; override; function ChangeTimes(const APath: string; mtime, atime: time_t): integer; override; function GetBlockSize: guint32; override; procedure SetBlockSize(Value: guint32); override; function CopyFileIn(Sender: Pointer; const SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; override; function CopyFileOut(Sender: Pointer; const SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; override; function IsOnSameFS(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; function TwoSameFiles(const Path1, Path2: string; FollowSymlinks: boolean): boolean; 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; // VFS additions function VFSOpenURI(const URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function VFSOpenEx(const OpenFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): TVFSResult; function VFSClose: boolean; function ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): integer; function GetPathURI: string; function GetPasswordRequired: boolean; procedure ResetPassword; // callbacks here are used for next volume prompts, password prompts (encrypted archives) function StartCopyOperation(Sender: Pointer; ErrorFunc: TEngineErrorFunc; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function StopCopyOperation(Sender: Pointer; ErrorFunc: TEngineErrorFunc): boolean; function CopyFileInEx(Sender: Pointer; const SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; function CopyFileOutEx(Sender: Pointer; const SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; published property Path: string read GetPath write SetPath; property BlockSize: Cardinal read GetBlockSize write SetBlockSize; property PluginID: string read GetPluginID; property ArchiveMode: boolean read FArchiveMode; property ArchivePath: string read FArchivePath; 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'; ConstGlobalModulePath3 = '/usr/lib64/tuxcmd'; ConstGlobalModulePath4 = '/usr/local/lib64/tuxcmd'; ConstLocalModulePath4 = '../lib64/tuxcmd'; ConstVFSVersionRequired = cVFSVersion; var BinaryPath: string; procedure VFSLogFunc(const s: PChar); cdecl; begin DebugMsg([' ### VFS ###: ', s]); end; (********************************************************************************************************************************) constructor TVFSPlugin.Create(PluginHandle: Pointer); var s: PChar; begin inherited Create; ModuleHandle := PluginHandle; // Find the symbols @FVFSNew := dlsym(ModuleHandle, 'VFSNew'); @FVFSFree := dlsym(ModuleHandle, 'VFSFree'); @FVFSVersion := dlsym(ModuleHandle, 'VFSVersion'); @FVFSGetInfo := dlsym(ModuleHandle, 'VFSGetInfo'); @FVFSOpenArchive := dlsym(ModuleHandle, 'VFSOpenArchive'); @FVFSOpenURI := dlsym(ModuleHandle, 'VFSOpenURI'); @FVFSClose := dlsym(ModuleHandle, 'VFSClose'); @FVFSListFirst := dlsym(ModuleHandle, 'VFSListFirst'); @FVFSListNext := dlsym(ModuleHandle, 'VFSListNext'); @FVFSListClose := dlsym(ModuleHandle, 'VFSListClose'); @FVFSGetPath := dlsym(ModuleHandle, 'VFSGetPath'); @FVFSGetPathURI := dlsym(ModuleHandle, 'VFSGetPathURI'); @FVFSChangeDir := dlsym(ModuleHandle, 'VFSChangeDir'); @FVFSGetFileSystemInfo := dlsym(ModuleHandle, 'VFSGetFileSystemInfo'); @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'); @FVFSCopyToLocal := dlsym(ModuleHandle, 'VFSCopyToLocal'); @FVFSCopyFromLocal := dlsym(ModuleHandle, 'VFSCopyFromLocal'); @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'); @FVFSGetArchiveExts := dlsym(ModuleHandle, 'VFSGetArchiveExts'); @FVFSGetNetworkServices := dlsym(ModuleHandle, 'VFSGetNetworkServices'); @FVFSGetPasswordRequired := dlsym(ModuleHandle, 'VFSGetPasswordRequired'); @FVFSSetCallbacks := dlsym(ModuleHandle, 'VFSSetCallbacks'); @FVFSResetPassword := dlsym(ModuleHandle, 'VFSResetPassword'); @FVFSStartCopyOperation := dlsym(ModuleHandle, 'VFSStartCopyOperation'); @FVFSStopCopyOperation := dlsym(ModuleHandle, 'VFSStopCopyOperation'); // Initialize the extensions list SetLength(Extensions, 0); SetLength(Services, 0); if (@FVFSGetArchiveExts <> nil) and (@FVFSOpenArchive <> nil) then begin s := FVFSGetArchiveExts; if s <> nil then begin ParseString(String(s), ';', Extensions); real_libc_free(s); end; end; if (@FVFSGetNetworkServices <> nil) and (@FVFSOpenURI <> nil) then begin s := FVFSGetNetworkServices; if s <> nil then begin ParseString(String(s), ';', Services); real_libc_free(s); end; end; end; destructor TVFSPlugin.Destroy; begin SetLength(Extensions, 0); SetLength(Services, 0); if ModuleHandle <> nil then dlclose(ModuleHandle); inherited Destroy; end; (********************************************************************************************************************************) function TVFSPlugin.VFSVersion: integer; begin if @FVFSVersion <> nil then Result := FVFSVersion else Result := -1; end; procedure _free_PVFSInfo(Info: PVFSInfo); begin if Info <> nil then begin if Info^.Name <> nil then real_libc_free(Info^.Name); if Info^.ID <> nil then real_libc_free(Info^.ID); if Info^.About <> nil then real_libc_free(Info^.About); if Info^.Copyright <> nil then real_libc_free(Info^.Copyright); end; end; function TVFSPlugin.ModuleID: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.ID); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.ModuleName: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.Name); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.ModuleAbout: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.About); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.ModuleCopyright: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.Copyright); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.GetHandlesArchives: boolean; begin Result := (Length(Extensions) > 0) and (@FVFSOpenArchive <> nil); end; function TVFSPlugin.GetHandlesNetwork: boolean; begin Result := (Length(Services) > 0) and (@FVFSOpenURI <> nil); end; (********************************************************************************************************************************) (********************************************************************************************************************************) constructor TVFSEngine.Create(SourcePlugin: TVFSPlugin); begin inherited Create; BreakProcessingType := 0; FSourcePlugin := SourcePlugin; FBlockSize := 65536; FArchiveMode := False; FArchivePath := ''; FGlobs := nil; Password := ''; PasswordUsed := False; RemoveFileOnClose := ''; OpenedFromQuickConnect := False; CustomPluginIDSave := ''; if @FSourcePlugin.FVFSNew <> nil then FGlobs := FSourcePlugin.FVFSNew(@VFSLogFunc); end; destructor TVFSEngine.Destroy; begin try if @FSourcePlugin.FVFSFree <> nil then FSourcePlugin.FVFSFree(FGlobs); if Length(Trim(RemoveFileOnClose)) > 0 then if unlink(PChar(RemoveFileOnClose)) <> 0 then DebugMsg(['(EE) TVFSEngine.Destroy: error while removing ''', RemoveFileOnClose, ''': ', strerror(errno)]); except on E: Exception do DebugMsg(['*** TVFSEngine.Destroy() -Exception: ', E.Message]); end; end; function TVFSEngine.VFSOpenURI(const URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; begin Result := False; if (FGlobs <> nil) and (@FSourcePlugin.FVFSOpenURI <> nil) then begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := FSourcePlugin.FVFSOpenURI(FGlobs, PChar(URI)) = cVFS_OK; FArchiveMode := False; FArchivePath := ''; if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; end; function TVFSEngine.VFSOpenEx(const OpenFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): TVFSResult; begin Result := cVFS_OK; if (FGlobs <> nil) and (@FSourcePlugin.FVFSOpenArchive <> nil) then begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := FSourcePlugin.FVFSOpenArchive(FGlobs, PChar(OpenFile)); FArchiveMode := True; if Result = cVFS_OK then FArchivePath := OpenFile else FArchivePath := ''; if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; end; (********************************************************************************************************************************) function TVFSEngine.VFSClose: boolean; begin Result := False; if (FGlobs <> nil) and (@FSourcePlugin.FVFSClose <> nil) then Result := FSourcePlugin.FVFSClose(FGlobs) = cVFS_OK; end; function TVFSEngine.GetDataItemFromVFSItem(P: PVFSItem): PDataItem; var Item: PDataItem; begin Item := malloc(sizeof(TDataItem)); memset(Item, 0, sizeof(TDataItem)); Item^.UpDir := False; Item^.Selected := False; Item^.FName := g_strdup(P^.FName); Item^.FDisplayName := g_strdup(P^.FDisplayName); Item^.LnkPointTo := g_strdup(P^.sLinkTo); Item^.Mode := P^.iMode; Item^.IsDotFile := (Length(P^.FName) > 1) and (P^.FName[0] = '.'); Item^.IsExecutable := (P^.iMode and S_IXUSR) = S_IXUSR; Item^.IsDir := TVFSItemType(P^.ItemType) = vDirectory; Item^.IsLnk := P^.IsLink; Item^.IsBlk := TVFSItemType(P^.ItemType) = vBlockdev; Item^.IsChr := TVFSItemType(P^.ItemType) = vChardev; Item^.IsFIFO := TVFSItemType(P^.ItemType) = vFifo; Item^.IsSock := TVFSItemType(P^.ItemType) = vSock; Item^.mtime := P^.m_time; Item^.atime := P^.a_time; Item^.ctime := P^.c_time; Item^.UID := P^.iUID; Item^.GID := P^.iGID; Item^.Size := P^.iSize; Item^.PackedSize := P^.iPackedSize; Item^.inode_no := P^.inode_no; Result := Item; end; function TVFSEngine.GetListing(List: TList; const APath: string; AddDotFiles, FollowSymlinks, AddFullPath: boolean): integer; var P: PVFSItem; Item: PDataItem; Res: integer; begin DebugMsg(['^^VFS (II): GetListing begin']); Result := 0; try if @FSourcePlugin.FVFSListFirst = nil then begin Result := ERRNoAccess; Exit; end; P := real_libc_malloc(sizeof(TVFSItem)); memset(P, 0, sizeof(TVFSItem)); Res := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), P, FollowSymlinks, AddFullPath); if Res <> cVFS_OK then begin FSourcePlugin.FVFSListClose(FGlobs); if Res = cVFS_Not_More_Files then Result := 0 else Result := Res; real_libc_free(P); Exit; end; repeat if (strlen(P^.FName) > 0) and (AddDotFiles or (P^.FName[0] <> '.')) then begin Item := GetDataItemFromVFSItem(P); List.Add(Item); end; if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); memset(P, 0, sizeof(TVFSItem)); Res := FSourcePlugin.FVFSListNext(FGlobs, P); until (Res <> cVFS_OK) or (BreakProcessingType = 2); if BreakProcessingType <> 0 then DebugMsg(['^^VFS (WW): GetListing: stopped by BreakProcessing']); real_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; BreakProcessingType := 0; DebugMsg(['^^VFS (II): GetListing end.']); end; function TVFSEngine.GetFileInfo(const APath: string; FollowSymlinks, AddFullPath: boolean): PDataItem; var P: PVFSItem; Res: integer; begin DebugMsg(['^^VFS (II): GetFileInfo begin']); Result := nil; if @FSourcePlugin.FVFSFileInfo = nil then Exit; try P := real_libc_malloc(sizeof(TVFSItem)); memset(P, 0, sizeof(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P, FollowSymlinks, AddFullPath); if Res = cVFS_OK then Result := GetDataItemFromVFSItem(P); if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); except on E: Exception do DebugMsg(['^^VFS (EE): GetFileInfo: Exception: ', E.Message]); end; DebugMsg(['^^VFS (II): GetFileInfo end.']); end; (********************************************************************************************************************************) procedure TVFSEngine.GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); var AFSSize, AFSFree: Int64; AFSName: PChar; begin FSSize := -1; FSFree := -1; FSName := 'plugin'; if @FSourcePlugin.FVFSGetFileSystemInfo <> nil then begin AFSSize := -1; AFSFree := -1; AFSName := nil; if FSourcePlugin.FVFSGetFileSystemInfo(FGlobs, PChar(APath), @AFSSize, @AFSFree, @AFSName) = cVFS_OK then begin FSSize := AFSSize; FSFree := AFSFree; if AFSName <> nil then begin FSName := string(AFSName); real_libc_free(AFSName); end; end; end; end; function TVFSEngine.IsOnROMedium(const FileName: string): boolean; begin Result := FArchiveMode; end; function TVFSEngine.FileCanRun(const FileName: string): boolean; var Item: PDataItem; begin Item := GetFileInfo(FileName, True, True); Result := (Item <> nil) and Item^.IsExecutable; FreeDataItem(Item); end; function TVFSEngine.GetPath: string; var s: PChar; begin Result := '/'; if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetPath <> nil) then begin s := FSourcePlugin.FVFSGetPath(FGlobs); if s <> nil then begin Result := String(s); real_libc_free(s); end; end; end; function TVFSEngine.GetPathURI: string; var s: PChar; begin Result := ''; if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetPathURI <> nil) then begin s := FSourcePlugin.FVFSGetPathURI(FGlobs); if s <> nil then begin Result := String(s); real_libc_free(s); end; end; end; function TVFSEngine.ChangeDir(const NewPath: string): 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; function TVFSEngine.ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): integer; begin Result := ERRNoAccess; if (FGlobs <> nil) and (@FSourcePlugin.FVFSChangeDir <> nil) then begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := ChangeDir(NewPath); if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; end; procedure TVFSEngine.SetPath(Value: string); begin ChangeDir(Value); end; function TVFSEngine.FileExists(const FileName: string; FollowSymlinks: boolean): boolean; var P: PVFSItem; begin Result := False; if (FGlobs = nil) or (@FSourcePlugin.FVFSFileInfo = nil) then Exit; try P := real_libc_malloc(sizeof(TVFSItem)); memset(P, 0, sizeof(TVFSItem)); Result := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(FileName), P, FollowSymlinks, False) = cVFS_OK; real_libc_free(P); except on E: Exception do DebugMsg(['^^VFS (EE): FileExists: Exception: ', E.Message]); end; end; function TVFSEngine.DirectoryExists(const FileName: string; FollowSymlinks: boolean): boolean; var P: PVFSItem; Res: integer; begin Result := False; if (FGlobs = nil) or (@FSourcePlugin.FVFSFileInfo = nil) then Exit; try P := real_libc_malloc(sizeof(TVFSItem)); memset(P, 0, sizeof(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(FileName), P, FollowSymlinks, False); Result := (Res = cVFS_OK) and (TVFSItemType(P^.ItemType) = vDirectory); real_libc_free(P); except on E: Exception do DebugMsg(['^^VFS (EE): FileExists: 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(const APath: string): integer; begin if @FSourcePlugin.FVFSRemove <> nil then Result := FSourcePlugin.FVFSRemove(FGlobs, PChar(APath)) else Result := cVFS_Failed; end; function TVFSEngine.RenameFile(const SourceFile, DestFile: string): integer; begin if @FSourcePlugin.FVFSRename <> nil then Result := FSourcePlugin.FVFSRename(FGlobs, PChar(SourceFile), PChar(DestFile)) else Result := cVFS_Failed; 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; Mode: cuLong): 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; UID, GID: cuLong): integer; begin if @FSourcePlugin.FVFSChown <> nil then Result := FSourcePlugin.FVFSChown(FGlobs, PChar(FileName), UID, GID) else Result := cVFS_Failed; end; function TVFSEngine.ChangeTimes(const APath: string; mtime, atime: time_t): integer; begin if @FSourcePlugin.FVFSChangeTimes <> nil then Result := FSourcePlugin.FVFSChangeTimes(FGlobs, PChar(APath), mtime, atime) else Result := cVFS_Failed; end; function TVFSEngine.GetDirSize(const 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 (FGlobs <> nil) and (@FSourcePlugin.FVFSSetBlockSize <> nil) then FSourcePlugin.FVFSSetBlockSize(FGlobs, Value); end; (********************************************************************************************************************************) function TVFSEngine.IsOnSameFS(const Path1, Path2: string; FollowSymlinks: boolean): boolean; begin if @FSourcePlugin.FVFSIsOnSameFS <> nil then Result := FSourcePlugin.FVFSIsOnSameFS(FGlobs, PChar(Path1), PChar(Path2), FollowSymlinks) 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; FollowSymlinks: boolean): boolean; begin if @FSourcePlugin.FVFSTwoSameFiles <> nil then Result := FSourcePlugin.FVFSTwoSameFiles(FGlobs, PChar(Path1), PChar(Path2), FollowSymlinks) else Result := False; end; (********************************************************************************************************************************) function TVFSEngine.CopyFileIn(Sender: Pointer; const SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; begin Result := StartCopyOperation(Sender, ErrorFunc, nil, nil, nil, nil); Result := Result and CopyFileInEx(Sender, SourceFile, DestFile, ErrorFunc, Append); Result := Result and StopCopyOperation(Sender, ErrorFunc); end; function TVFSEngine.CopyFileOut(Sender: Pointer; const SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; begin Result := StartCopyOperation(Sender, ErrorFunc, nil, nil, nil, nil); Result := Result and CopyFileInEx(Sender, SourceFile, DestFile, ErrorFunc, Append); Result := Result and StopCopyOperation(Sender, ErrorFunc); end; function TVFSEngine.CopyFileOutEx(Sender: Pointer; const SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; var Res: TVFSResult; begin Result := False; try if @FSourcePlugin.FVFSCopyToLocal <> nil then begin try Res := FSourcePlugin.FVFSCopyToLocal(FGlobs, PChar(SourceFile), PChar(DestFile), 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; Result := Res = cVFS_OK; // * TODO: Port to GError 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: ErrorFunc(Sender, 0, 0, SourceFile); 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.CopyFileInEx(Sender: Pointer; const SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; var Res: TVFSResult; begin Result := False; try if @FSourcePlugin.FVFSCopyFromLocal <> nil then begin try Res := FSourcePlugin.FVFSCopyFromLocal(FGlobs, PChar(SourceFile), PChar(DestFile), 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; // * TODO: Port to GError 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: ErrorFunc(Sender, 0, 0, SourceFile); 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.StartCopyOperation(Sender: Pointer; ErrorFunc: TEngineErrorFunc; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; var Res: TVFSResult; begin Result := False; try if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); if @FSourcePlugin.FVFSStartCopyOperation <> nil then begin Res := FSourcePlugin.FVFSStartCopyOperation(FGlobs); Result := Res = cVFS_OK; // * TODO: Port to GError if (Res <> cVFS_OK) and Assigned(ErrorFunc) then case Res of cVFS_ReadErr: Result := ErrorFunc(Sender, 6, 0, 'StartCopyOperation'); cVFS_WriteErr: Result := ErrorFunc(Sender, 7, 0, 'StartCopyOperation'); cVFS_mallocFailed: ErrorFunc(Sender, 1, 0, 'StartCopyOperation'); cVFS_Cancelled: ErrorFunc(Sender, 0, 0, 'StartCopyOperation'); end; end else ErrorFunc(Sender, 2, 0, 'StartCopyOperation not supported'); except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.StartCopyOperation(Sender=', QWord(Sender), '): (', E.ClassName, '): ', E.Message]); end; end; function TVFSEngine.StopCopyOperation(Sender: Pointer; ErrorFunc: TEngineErrorFunc): boolean; var Res: TVFSResult; begin Result := False; try if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); if @FSourcePlugin.FVFSStopCopyOperation <> nil then begin Res := FSourcePlugin.FVFSStopCopyOperation(FGlobs); Result := Res = cVFS_OK; // * TODO: Port to GError if (Res <> cVFS_OK) and Assigned(ErrorFunc) then case Res of cVFS_ReadErr: Result := ErrorFunc(Sender, 6, 0, 'StopCopyOperation'); cVFS_WriteErr: Result := ErrorFunc(Sender, 7, 0, 'StopCopyOperation'); cVFS_mallocFailed: ErrorFunc(Sender, 1, 0, 'StopCopyOperation'); cVFS_Cancelled: ErrorFunc(Sender, 0, 0, 'StopCopyOperation'); end; end else ErrorFunc(Sender, 5, 0, 'StopCopyOperation not supported'); except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.StopCopyOperation(Sender=', QWord(Sender), '): (', E.ClassName, '): ', E.Message]); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TVFSEngine.GetPasswordRequired: boolean; begin if @FSourcePlugin.FVFSGetPasswordRequired <> nil then Result := FSourcePlugin.FVFSGetPasswordRequired(FGlobs) else Result := False; end; procedure TVFSEngine.ResetPassword; begin if @FSourcePlugin.FVFSResetPassword <> nil then FSourcePlugin.FVFSResetPassword(FGlobs); end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TVFSEngine.GetPluginID: string; begin if FSourcePlugin <> nil then Result := FSourcePlugin.ModuleID else Result := ''; 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]).FullModulePath, IncludeTrailingPathDelimiter(s) + Buf) = 0 then begin b := True; Break; end; if b then DebugMsg(['Module ', s, ' is already loaded --> skipping...']) else begin ModuleHandler := dlopen(PChar(IncludeTrailingPathDelimiter(s) + Buf), RTLD_NOW); 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.FullModulePath := 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); {$IFDEF CPU64} QueryModules(ConstGlobalModulePath3); QueryModules(ConstGlobalModulePath4); QueryModules(ConstLocalModulePath4); {$ENDIF} end; end; initialization finalization UnloadModules; PluginList.Free; end.