doublecmd/src/platform/utarwriter.pas

809 lines
23 KiB
ObjectPascal

{
Double Commander
-------------------------------------------------------------------------
Simple TAR archive writer
Copyright (C) 2011-2019 Alexander Koblov (alexx2000@mail.ru)
This unit is based on libtar.pp from the Free Component Library (FCL)
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, see <http://www.gnu.org/licenses/>.
}
unit uTarWriter;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,LazUtf8,
uGlobs, uWcxModule, WcxPlugin, DCClassesUtf8,
uFile,
uFileSource,
uFileSourceOperationUI,
uFileSourceOperation,
uFileSourceCopyOperation;
const
RECORDSIZE = 512;
NAMSIZ = 100;
TUNMLEN = 32;
TGNMLEN = 32;
CHKBLANKS = #32#32#32#32#32#32#32#32;
USTAR = 'ustar'#32#32;
LONGLINK = '././@LongLink';
LONGLEN = RECORDSIZE * 64;
LONGMAX = RECORDSIZE * 128;
type
TDataWriteProcedure = procedure(Buffer: Pointer; BytesToWrite: Int64) of object;
TUpdateStatisticsFunction = procedure(var NewStatistics: TFileSourceCopyOperationStatistics) of object;
{ TTarHeader }
TTarHeader = packed record
Name: array [0..NAMSIZ - 1] of AnsiChar;
Mode: array [0..7] of AnsiChar;
UID: array [0..7] of AnsiChar;
GID: array [0..7] of AnsiChar;
Size: array [0..11] of AnsiChar;
MTime: array [0..11] of AnsiChar;
ChkSum: array [0..7] of AnsiChar;
TypeFlag: AnsiChar;
LinkName: array [0..NAMSIZ - 1] of AnsiChar;
Magic: array [0..7] of AnsiChar;
UName: array [0..TUNMLEN - 1] of AnsiChar;
GName: array [0..TGNMLEN - 1] of AnsiChar;
DevMajor: array [0..7] of AnsiChar;
DevMinor: array [0..7] of AnsiChar;
Prefix: array [0..154] of AnsiChar;
end;
{ TTarHeaderEx }
TTarHeaderEx = packed record
case Boolean of
True: (HR: TTarHeader);
False: (HA: array [0..RECORDSIZE - 1] of AnsiChar);
end;
{ TTarWriter }
TTarWriter = class
private
FSourceStream,
FTargetStream: TFileStreamEx;
FWcxModule: TWcxModule;
FTarHeader: TTarHeaderEx;
FBasePath,
FTargetPath,
FArchiveFileName: String;
FBufferIn,
FBufferOut: Pointer;
FBufferSize: LongWord;
FMemPack: TArcHandle;
FLongName: array[0..Pred(LONGMAX)] of AnsiChar;
procedure WriteFakeHeader(const ItemName: String; IsFileName: Boolean; Offset: LongInt);
function MakeLongName(const FileName, LinkName: String;
NameLen, LinkLen: LongInt): LongInt;
function ReadData(BytesToRead: Int64): Int64;
procedure WriteData(Buffer: Pointer; BytesToWrite: Int64);
procedure CompressData(BufferIn: Pointer; BytesToCompress: Int64);
protected
AskQuestion: TAskQuestionFunction;
AbortOperation: TAbortOperationFunction;
CheckOperationState: TCheckOperationStateFunction;
UpdateStatistics: TUpdateStatisticsFunction;
DataWrite: TDataWriteProcedure;
procedure ShowError(sMessage: String);
procedure AddFile(const FileName: String);
function WriteFile(const FileName: String; var Statistics: TFileSourceCopyOperationStatistics): Boolean;
public
constructor Create(ArchiveFileName: String;
AskQuestionFunction: TAskQuestionFunction;
AbortOperationFunction: TAbortOperationFunction;
CheckOperationStateFunction: TCheckOperationStateFunction;
UpdateStatisticsFunction: TUpdateStatisticsFunction
);
constructor Create(ArchiveFileName: String;
AskQuestionFunction: TAskQuestionFunction;
AbortOperationFunction: TAbortOperationFunction;
CheckOperationStateFunction: TCheckOperationStateFunction;
UpdateStatisticsFunction: TUpdateStatisticsFunction;
WcxModule: TWcxModule
);
destructor Destroy; override;
function TarBegin: Boolean;
function TarFiles(const Files: TFiles; var Statistics: TFileSourceCopyOperationStatistics): Boolean;
function TarEnd(const beforeResult: Boolean): Boolean;
end;
implementation
uses
{$IF DEFINED(MSWINDOWS)}
Windows, DCFileAttributes, DCWindows, uMyWindows,
{$ELSEIF DEFINED(UNIX)}
BaseUnix, FileUtil, uUsersGroups,
{$ENDIF}
uLng, DCStrUtils, DCOSUtils, uOSUtils;
{$IF DEFINED(MSWINDOWS)}
const
FILE_UNIX_MODE = S_IRUSR or S_IWUSR or S_IRGRP or S_IROTH;
FOLDER_UNIX_MODE = S_IRUSR or S_IWUSR or S_IXUSR or S_IRGRP or S_IXGRP or S_IROTH or S_IXOTH;
{$ENDIF}
// Makes a string of octal digits
// The string will always be "Len" characters long
procedure Octal64(N : Int64; P : PAnsiChar; Len : Integer);
var
I : Integer;
begin
for I := Len - 1 downto 0 do
begin
(P + I)^ := AnsiChar (ORD ('0') + ORD (N and $07));
N := N shr 3;
end;
for I := 0 to Len - 1 do
begin
if (P + I)^ in ['0'..'7'] then Break;
(P + I)^ := '0';
end;
end;
procedure OctalN(N : Int64; P : PAnsiChar; Len : Integer);
begin
Octal64(N, P, Len-1);
(P + Len - 1)^ := #0;
end;
procedure CheckSum(var TarHeader: TTarHeaderEx);
var
I: Integer;
ChkSum : Cardinal = 0;
begin
with TarHeader do
begin
StrMove(HR.ChkSum, CHKBLANKS, 8);
for I := 0 to SizeOf(TTarHeader) - 1 do
Inc(ChkSum, Ord(HA[I]));
Octal64(ChkSum, HR.ChkSum, 6);
HR.ChkSum[6] := #0;
HR.ChkSum[7] := #32;
end;
end;
{$IF DEFINED(MSWINDOWS)}
function GetFileInfo(const FileName: String; out FileInfo: TWin32FindDataW): Boolean;
var
Handle: System.THandle;
begin
Handle := FindFirstFileW(PWideChar(UTF16LongName(FileName)), FileInfo);
Result := Handle <> INVALID_HANDLE_VALUE;
if Result then
begin
FileInfo.dwFileAttributes:= ExtractFileAttributes(FileInfo);
Windows.FindClose(Handle);
end;
end;
{$ELSEIF DEFINED(UNIX)}
function GetFileInfo(const FileName: String; out FileInfo: BaseUnix.Stat): Boolean;
begin
Result:= fpLStat(UTF8ToSys(FileName), FileInfo) >= 0;
end;
{$ENDIF}
{ TTarWriter }
procedure TTarWriter.ShowError(sMessage: String);
begin
AskQuestion(sMessage, '', [fsourAbort], fsourAbort, fsourAbort);
AbortOperation;
end;
constructor TTarWriter.Create(ArchiveFileName: String;
AskQuestionFunction: TAskQuestionFunction;
AbortOperationFunction: TAbortOperationFunction;
CheckOperationStateFunction: TCheckOperationStateFunction;
UpdateStatisticsFunction: TUpdateStatisticsFunction);
begin
AskQuestion := AskQuestionFunction;
AbortOperation := AbortOperationFunction;
CheckOperationState := CheckOperationStateFunction;
UpdateStatistics := UpdateStatisticsFunction;
DataWrite:= @WriteData;
FArchiveFileName:= ArchiveFileName;
FTargetPath:= ExtractFilePath(ArchiveFileName);
// Allocate buffers
FBufferSize := gCopyBlockSize;
GetMem(FBufferIn, FBufferSize);
FBufferOut:= nil;
FWcxModule:= nil;
FMemPack:= 0;
end;
constructor TTarWriter.Create(ArchiveFileName: String;
AskQuestionFunction: TAskQuestionFunction;
AbortOperationFunction: TAbortOperationFunction;
CheckOperationStateFunction: TCheckOperationStateFunction;
UpdateStatisticsFunction: TUpdateStatisticsFunction;
WcxModule: TWcxModule);
begin
AskQuestion := AskQuestionFunction;
AbortOperation := AbortOperationFunction;
CheckOperationState := CheckOperationStateFunction;
UpdateStatistics := UpdateStatisticsFunction;
DataWrite:= @CompressData;
FArchiveFileName:= ArchiveFileName;
FTargetPath:= ExtractFilePath(ArchiveFileName);
// Allocate buffers
FBufferSize := gCopyBlockSize;
GetMem(FBufferIn, FBufferSize);
GetMem(FBufferOut, FBufferSize);
FWcxModule:= WcxModule;
// Starts packing into memory
FMemPack:= FWcxModule.WcxStartMemPack(MEM_OPTIONS_WANTHEADERS, ExtractFileName(ArchiveFileName));
end;
destructor TTarWriter.Destroy;
begin
inherited Destroy;
if Assigned(FWcxModule) then
begin
// Ends packing into memory
if (FMemPack <> 0) then
FWcxModule.DoneMemPack(FMemPack);
end;
if Assigned(FBufferIn) then
begin
FreeMem(FBufferIn);
FBufferIn := nil;
end;
if Assigned(FBufferOut) then
begin
FreeMem(FBufferOut);
FBufferOut := nil;
end;
end;
procedure TTarWriter.AddFile(const FileName: String);
{$IF DEFINED(MSWINDOWS)}
var
FileInfo: TWin32FindDataW;
LinkName,
FileNameIn: String;
FileMode: Cardinal;
FileTime,
FileSize: Int64;
NameLen,
LinkLen: LongInt;
begin
if GetFileInfo(FileName, FileInfo) then
with FTarHeader do
begin
FillByte(HR, SizeOf(FTarHeader), 0);
// File name
FileNameIn:= ExtractDirLevel(FBasePath, FileName);
FileNameIn:= StringReplace (FileNameIn, '\', '/', [rfReplaceAll]);
if FPS_ISDIR(FileInfo.dwFileAttributes) then
FileNameIn:= FileNameIn + '/';
StrLCopy (HR.Name, PAnsiChar(FileNameIn), NAMSIZ);
// File mode
if FPS_ISDIR(FileInfo.dwFileAttributes) then
FileMode:= FOLDER_UNIX_MODE
else
FileMode:= FILE_UNIX_MODE;
OctalN(FileMode, HR.Mode, 8);
// File size
FileSize:= (FileInfo.nFileSizeHigh shl 32) or FileInfo.nFileSizeLow;
if FPS_ISLNK(FileInfo.dwFileAttributes) then
OctalN(0, HR.Size, 12)
else
OctalN(FileSize, HR.Size, 12);
// Modification time
FileTime:= Round((Int64(FileInfo.ftLastWriteTime) - 116444736000000000) / 10000000);
OctalN(FileTime, HR.MTime, 12);
// File type
if FPS_ISLNK(FileInfo.dwFileAttributes) then
HR.TypeFlag := '2'
else if FPS_ISDIR(FileInfo.dwFileAttributes) then
HR.TypeFlag := '5'
else
HR.TypeFlag := '0';
// Link name
if FPS_ISLNK(FileInfo.dwFileAttributes) then
begin
LinkName:= ReadSymLink(FileName);
StrLCopy(HR.LinkName, PAnsiChar(LinkName), NAMSIZ);
end;
// Magic
StrLCopy (HR.Magic, PAnsiChar(USTAR), 8);
// Header checksum
CheckSum(FTarHeader);
// Get file name and link name length
NameLen:= Length(FileNameIn);
LinkLen:= Length(LinkName);
// Write data
if not ((NameLen > NAMSIZ) or (LinkLen > NAMSIZ)) then
DataWrite(@HA, RECORDSIZE)
else
begin
NameLen:= MakeLongName(FileNameIn, LinkName, NameLen, LinkLen);
DataWrite(@FLongName, NameLen);
end;
end;
end;
{$ELSEIF DEFINED(UNIX)}
var
FileInfo: BaseUnix.Stat;
LinkName,
FileNameIn: String;
NameLen,
LinkLen: LongInt;
begin
if GetFileInfo(FileName, FileInfo) then
with FTarHeader do
begin
FillByte(HR, SizeOf(FTarHeader), 0);
// File name
FileNameIn:= ExtractDirLevel(FBasePath, FileName);
if fpS_ISDIR(FileInfo.st_mode) then
FileNameIn:= FileNameIn + PathDelim;
StrLCopy (HR.Name, PAnsiChar(FileNameIn), NAMSIZ);
// File mode
OctalN(FileInfo.st_mode and $FFF, HR.Mode, 8);
// UID
OctalN(FileInfo.st_uid, HR.UID, 8);
// GID
OctalN(FileInfo.st_gid, HR.GID, 8);
// File size
if fpS_ISLNK(FileInfo.st_mode) or fpS_ISDIR(FileInfo.st_mode) then
OctalN(0, HR.Size, 12)
else
OctalN(FileInfo.st_size, HR.Size, 12);
// Modification time
OctalN(FileInfo.st_mtime, HR.MTime, 12);
// File type
if fpS_ISLNK(FileInfo.st_mode) then
HR.TypeFlag:= '2'
else if fpS_ISCHR(FileInfo.st_mode) then
HR.TypeFlag:= '3'
else if fpS_ISBLK(FileInfo.st_mode) then
HR.TypeFlag:= '4'
else if fpS_ISDIR(FileInfo.st_mode) then
HR.TypeFlag:= '5'
else if fpS_ISFIFO(FileInfo.st_mode) then
HR.TypeFlag:= '6'
else
HR.TypeFlag:= '0';
// Link name
if fpS_ISLNK(FileInfo.st_mode) then
begin
LinkName:= ReadSymLink(FileName);
StrLCopy(HR.LinkName, PAnsiChar(LinkName), NAMSIZ);
end;
// Magic
StrLCopy (HR.Magic, PAnsiChar(USTAR), 8);
// User
StrPLCopy(HR.UName, UIDToStr(FileInfo.st_uid), TUNMLEN);
// Group
StrPLCopy(HR.GName, GIDToStr(FileInfo.st_gid), TGNMLEN);
// Header checksum
CheckSum(FTarHeader);
// Get file name and link name length
NameLen:= Length(FileNameIn);
LinkLen:= Length(LinkName);
// Write data
if not ((NameLen > NAMSIZ) or (LinkLen > NAMSIZ)) then
DataWrite(@HA, RECORDSIZE)
else
begin
NameLen:= MakeLongName(FileNameIn, LinkName, NameLen, LinkLen);
DataWrite(@FLongName, NameLen);
end;
end;
end;
{$ENDIF}
procedure TTarWriter.WriteFakeHeader(const ItemName: String;
IsFileName: Boolean; Offset: LongInt);
var
TarHeader: TTarHeaderEx;
begin
with TarHeader do
begin
FillByte(TarHeader, SizeOf(TTarHeaderEx), 0);
StrPLCopy (HR.Name, LONGLINK, NAMSIZ);
if IsFileName then
HR.TypeFlag:= 'L'
else
HR.TypeFlag:= 'K';
// File mode
OctalN(0, HR.Mode, 8);
// UID
OctalN(0, HR.UID, 8);
// GID
OctalN(0, HR.GID, 8);
// Name size
OctalN(Length(ItemName) + 1, HR.Size, 12);
// Modification time
OctalN(0, HR.MTime, 12);
// Magic
StrLCopy (HR.Magic, PAnsiChar(USTAR), 8);
// User
StrPLCopy(HR.UName, 'root', TUNMLEN);
// Group
StrPLCopy(HR.GName, 'root', TGNMLEN);
// Header checksum
CheckSum(TarHeader);
// Copy file record
Move(HA, PByte(PAnsiChar(@FLongName) + Offset)^, RECORDSIZE);
// Copy file name
StrMove(PAnsiChar(@FLongName) + Offset + RECORDSIZE, PAnsiChar(ItemName), Length(ItemName));
end;
end;
function TTarWriter.MakeLongName(const FileName, LinkName: String;
NameLen, LinkLen: LongInt): LongInt;
begin
with FTarHeader do
begin
Result:= 0;
// Strip string length to maximum length
if (NameLen + RECORDSIZE) > LONGLEN then
NameLen:= LONGLEN - RECORDSIZE * 2;
if (LinkLen + RECORDSIZE) > LONGLEN then
LinkLen:= LONGLEN - RECORDSIZE * 2;
// Clear output buffer
FillChar(FLongName, NameLen + LinkLen + RECORDSIZE * 4, #0);
// Write Header for long link name
if LinkLen > NAMSIZ then
begin
WriteFakeHeader(LinkName, False, Result);
// Align link name by RECORDSIZE (512)
if (LinkLen mod RECORDSIZE) = 0 then
Result:= Result + RECORDSIZE + Linklen
else
Result:= Result + RECORDSIZE * 2 + (LinkLen div RECORDSIZE) * RECORDSIZE;
end;
// Write Header for long file name
if NameLen > NAMSIZ then
begin
WriteFakeHeader(FileName, True, Result);
// Align file name by RECORDSIZE (512)
if (NameLen mod RECORDSIZE) = 0 then
Result:= Result + RECORDSIZE + NameLen
else
Result:= Result + RECORDSIZE * 2 + (NameLen div RECORDSIZE) * RECORDSIZE;
end;
// Copy file record
Move(HA, PByte(PAnsiChar(@FLongName) + Result)^, RECORDSIZE);
Result:= Result + RECORDSIZE;
end;
end;
function TTarWriter.ReadData(BytesToRead: Int64): Int64;
var
bRetryRead: Boolean;
BytesRead: Int64;
begin
repeat
try
bRetryRead := False;
FillByte(FBufferIn^, FBufferSize, 0);
BytesRead:= FSourceStream.Read(FBufferIn^, BytesToRead);
if (BytesRead = 0) then
Raise EReadError.Create(mbSysErrorMessage(GetLastOSError));
except
on E: EReadError do
begin
case AskQuestion(rsMsgErrERead + ' ' + FSourceStream.FileName + ':',
E.Message,
[fsourRetry, fsourSkip, fsourAbort],
fsourRetry, fsourSkip) of
fsourRetry:
bRetryRead := True;
fsourAbort:
AbortOperation;
fsourSkip:
Exit;
end; // case
end;
end;
until not bRetryRead;
Result:= BytesRead;
end;
procedure TTarWriter.WriteData(Buffer: Pointer; BytesToWrite: Int64);
var
iTotalDiskSize, iFreeDiskSize: Int64;
bRetryWrite: Boolean;
BytesWrittenTry, BytesWritten: Int64;
begin
BytesWritten := 0;
repeat
try
bRetryWrite := False;
BytesWrittenTry := FTargetStream.Write((Buffer + BytesWritten)^, BytesToWrite - BytesWritten);
BytesWritten := BytesWritten + BytesWrittenTry;
if BytesWrittenTry = 0 then
begin
Raise EWriteError.Create(mbSysErrorMessage(GetLastOSError));
end
else if BytesWritten < BytesToWrite then
begin
bRetryWrite := True; // repeat and try to write the rest
end;
except
on E: EWriteError do
begin
{ Check disk free space }
GetDiskFreeSpace(FTargetPath, iFreeDiskSize, iTotalDiskSize);
if BytesToWrite > iFreeDiskSize then
begin
case AskQuestion(rsMsgNoFreeSpaceRetry, '',
[fsourYes, fsourNo],
fsourYes, fsourNo) of
fsourYes:
bRetryWrite := True;
fsourNo:
AbortOperation;
end; // case
end
else
begin
case AskQuestion(rsMsgErrEWrite + ' ' + FArchiveFileName + ':',
E.Message,
[fsourRetry, fsourSkip, fsourAbort],
fsourRetry, fsourSkip) of
fsourRetry:
bRetryWrite := True;
fsourAbort:
AbortOperation;
fsourSkip:
Exit;
end; // case
end;
end; // on do
end; // except
until not bRetryWrite;
end;
procedure TTarWriter.CompressData(BufferIn: Pointer; BytesToCompress: Int64);
var
InLen: LongInt;
Written: LongInt = 0;
Taken: LongInt = 0;
SeekBy: LongInt = 0;
OffSet: LongInt = 0;
Result: LongInt;
begin
InLen:= BytesToCompress;
// Do while not all data accepted
repeat
// Recalculate offset
if (Taken <> 0) then
begin
OffSet:= OffSet + Taken;
InLen:= InLen - Taken;
end;
// Compress input buffer
{$PUSH}{$WARNINGS OFF}
Result:= FWcxModule.PackToMem(FMemPack, PByte(PtrUInt(BufferIn) + OffSet), InLen, @Taken, FBufferOut, FBufferSize, @Written, @SeekBy);
{$POP}
if not (Result in [MEMPACK_OK, MEMPACK_DONE]) then
begin
ShowError(Format(rsMsgLogError + rsMsgLogPack,
[FArchiveFileName + ' - ' + GetErrorMsg(Result)]));
end;
// Seek if needed
if (SeekBy <> 0) then
FTargetStream.Seek(SeekBy, soCurrent);
// Write compressed data
if Written > 0 then
WriteData(FBufferOut, Written);
until ((Taken = InLen) and (BytesToCompress <> 0)) or (Result = MEMPACK_DONE);
end;
function TTarWriter.WriteFile(const FileName: String; var Statistics: TFileSourceCopyOperationStatistics): Boolean;
var
BytesRead, BytesToRead, BytesToWrite: Int64;
TotalBytesToRead: Int64 = 0;
begin
Result := False;
BytesToRead := FBufferSize;
try
FSourceStream:= nil;
try
FSourceStream := TFileStreamEx.Create(FileName, fmOpenRead or fmShareDenyWrite);
TotalBytesToRead := FSourceStream.Size;
while TotalBytesToRead > 0 do
begin
// Without the following line the reading is very slow
// if it tries to read past end of file.
if TotalBytesToRead < BytesToRead then
BytesToRead := TotalBytesToRead;
BytesRead:= ReadData(BytesToRead);
TotalBytesToRead := TotalBytesToRead - BytesRead;
BytesToWrite:= BytesRead;
if (BytesRead mod RECORDSIZE) <> 0 then
begin
// Align by TAR RECORDSIZE
BytesToWrite:= (BytesRead div RECORDSIZE) * RECORDSIZE + RECORDSIZE;
end;
// Write data
DataWrite(FBufferIn, BytesToWrite);
with Statistics do
begin
CurrentFileDoneBytes := CurrentFileDoneBytes + BytesRead;
DoneBytes := DoneBytes + BytesRead;
UpdateStatistics(Statistics);
end;
CheckOperationState; // check pause and stop
end; // while
finally
FreeAndNil(FSourceStream);
end;
Result:= True;
except
on EFOpenError do
begin
ShowError(rsMsgLogError + rsMsgErrEOpen + ': ' + FileName);
end;
on EWriteError do
begin
ShowError(rsMsgLogError + rsMsgErrEWrite + ': ' + FArchiveFileName);
end;
end;
end;
function TTarWriter.TarBegin: Boolean;
begin
Result:= False;
try
FTargetStream:= TFileStreamEx.Create(FArchiveFileName, fmCreate);
Result:= True;
except
on EFCreateError do
begin
ShowError(rsMsgLogError + rsMsgErrECreate + ': ' + FArchiveFileName);
end;
end;
end;
function TTarWriter.TarFiles(
const Files: TFiles;
var Statistics: TFileSourceCopyOperationStatistics): Boolean;
var
aFile: TFile;
Divider: Int64 = 1;
CurrentFileIndex: Integer;
iTotalDiskSize, iFreeDiskSize: Int64;
procedure initCurrentTar;
begin
// Set base path
FBasePath:= Files.Path;
if FMemPack = 0 then begin
Divider:= 2;
end;
// Update progress
with Statistics do
begin
CurrentFileTo:= FArchiveFileName;
TotalBytes:= TotalBytes * Divider;
UpdateStatistics(Statistics);
end;
// initCurrentTar disk free space
//if FCheckFreeSpace = True then
begin
GetDiskFreeSpace(FTargetPath, iFreeDiskSize, iTotalDiskSize);
if Statistics.TotalBytes > iFreeDiskSize then
begin
AskQuestion('', rsMsgNoFreeSpaceCont, [fsourAbort], fsourAbort, fsourAbort);
AbortOperation;
end;
end;
end;
begin
Result:= False;
initCurrentTar;
for CurrentFileIndex := 0 to Files.Count - 1 do begin
aFile := Files[CurrentFileIndex];
if aFile.IsDirectory or aFile.IsLink then begin
// Add file record only
AddFile(aFile.FullPath);
end else begin
// Update progress
with Statistics do
begin
CurrentFileFrom := aFile.FullPath;
CurrentFileTotalBytes := aFile.Size;
CurrentFileDoneBytes := 0;
end;
UpdateStatistics(Statistics);
// Add file record
AddFile(aFile.FullPath);
// TAR current file
if not WriteFile(aFile.FullPath, Statistics) then
Exit;
end;
CheckOperationState;
end;
Result:= True;
end;
function TTarWriter.TarEnd(const beforeResult: Boolean): Boolean;
begin
Result:= False;
try
// Finish TAR archive with two null records
FillByte(FBufferIn^, RECORDSIZE * 2, 0);
DataWrite(FBufferIn, RECORDSIZE * 2);
// Finish compression if needed
if (FMemPack <> 0) then CompressData(FBufferIn, 0);
Result:= beforeResult;
finally
if Assigned(FTargetStream) then begin
FreeAndNil(FTargetStream);
if NOT Result then begin
// There was some error, because not all files has been archived.
// Delete the not completed target file.
mbDeleteFile(FArchiveFileName)
end;
end;
end;
end;
end.