You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
7.4 KiB

  1. unit uvfsTarArchive;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, libtar, syncobjs,
  6. uvfsManager;
  7. type
  8. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  9. TvfsTarArchive = class(TvfsProvider)
  10. private
  11. fStream: TStream;
  12. fOwnsStream: Boolean;
  13. fFileSpec: String;
  14. fTar: TTarArchive;
  15. fTarCS: TCriticalSection;
  16. private
  17. function FindFile(const aFilename: String; out aRec: TTarDirRec): Boolean;
  18. function PrepareFilename(const aFilename: String): String;
  19. public { TvfsProvider }
  20. class function StorageName: string; override;
  21. public { TvfsProvider }
  22. function StorageGetFileSpec: string; override;
  23. function GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): Boolean; override;
  24. function Open(const FileName: string; const OpenMode: TvfsFileOpenMode; out Stream: IStreamHandle): boolean; override;
  25. function Rename(const OldName, NewName: string): boolean; override;
  26. function Delete(const aName: String): boolean; override;
  27. procedure DirectoryIndex(AddFunction: TvfsDirectoryAddFunc; List: TvfsDirectoryList; Path: string; Subdirs: boolean); override;
  28. public
  29. constructor Create(const FileSpec: string; const ExtendedData: string = ''); override;
  30. constructor Create(const aStream: TStream; const aOwnsStream: Boolean);
  31. destructor Destroy; override;
  32. end;
  33. implementation
  34. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  35. //TvfsTarArchive////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  36. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  37. function TvfsTarArchive.FindFile(const aFilename: String; out aRec: TTarDirRec): Boolean;
  38. var
  39. fn: String;
  40. begin
  41. fn := PrepareFilename(aFilename);
  42. fTar.Reset;
  43. result := true;
  44. while fTar.FindNext(aRec{%H-}) do begin
  45. if (aRec.Name = fn) then
  46. exit;
  47. end;
  48. result := false;
  49. end;
  50. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  51. function TvfsTarArchive.PrepareFilename(const aFilename: String): String;
  52. var
  53. sl: TStringList;
  54. i: Integer;
  55. begin
  56. sl := TStringList.Create;
  57. try
  58. sl.Delimiter := VFS_PATH_DELIM;
  59. sl.StrictDelimiter := true;
  60. sl.DelimitedText := vfsVirtualPathDelim(aFilename);
  61. i := 0;
  62. while (i < sl.Count) do begin
  63. if (sl[i] = '..') and (i > 0) then begin
  64. sl.Delete(i);
  65. dec(i);
  66. sl.Delete(i);
  67. end else
  68. inc(i);
  69. end;
  70. result := sl.DelimitedText;
  71. finally
  72. FreeAndNil(sl);
  73. end;
  74. end;
  75. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  76. class function TvfsTarArchive.StorageName: string;
  77. begin
  78. result := 'tar';
  79. end;
  80. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  81. function TvfsTarArchive.StorageGetFileSpec: string;
  82. begin
  83. result := fFileSpec;
  84. end;
  85. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  86. function TvfsTarArchive.GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): Boolean;
  87. var
  88. rec: TTarDirRec;
  89. begin
  90. result := FindFile(FileName, rec);
  91. if result then begin
  92. FileInfo.Size := rec.Size;
  93. FileInfo.ModDate := rec.DateTime;
  94. FileInfo.Attributes := 0;
  95. end;
  96. end;
  97. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  98. function TvfsTarArchive.Open(const FileName: string; const OpenMode: TvfsFileOpenMode; out Stream: IStreamHandle): boolean;
  99. var
  100. ms: TMemoryStream;
  101. rec: TTarDirRec;
  102. begin
  103. result := false;
  104. fTarCS.Enter;
  105. try
  106. if not FindFile(FileName, rec) then
  107. exit;
  108. ms := TMemoryStream.Create;
  109. fTar.ReadFile(ms);
  110. case OpenMode of
  111. omReadOnly: begin
  112. ms.Position := 0;
  113. Stream := TvfsStreamHandleRead.Create(ms);
  114. end;
  115. else
  116. raise ENotSupportedException.Create('tar archive only supports read operations');
  117. end;
  118. result := Assigned(Stream);
  119. if not result then
  120. FreeAndNil(ms);
  121. finally
  122. fTarCS.Leave;
  123. end;
  124. end;
  125. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  126. function TvfsTarArchive.Rename(const OldName, NewName: string): boolean;
  127. begin
  128. result := false;
  129. end;
  130. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  131. function TvfsTarArchive.Delete(const aName: String): boolean;
  132. begin
  133. result := false;
  134. end;
  135. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  136. procedure TvfsTarArchive.DirectoryIndex(AddFunction: TvfsDirectoryAddFunc; List: TvfsDirectoryList; Path: string; Subdirs: boolean);
  137. var
  138. rec: TTarDirRec;
  139. dirOK: Boolean;
  140. fp: String;
  141. a: TvfsDirectoryEntry;
  142. begin
  143. fTarCS.Enter;
  144. try
  145. fTar.Reset;
  146. while fTar.FindNext(rec{%H-}) do begin
  147. fp := ExtractFilePath(rec.Name);
  148. if Subdirs then
  149. dirOK := (AnsiCompareStr(Path, copy(fp, 1, Length(Path))) = 0)
  150. else
  151. dirOk := (AnsiCompareFileName(Path, fp) = 0);
  152. if dirOK then begin
  153. a := TvfsDirectoryEntry.Create;
  154. a.FileInfo.Size := rec.Size;
  155. a.FileInfo.ModDate := rec.DateTime;
  156. a.FileInfo.Attributes := 0;
  157. AddFunction(rec.Name, a, List);
  158. end;
  159. end;
  160. finally
  161. fTarCS.Leave;
  162. end;
  163. end;
  164. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  165. constructor TvfsTarArchive.Create(const FileSpec: string; const ExtendedData: string);
  166. var
  167. fs: TFileStream;
  168. begin
  169. fFileSpec := FileSpec;
  170. if not FileExists(fFileSpec) then
  171. EFileNotFoundException.Create(fFileSpec);
  172. fs := TFileStream.Create(fFileSpec, fmOpenRead);
  173. Create(fs, true);
  174. end;
  175. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  176. constructor TvfsTarArchive.Create(const aStream: TStream; const aOwnsStream: Boolean);
  177. begin
  178. inherited Create('', '');
  179. fOwnsStream := aOwnsStream;
  180. fStream := aStream;
  181. fTar := TTarArchive.Create(fStream);
  182. fTarCS := TCriticalSection.Create;
  183. end;
  184. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  185. destructor TvfsTarArchive.Destroy;
  186. begin
  187. FreeAndNil(fTar);
  188. FreeAndNil(fTarCS);
  189. if (fOwnsStream) then
  190. FreeAndNil(fStream);
  191. inherited Destroy;
  192. end;
  193. initialization
  194. vfsManager.RegisterProvider(TvfsTarArchive);
  195. end.