mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-17 02:11:49 -06:00
Update to 7-Zip Version 22.00
See: https://sourceforge.net/p/sevenzip/discussion/45797/thread/9c2d9061ce/
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include "../../../../C/7zCrc.h"
|
||||
|
||||
#include "../../../Common/IntToString.h"
|
||||
|
||||
#include "../../Common/StreamUtils.h"
|
||||
|
||||
#include "TarOut.h"
|
||||
@@ -9,23 +13,27 @@
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
HRESULT COutArchive::WriteBytes(const void *data, unsigned size)
|
||||
{
|
||||
Pos += size;
|
||||
return WriteStream(m_Stream, data, size);
|
||||
}
|
||||
using namespace NFileHeader;
|
||||
|
||||
static bool WriteOctal_8(char *s, UInt32 val)
|
||||
// it's path prefix assigned by 7-Zip to show that file path was cut
|
||||
#define K_PREFIX_PATH_CUT "@PathCut"
|
||||
|
||||
static const UInt32 k_7_oct_digits_Val_Max = ((UInt32)1 << (7 * 3)) - 1;
|
||||
|
||||
static void WriteOctal_8(char *s, UInt32 val)
|
||||
{
|
||||
const unsigned kNumDigits = 8 - 1;
|
||||
if (val >= ((UInt32)1 << (kNumDigits * 3)))
|
||||
return false;
|
||||
{
|
||||
val = 0;
|
||||
// return false;
|
||||
}
|
||||
for (unsigned i = 0; i < kNumDigits; i++)
|
||||
{
|
||||
s[kNumDigits - 1 - i] = (char)('0' + (val & 7));
|
||||
val >>= 3;
|
||||
}
|
||||
return true;
|
||||
// return true;
|
||||
}
|
||||
|
||||
static void WriteBin_64bit(char *s, UInt64 val)
|
||||
@@ -68,61 +76,93 @@ static void CopyString(char *dest, const AString &src, unsigned maxSize)
|
||||
unsigned len = src.Len();
|
||||
if (len == 0)
|
||||
return;
|
||||
// 21.07: we don't require additional 0 character at the end
|
||||
// 21.07: new gnu : we don't require additional 0 character at the end
|
||||
// if (len >= maxSize)
|
||||
if (len > maxSize)
|
||||
{
|
||||
len = maxSize;
|
||||
// return false;
|
||||
/*
|
||||
// oldgnu needs 0 character at the end
|
||||
len = maxSize - 1;
|
||||
dest[len] = 0;
|
||||
*/
|
||||
}
|
||||
memcpy(dest, src.Ptr(), len);
|
||||
// return true;
|
||||
}
|
||||
|
||||
#define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_FAIL; }
|
||||
// #define RETURN_IF_NOT_TRUE(x) { if (!(x)) return E_INVALIDARG; }
|
||||
#define RETURN_IF_NOT_TRUE(x) { x; }
|
||||
|
||||
#define COPY_STRING_CHECK(dest, src, size) \
|
||||
CopyString(dest, src, size); dest += (size);
|
||||
|
||||
#define WRITE_OCTAL_8_CHECK(dest, src) \
|
||||
RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src));
|
||||
RETURN_IF_NOT_TRUE(WriteOctal_8(dest, src))
|
||||
|
||||
|
||||
HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
HRESULT COutArchive::WriteHeaderReal(const CItem &item, bool isPax
|
||||
// , bool zero_PackSize
|
||||
// , bool zero_MTime
|
||||
)
|
||||
{
|
||||
char record[NFileHeader::kRecordSize];
|
||||
memset(record, 0, NFileHeader::kRecordSize);
|
||||
/*
|
||||
if (isPax) { we don't use Glob_Name and Prefix }
|
||||
if (!isPax)
|
||||
{
|
||||
we use Glob_Name if it's not empty
|
||||
we use Prefix if it's not empty
|
||||
}
|
||||
*/
|
||||
char record[kRecordSize];
|
||||
memset(record, 0, kRecordSize);
|
||||
char *cur = record;
|
||||
|
||||
COPY_STRING_CHECK (cur, item.Name, NFileHeader::kNameSize);
|
||||
COPY_STRING_CHECK (cur,
|
||||
(!isPax && !Glob_Name.IsEmpty()) ? Glob_Name : item.Name,
|
||||
kNameSize);
|
||||
|
||||
WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8;
|
||||
WRITE_OCTAL_8_CHECK (cur, item.Mode); cur += 8; // & k_7_oct_digits_Val_Max
|
||||
WRITE_OCTAL_8_CHECK (cur, item.UID); cur += 8;
|
||||
WRITE_OCTAL_8_CHECK (cur, item.GID); cur += 8;
|
||||
|
||||
WriteOctal_12(cur, item.PackSize); cur += 12;
|
||||
WriteOctal_12_Signed(cur, item.MTime); cur += 12;
|
||||
WriteOctal_12 (cur, /* zero_PackSize ? 0 : */ item.PackSize); cur += 12;
|
||||
WriteOctal_12_Signed (cur, /* zero_MTime ? 0 : */ item.MTime); cur += 12;
|
||||
|
||||
memset(cur, ' ', 8); // checksum field
|
||||
// we will use binary init for checksum instead of memset
|
||||
// checksum field:
|
||||
// memset(cur, ' ', 8);
|
||||
cur += 8;
|
||||
|
||||
*cur++ = item.LinkFlag;
|
||||
|
||||
COPY_STRING_CHECK (cur, item.LinkName, NFileHeader::kNameSize);
|
||||
COPY_STRING_CHECK (cur, item.LinkName, kNameSize);
|
||||
|
||||
memcpy(cur, item.Magic, 8);
|
||||
cur += 8;
|
||||
|
||||
COPY_STRING_CHECK (cur, item.User, NFileHeader::kUserNameSize);
|
||||
COPY_STRING_CHECK (cur, item.Group, NFileHeader::kGroupNameSize);
|
||||
COPY_STRING_CHECK (cur, item.User, kUserNameSize);
|
||||
COPY_STRING_CHECK (cur, item.Group, kGroupNameSize);
|
||||
|
||||
if (item.DeviceMajorDefined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor);
|
||||
const bool needDevice = (IsPosixMode && !isPax);
|
||||
|
||||
if (item.DeviceMajor_Defined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMajor)
|
||||
else if (needDevice)
|
||||
WRITE_OCTAL_8_CHECK (cur, 0)
|
||||
cur += 8;
|
||||
if (item.DeviceMinorDefined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor);
|
||||
|
||||
if (item.DeviceMinor_Defined)
|
||||
WRITE_OCTAL_8_CHECK (cur, item.DeviceMinor)
|
||||
else if (needDevice)
|
||||
WRITE_OCTAL_8_CHECK (cur, 0)
|
||||
cur += 8;
|
||||
|
||||
if (item.IsSparse())
|
||||
if (!isPax && !Prefix.IsEmpty())
|
||||
{
|
||||
COPY_STRING_CHECK (cur, Prefix, kPrefixSize);
|
||||
}
|
||||
|
||||
if (item.Is_Sparse())
|
||||
{
|
||||
record[482] = (char)(item.SparseBlocks.Size() > 4 ? 1 : 0);
|
||||
WriteOctal_12(record + 483, item.Size);
|
||||
@@ -136,31 +176,31 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
}
|
||||
|
||||
{
|
||||
UInt32 checkSum = 0;
|
||||
UInt32 sum = (unsigned)(' ') * 8; // we use binary init
|
||||
{
|
||||
for (unsigned i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
checkSum += (Byte)record[i];
|
||||
for (unsigned i = 0; i < kRecordSize; i++)
|
||||
sum += (Byte)record[i];
|
||||
}
|
||||
/* we use GNU TAR scheme:
|
||||
checksum field is formatted differently from the
|
||||
/* checksum field is formatted differently from the
|
||||
other fields: it has [6] digits, a null, then a space. */
|
||||
// WRITE_OCTAL_8_CHECK(record + 148, checkSum);
|
||||
// WRITE_OCTAL_8_CHECK(record + 148, sum);
|
||||
const unsigned kNumDigits = 6;
|
||||
for (unsigned i = 0; i < kNumDigits; i++)
|
||||
{
|
||||
record[148 + kNumDigits - 1 - i] = (char)('0' + (checkSum & 7));
|
||||
checkSum >>= 3;
|
||||
record[148 + kNumDigits - 1 - i] = (char)('0' + (sum & 7));
|
||||
sum >>= 3;
|
||||
}
|
||||
record[148 + 6] = 0;
|
||||
// record[148 + 6] = 0; // we need it, if we use memset(' ') init
|
||||
record[148 + 7] = ' '; // we need it, if we use binary init
|
||||
}
|
||||
|
||||
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
|
||||
RINOK(Write_Data(record, kRecordSize));
|
||||
|
||||
if (item.IsSparse())
|
||||
if (item.Is_Sparse())
|
||||
{
|
||||
for (unsigned i = 4; i < item.SparseBlocks.Size();)
|
||||
{
|
||||
memset(record, 0, NFileHeader::kRecordSize);
|
||||
memset(record, 0, kRecordSize);
|
||||
for (unsigned t = 0; t < 21 && i < item.SparseBlocks.Size(); t++, i++)
|
||||
{
|
||||
const CSparseBlock &sb = item.SparseBlocks[i];
|
||||
@@ -169,7 +209,7 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
WriteOctal_12(p + 12, sb.Size);
|
||||
}
|
||||
record[21 * 24] = (char)(i < item.SparseBlocks.Size() ? 1 : 0);
|
||||
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
|
||||
RINOK(Write_Data(record, kRecordSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,101 +217,426 @@ HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
}
|
||||
|
||||
|
||||
/* OLD_GNU_TAR: writes short name with zero at the end
|
||||
NEW_GNU_TAR: writes short name without zero at the end */
|
||||
static void AddPaxLine(AString &s, const char *name, const AString &val)
|
||||
{
|
||||
// s.Add_LF(); // for debug
|
||||
const unsigned len = 3 + (unsigned)strlen(name) + val.Len();
|
||||
AString n;
|
||||
for (unsigned numDigits = 1;; numDigits++)
|
||||
{
|
||||
n.Empty();
|
||||
n.Add_UInt32(numDigits + len);
|
||||
if (numDigits == n.Len())
|
||||
break;
|
||||
}
|
||||
s += n;
|
||||
s.Add_Space();
|
||||
s += name;
|
||||
s += '=';
|
||||
s += val;
|
||||
s.Add_LF();
|
||||
}
|
||||
|
||||
|
||||
static void AddPaxTime(AString &s, const char *name, const CPaxTime &pt,
|
||||
const CTimeOptions &options)
|
||||
{
|
||||
unsigned numDigits = pt.NumDigits;
|
||||
if (numDigits > options.NumDigitsMax)
|
||||
numDigits = options.NumDigitsMax;
|
||||
|
||||
bool needNs = false;
|
||||
UInt32 ns = 0;
|
||||
if (numDigits != 0)
|
||||
{
|
||||
ns = pt.Ns;
|
||||
// if (ns != 0) before reduction, we show all digits after digits reduction
|
||||
needNs = (ns != 0 || options.RemoveZeroMode == k_PaxTimeMode_DontRemoveZero);
|
||||
UInt32 d = 1;
|
||||
for (unsigned k = numDigits; k < 9; k++)
|
||||
d *= 10;
|
||||
ns /= d;
|
||||
ns *= d;
|
||||
}
|
||||
|
||||
AString v;
|
||||
{
|
||||
Int64 sec = pt.Sec;
|
||||
if (pt.Sec < 0)
|
||||
{
|
||||
sec = -sec;
|
||||
v += '-';
|
||||
if (ns != 0)
|
||||
{
|
||||
ns = 1000*1000*1000 - ns;
|
||||
sec--;
|
||||
}
|
||||
}
|
||||
v.Add_UInt64(sec);
|
||||
}
|
||||
|
||||
if (needNs)
|
||||
{
|
||||
AString d;
|
||||
d.Add_UInt32(ns);
|
||||
while (d.Len() < 9)
|
||||
d.InsertAtFront('0');
|
||||
// here we have precision
|
||||
while (d.Len() > (unsigned)numDigits)
|
||||
d.DeleteBack();
|
||||
// GNU TAR reduces '0' digits.
|
||||
if (options.RemoveZeroMode == k_PaxTimeMode_RemoveZero_Always)
|
||||
while (!d.IsEmpty() && d.Back() == '0')
|
||||
d.DeleteBack();
|
||||
|
||||
if (!d.IsEmpty())
|
||||
{
|
||||
v += '.';
|
||||
v += d;
|
||||
// v += "1234567009999"; // for debug
|
||||
// for (int y = 0; y < 1000; y++) v += '8'; // for debug
|
||||
}
|
||||
}
|
||||
|
||||
AddPaxLine(s, name, v);
|
||||
}
|
||||
|
||||
|
||||
static void AddPax_UInt32_ifBig(AString &s, const char *name, const UInt32 &v)
|
||||
{
|
||||
if (v > k_7_oct_digits_Val_Max)
|
||||
{
|
||||
AString s2;
|
||||
s2.Add_UInt32(v);
|
||||
AddPaxLine(s, name, s2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* OLD_GNU_TAR: writes name with zero at the end
|
||||
NEW_GNU_TAR: can write name filled with all kNameSize characters */
|
||||
|
||||
static const unsigned kNameSize_Max =
|
||||
NFileHeader::kNameSize; // NEW_GNU_TAR / 7-Zip 21.07
|
||||
// NFileHeader::kNameSize - 1; // OLD_GNU_TAR / old 7-Zip
|
||||
kNameSize; // NEW_GNU_TAR / 7-Zip 21.07
|
||||
// kNameSize - 1; // OLD_GNU_TAR / old 7-Zip
|
||||
|
||||
#define DOES_NAME_FIT_IN_FIELD(name) ((name).Len() <= kNameSize_Max)
|
||||
|
||||
|
||||
HRESULT COutArchive::WriteHeader(const CItem &item)
|
||||
{
|
||||
if (DOES_NAME_FIT_IN_FIELD(item.Name) &&
|
||||
DOES_NAME_FIT_IN_FIELD(item.LinkName))
|
||||
return WriteHeaderReal(item);
|
||||
Glob_Name.Empty();
|
||||
Prefix.Empty();
|
||||
|
||||
// here we can get all fields from main (item) or create new empty item
|
||||
/*
|
||||
CItem mi;
|
||||
mi.SetDefaultWriteFields();
|
||||
*/
|
||||
|
||||
CItem mi = item;
|
||||
mi.LinkName.Empty();
|
||||
// SparseBlocks will be ignored by IsSparse()
|
||||
// mi.SparseBlocks.Clear();
|
||||
unsigned namePos = 0;
|
||||
bool needPathCut = false;
|
||||
bool allowPrefix = false;
|
||||
|
||||
mi.Name = NFileHeader::kLongLink;
|
||||
// 21.07 : we set Mode and MTime props as in GNU TAR:
|
||||
mi.Mode = 0644; // octal
|
||||
mi.MTime = 0;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name))
|
||||
{
|
||||
const AString *name;
|
||||
// We suppose that GNU TAR also writes item for long link before item for LongName?
|
||||
if (i == 0)
|
||||
{
|
||||
mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongLink;
|
||||
name = &item.LinkName;
|
||||
}
|
||||
else
|
||||
{
|
||||
mi.LinkFlag = NFileHeader::NLinkFlag::kGnu_LongName;
|
||||
name = &item.Name;
|
||||
}
|
||||
if (DOES_NAME_FIT_IN_FIELD(*name))
|
||||
continue;
|
||||
// GNU TAR writes null character after NAME to file. We do same here:
|
||||
const unsigned nameStreamSize = name->Len() + 1;
|
||||
mi.PackSize = nameStreamSize;
|
||||
RINOK(WriteHeaderReal(mi));
|
||||
RINOK(WriteBytes(name->Ptr(), nameStreamSize));
|
||||
RINOK(FillDataResidual(nameStreamSize));
|
||||
const char *s = item.Name;
|
||||
const char *p = s + item.Name.Len() - 1;
|
||||
for (; *p == '/' && p != s; p--)
|
||||
{}
|
||||
for (; p != s && p[-1] != '/'; p--)
|
||||
{}
|
||||
namePos = (unsigned)(p - s);
|
||||
needPathCut = true;
|
||||
}
|
||||
|
||||
if (IsPosixMode)
|
||||
{
|
||||
AString s;
|
||||
|
||||
if (needPathCut)
|
||||
{
|
||||
const unsigned nameLen = item.Name.Len() - namePos;
|
||||
if ( item.LinkFlag >= NLinkFlag::kNormal
|
||||
&& item.LinkFlag <= NLinkFlag::kDirectory
|
||||
&& namePos > 1
|
||||
&& nameLen != 0
|
||||
// && IsPrefixAllowed
|
||||
&& item.IsMagic_Posix_ustar_00())
|
||||
{
|
||||
/* GNU TAR decoder supports prefix field, only if (magic)
|
||||
signature matches 6-bytes "ustar\0".
|
||||
so here we use prefix field only in posix mode with posix signature */
|
||||
|
||||
allowPrefix = true;
|
||||
// allowPrefix = false; // for debug
|
||||
if (namePos <= kPrefixSize + 1 && nameLen <= kNameSize_Max)
|
||||
{
|
||||
needPathCut = false;
|
||||
/* we will set Prefix and Glob_Name later, for such conditions:
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name) && !needPathCut) */
|
||||
}
|
||||
}
|
||||
|
||||
if (needPathCut)
|
||||
AddPaxLine(s, "path", item.Name);
|
||||
}
|
||||
|
||||
// AddPaxLine(s, "testname", AString("testval")); // for debug
|
||||
|
||||
if (item.LinkName.Len() > kNameSize_Max)
|
||||
AddPaxLine(s, "linkpath", item.LinkName);
|
||||
|
||||
const UInt64 kPaxSize_Limit = ((UInt64)1 << 33);
|
||||
// const UInt64 kPaxSize_Limit = ((UInt64)1 << 1); // for debug
|
||||
// bool zero_PackSize = false;
|
||||
if (item.PackSize >= kPaxSize_Limit)
|
||||
{
|
||||
/* GNU TAR in pax mode sets PackSize = 0 in main record, if pack_size >= 8 GiB
|
||||
But old 7-Zip doesn't detect "size" property from pax header.
|
||||
So we write real size (>= 8 GiB) to main record in binary format,
|
||||
and old 7-Zip can decode size correctly */
|
||||
// zero_PackSize = true;
|
||||
AString v;
|
||||
v.Add_UInt64(item.PackSize);
|
||||
AddPaxLine(s, "size", v);
|
||||
}
|
||||
|
||||
/* GNU TAR encoder can set "devmajor" / "devminor" attributes,
|
||||
but GNU TAR decoder doesn't parse "devmajor" / "devminor" */
|
||||
if (item.DeviceMajor_Defined)
|
||||
AddPax_UInt32_ifBig(s, "devmajor", item.DeviceMajor);
|
||||
if (item.DeviceMinor_Defined)
|
||||
AddPax_UInt32_ifBig(s, "devminor", item.DeviceMinor);
|
||||
|
||||
AddPax_UInt32_ifBig(s, "uid", item.UID);
|
||||
AddPax_UInt32_ifBig(s, "gid", item.GID);
|
||||
|
||||
const UInt64 kPax_MTime_Limit = ((UInt64)1 << 33);
|
||||
const bool zero_MTime = (
|
||||
item.MTime < 0 ||
|
||||
item.MTime >= (Int64)kPax_MTime_Limit);
|
||||
|
||||
const CPaxTime &mtime = item.PaxTimes.MTime;
|
||||
if (mtime.IsDefined())
|
||||
{
|
||||
bool needPax = false;
|
||||
if (zero_MTime)
|
||||
needPax = true;
|
||||
else if (TimeOptions.NumDigitsMax > 0)
|
||||
if (mtime.Ns != 0 ||
|
||||
(mtime.NumDigits != 0 &&
|
||||
TimeOptions.RemoveZeroMode == k_PaxTimeMode_DontRemoveZero))
|
||||
needPax = true;
|
||||
if (needPax)
|
||||
AddPaxTime(s, "mtime", mtime, TimeOptions);
|
||||
}
|
||||
|
||||
if (item.PaxTimes.ATime.IsDefined())
|
||||
AddPaxTime(s, "atime", item.PaxTimes.ATime, TimeOptions);
|
||||
if (item.PaxTimes.CTime.IsDefined())
|
||||
AddPaxTime(s, "ctime", item.PaxTimes.CTime, TimeOptions);
|
||||
|
||||
if (item.User.Len() > kUserNameSize)
|
||||
AddPaxLine(s, "uname", item.User);
|
||||
if (item.Group.Len() > kGroupNameSize)
|
||||
AddPaxLine(s, "gname", item.Group);
|
||||
|
||||
/*
|
||||
// for debug
|
||||
AString a ("11"); for (int y = 0; y < (1 << 24); y++) AddPaxLine(s, "temp", a);
|
||||
*/
|
||||
|
||||
const unsigned paxSize = s.Len();
|
||||
if (paxSize != 0)
|
||||
{
|
||||
CItem mi = item;
|
||||
mi.LinkName.Empty();
|
||||
// SparseBlocks will be ignored by Is_Sparse()
|
||||
// mi.SparseBlocks.Clear();
|
||||
// we use "PaxHeader/*" for compatibility with previous 7-Zip decoder
|
||||
|
||||
// GNU TAR writes empty for these fields;
|
||||
mi.User.Empty();
|
||||
mi.Group.Empty();
|
||||
mi.UID = 0;
|
||||
mi.GID = 0;
|
||||
|
||||
mi.DeviceMajor_Defined = false;
|
||||
mi.DeviceMinor_Defined = false;
|
||||
|
||||
mi.Name = "PaxHeader/@PaxHeader";
|
||||
mi.Mode = 0644; // octal
|
||||
if (zero_MTime)
|
||||
mi.MTime = 0;
|
||||
mi.LinkFlag = NLinkFlag::kPax;
|
||||
// mi.LinkFlag = 'Z'; // for debug
|
||||
mi.PackSize = paxSize;
|
||||
// for (unsigned y = 0; y < 1; y++) { // for debug
|
||||
RINOK(WriteHeaderReal(mi, true)); // isPax
|
||||
RINOK(Write_Data_And_Residual(s, paxSize));
|
||||
// } // for debug
|
||||
/*
|
||||
we can send (zero_MTime) for compatibility with gnu tar output.
|
||||
we can send (zero_MTime = false) for better compatibility with old 7-Zip
|
||||
*/
|
||||
// return WriteHeaderReal(item);
|
||||
/*
|
||||
false, // isPax
|
||||
false, // zero_PackSize
|
||||
false); // zero_MTime
|
||||
*/
|
||||
}
|
||||
}
|
||||
else // !PosixMode
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name) ||
|
||||
!DOES_NAME_FIT_IN_FIELD(item.LinkName))
|
||||
{
|
||||
// here we can get all fields from main (item) or create new empty item
|
||||
/*
|
||||
CItem mi;
|
||||
mi.SetDefaultWriteFields();
|
||||
*/
|
||||
CItem mi = item;
|
||||
mi.LinkName.Empty();
|
||||
// SparseBlocks will be ignored by Is_Sparse()
|
||||
// mi.SparseBlocks.Clear();
|
||||
mi.Name = kLongLink;
|
||||
// mi.Name = "././@BAD_LONG_LINK_TEST"; // for debug
|
||||
// 21.07 : we set Mode and MTime props as in GNU TAR:
|
||||
mi.Mode = 0644; // octal
|
||||
mi.MTime = 0;
|
||||
|
||||
mi.User.Empty();
|
||||
mi.Group.Empty();
|
||||
/*
|
||||
gnu tar sets "root" for such items:
|
||||
uid_to_uname (0, &uname);
|
||||
gid_to_gname (0, &gname);
|
||||
*/
|
||||
/*
|
||||
mi.User = "root";
|
||||
mi.Group = "root";
|
||||
*/
|
||||
mi.UID = 0;
|
||||
mi.GID = 0;
|
||||
mi.DeviceMajor_Defined = false;
|
||||
mi.DeviceMinor_Defined = false;
|
||||
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
const AString *name;
|
||||
// We suppose that GNU TAR also writes item for long link before item for LongName?
|
||||
if (i == 0)
|
||||
{
|
||||
mi.LinkFlag = NLinkFlag::kGnu_LongLink;
|
||||
name = &item.LinkName;
|
||||
}
|
||||
else
|
||||
{
|
||||
mi.LinkFlag = NLinkFlag::kGnu_LongName;
|
||||
name = &item.Name;
|
||||
}
|
||||
if (DOES_NAME_FIT_IN_FIELD(*name))
|
||||
continue;
|
||||
// GNU TAR writes null character after NAME to file. We do same here:
|
||||
const unsigned nameStreamSize = name->Len() + 1;
|
||||
mi.PackSize = nameStreamSize;
|
||||
// for (unsigned y = 0; y < 3; y++) { // for debug
|
||||
RINOK(WriteHeaderReal(mi));
|
||||
RINOK(Write_Data_And_Residual(name->Ptr(), nameStreamSize));
|
||||
// }
|
||||
|
||||
// for debug
|
||||
/*
|
||||
const unsigned kSize = (1 << 29) + 16;
|
||||
CByteBuffer buf;
|
||||
buf.Alloc(kSize);
|
||||
memset(buf, 0, kSize);
|
||||
memcpy(buf, name->Ptr(), name->Len());
|
||||
const unsigned nameStreamSize = kSize;
|
||||
mi.PackSize = nameStreamSize;
|
||||
// for (unsigned y = 0; y < 3; y++) { // for debug
|
||||
RINOK(WriteHeaderReal(mi));
|
||||
RINOK(WriteBytes(buf, nameStreamSize));
|
||||
RINOK(FillDataResidual(nameStreamSize));
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// bool fals = false; if (fals) // for debug: for bit-to-bit output compatibility with GNU TAR
|
||||
|
||||
if (!DOES_NAME_FIT_IN_FIELD(item.Name))
|
||||
{
|
||||
const unsigned nameLen = item.Name.Len() - namePos;
|
||||
if (!needPathCut)
|
||||
Prefix.SetFrom(item.Name, namePos - 1);
|
||||
else
|
||||
{
|
||||
Glob_Name = K_PREFIX_PATH_CUT "/_pc_";
|
||||
|
||||
if (namePos == 0)
|
||||
Glob_Name += "root";
|
||||
else
|
||||
{
|
||||
Glob_Name += "crc32/";
|
||||
char temp[12];
|
||||
ConvertUInt32ToHex8Digits(CrcCalc(item.Name, namePos - 1), temp);
|
||||
Glob_Name += temp;
|
||||
}
|
||||
|
||||
if (!allowPrefix || Glob_Name.Len() + 1 + nameLen <= kNameSize_Max)
|
||||
Glob_Name.Add_Slash();
|
||||
else
|
||||
{
|
||||
Prefix = Glob_Name;
|
||||
Glob_Name.Empty();
|
||||
}
|
||||
}
|
||||
Glob_Name.AddFrom(item.Name.Ptr(namePos), nameLen);
|
||||
}
|
||||
|
||||
// 21.07: WriteHeaderReal() writes short part of (Name) and (LinkName).
|
||||
return WriteHeaderReal(item);
|
||||
/*
|
||||
mi = item;
|
||||
if (!DOES_NAME_FIT_IN_FIELD(mi.Name))
|
||||
mi.Name.SetFrom(item.Name, kNameSize_Max);
|
||||
if (!DOES_NAME_FIT_IN_FIELD(mi.LinkName))
|
||||
mi.LinkName.SetFrom(item.LinkName, kNameSize_Max);
|
||||
return WriteHeaderReal(mi);
|
||||
*/
|
||||
}
|
||||
|
||||
HRESULT COutArchive::FillDataResidual(UInt64 dataSize)
|
||||
|
||||
HRESULT COutArchive::Write_Data(const void *data, unsigned size)
|
||||
{
|
||||
unsigned lastRecordSize = ((unsigned)dataSize & (NFileHeader::kRecordSize - 1));
|
||||
if (lastRecordSize == 0)
|
||||
return S_OK;
|
||||
unsigned rem = NFileHeader::kRecordSize - lastRecordSize;
|
||||
Byte buf[NFileHeader::kRecordSize];
|
||||
memset(buf, 0, rem);
|
||||
return WriteBytes(buf, rem);
|
||||
Pos += size;
|
||||
return WriteStream(Stream, data, size);
|
||||
}
|
||||
|
||||
HRESULT COutArchive::Write_AfterDataResidual(UInt64 dataSize)
|
||||
{
|
||||
const unsigned v = ((unsigned)dataSize & (kRecordSize - 1));
|
||||
if (v == 0)
|
||||
return S_OK;
|
||||
const unsigned rem = kRecordSize - v;
|
||||
Byte buf[kRecordSize];
|
||||
memset(buf, 0, rem);
|
||||
return Write_Data(buf, rem);
|
||||
}
|
||||
|
||||
|
||||
HRESULT COutArchive::Write_Data_And_Residual(const void *data, unsigned size)
|
||||
{
|
||||
RINOK(Write_Data(data, size));
|
||||
return Write_AfterDataResidual(size);
|
||||
}
|
||||
|
||||
|
||||
HRESULT COutArchive::WriteFinishHeader()
|
||||
{
|
||||
Byte record[NFileHeader::kRecordSize];
|
||||
memset(record, 0, NFileHeader::kRecordSize);
|
||||
Byte record[kRecordSize];
|
||||
memset(record, 0, kRecordSize);
|
||||
|
||||
const unsigned kNumFinishRecords = 2;
|
||||
|
||||
/* GNU TAR by default uses --blocking-factor=20 (512 * 20 = 10 KiB)
|
||||
we also can use cluster alignment:
|
||||
const unsigned numBlocks = (unsigned)(Pos / NFileHeader::kRecordSize) + kNumFinishRecords;
|
||||
const unsigned numBlocks = (unsigned)(Pos / kRecordSize) + kNumFinishRecords;
|
||||
const unsigned kNumClusterBlocks = (1 << 3); // 8 blocks = 4 KiB
|
||||
const unsigned numFinishRecords = kNumFinishRecords + ((kNumClusterBlocks - numBlocks) & (kNumClusterBlocks - 1));
|
||||
*/
|
||||
|
||||
for (unsigned i = 0; i < kNumFinishRecords; i++)
|
||||
{
|
||||
RINOK(WriteBytes(record, NFileHeader::kRecordSize));
|
||||
RINOK(Write_Data(record, kRecordSize));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user