diff options
Diffstat (limited to 'CPP/7zip/Archive/Hfs/HfsIn.cpp')
-rwxr-xr-x | CPP/7zip/Archive/Hfs/HfsIn.cpp | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Hfs/HfsIn.cpp b/CPP/7zip/Archive/Hfs/HfsIn.cpp new file mode 100755 index 00000000..8391dd93 --- /dev/null +++ b/CPP/7zip/Archive/Hfs/HfsIn.cpp @@ -0,0 +1,480 @@ +// HfsIn.cpp + +#include "StdAfx.h" + +#include "../../Common/StreamUtils.h" +#include "Common/IntToString.h" + +#include "HfsIn.h" + +#include "../../../../C/CpuArch.h" + +#define Get16(p) GetBe16(p) +#define Get32(p) GetBe32(p) +#define Get64(p) GetBe64(p) + +namespace NArchive { +namespace NHfs { + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareIdToIndex(const CIdIndexPair *p1, const CIdIndexPair *p2, void * /* param */) +{ + RINOZ(MyCompare(p1->ID, p2->ID)); + return MyCompare(p1->Index, p2->Index); +} + +bool operator< (const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID < a2.ID); } +bool operator> (const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID > a2.ID); } +bool operator==(const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID == a2.ID); } +bool operator!=(const CIdIndexPair &a1, const CIdIndexPair &a2) { return (a1.ID != a2.ID); } + +static UString GetSpecName(const UString &name, UInt32 /* id */) +{ + UString name2 = name; + name2.Trim(); + if (name2.IsEmpty()) + { + /* + wchar_t s[32]; + ConvertUInt64ToString(id, s); + return L"[" + (UString)s + L"]"; + */ + return L"[]"; + } + return name; +} + +UString CDatabase::GetItemPath(int index) const +{ + const CItem *item = &Items[index]; + UString name = GetSpecName(item->Name, item->ID); + + for (int i = 0; i < 1000; i++) + { + if (item->ParentID < 16 && item->ParentID != 2) + { + if (item->ParentID != 1) + break; + return name; + } + CIdIndexPair pair; + pair.ID = item->ParentID; + pair.Index = 0; + int indexInMap = IdToIndexMap.FindInSorted(pair); + if (indexInMap < 0) + break; + item = &Items[IdToIndexMap[indexInMap].Index]; + name = GetSpecName(item->Name, item->ID) + WCHAR_PATH_SEPARATOR + name; + } + return (UString)L"Unknown" + WCHAR_PATH_SEPARATOR + name; +} + +void CFork::Parse(const Byte *p) +{ + Size = Get64(p); + // ClumpSize = Get32(p + 8); + NumBlocks = Get32(p + 0xC); + for (int i = 0; i < 8; i++) + { + CExtent &e = Extents[i]; + e.Pos = Get32(p + 0x10 + i * 8); + e.NumBlocks = Get32(p + 0x10 + i * 8 + 4); + } +} + +static HRESULT ReadExtent(int blockSizeLog, IInStream *inStream, Byte *buf, const CExtent &e) +{ + RINOK(inStream->Seek((UInt64)e.Pos << blockSizeLog, STREAM_SEEK_SET, NULL)); + return ReadStream_FALSE(inStream, buf, (size_t)e.NumBlocks << blockSizeLog); +} + +HRESULT CDatabase::ReadFile(const CFork &fork, CByteBuffer &buf, IInStream *inStream) +{ + if (fork.NumBlocks >= Header.NumBlocks) + return S_FALSE; + size_t totalSize = (size_t)fork.NumBlocks << Header.BlockSizeLog; + if ((totalSize >> Header.BlockSizeLog) != fork.NumBlocks) + return S_FALSE; + buf.SetCapacity(totalSize); + UInt32 curBlock = 0; + for (int i = 0; i < 8; i++) + { + if (curBlock >= fork.NumBlocks) + break; + const CExtent &e = fork.Extents[i]; + if (fork.NumBlocks - curBlock < e.NumBlocks || e.Pos >= Header.NumBlocks) + return S_FALSE; + RINOK(ReadExtent(Header.BlockSizeLog, inStream, + (Byte *)buf + ((size_t)curBlock << Header.BlockSizeLog), e)); + curBlock += e.NumBlocks; + } + return S_OK; +} + +struct CNodeDescriptor +{ + UInt32 fLink; + UInt32 bLink; + Byte Kind; + Byte Height; + UInt16 NumRecords; + // UInt16 Reserved; + void Parse(const Byte *p); +}; + +void CNodeDescriptor::Parse(const Byte *p) +{ + fLink = Get32(p); + bLink = Get32(p + 4); + Kind = p[8]; + Height = p[9]; + NumRecords = Get16(p + 10); +} + +struct CHeaderRec +{ + // UInt16 TreeDepth; + // UInt32 RootNode; + // UInt32 LeafRecords; + UInt32 FirstLeafNode; + // UInt32 LastLeafNode; + int NodeSizeLog; + // UInt16 MaxKeyLength; + UInt32 TotalNodes; + // UInt32 FreeNodes; + // UInt16 Reserved1; + // UInt32 ClumpSize; + // Byte BtreeType; + // Byte KeyCompareType; + // UInt32 Attributes; + // UInt32 Reserved3[16]; + + HRESULT Parse(const Byte *p); +}; + +HRESULT CHeaderRec::Parse(const Byte *p) +{ + // TreeDepth = Get16(p); + // RootNode = Get32(p + 2); + // LeafRecords = Get32(p + 6); + FirstLeafNode = Get32(p + 0xA); + // LastLeafNode = Get32(p + 0xE); + UInt32 nodeSize = Get16(p + 0x12); + + int i; + for (i = 9; ((UInt32)1 << i) != nodeSize; i++) + if (i == 16) + return S_FALSE; + NodeSizeLog = i; + + // MaxKeyLength = Get16(p + 0x14); + TotalNodes = Get32(p + 0x16); + // FreeNodes = Get32(p + 0x1A); + // Reserved1 = Get16(p + 0x1E); + // ClumpSize = Get32(p + 0x20); + // BtreeType = p[0x24]; + // KeyCompareType = p[0x25]; + // Attributes = Get32(p + 0x26); + /* + for (int i = 0; i < 16; i++) + Reserved3[i] = Get32(p + 0x2A + i * 4); + */ + return S_OK; +} + + +enum ENodeType +{ + NODE_TYPE_LEAF = 0xFF, + NODE_TYPE_INDEX = 0, + NODE_TYPE_HEADER = 1, + NODE_TYPE_MODE = 2 +}; + +HRESULT CDatabase::LoadExtentFile(IInStream *inStream) +{ + // FileExtents.Clear(); + // ResExtents.Clear(); + + CByteBuffer extents; + RINOK(ReadFile(Header.ExtentsFile, extents, inStream)); + + const Byte *p = (const Byte *)extents; + + // CNodeDescriptor nodeDesc; + // nodeDesc.Parse(p); + CHeaderRec hr; + RINOK(hr.Parse(p + 14)); + + UInt32 node = hr.FirstLeafNode; + if (node != 0) + return S_FALSE; + /* + while (node != 0) + { + size_t nodeOffset = node * hr.NodeSize; + if ((node + 1)* hr.NodeSize > CatalogBuf.GetCapacity()) + return S_FALSE; + CNodeDescriptor desc; + desc.Parse(p + nodeOffset); + if (desc.Kind != NODE_TYPE_LEAF) + return S_FALSE; + UInt32 ptr = hr.NodeSize; + for (int i = 0; i < desc.NumRecords; i++) + { + UInt32 offs = Get16(p + nodeOffset + hr.NodeSize - (i + 1) * 2); + UInt32 offsNext = Get16(p + nodeOffset + hr.NodeSize - (i + 2) * 2); + + const Byte *r = p + nodeOffset + offs; + int keyLength = Get16(r); + Byte forkType = r[2]; + UInt32 id = Get16(r + 4); + UInt32 startBlock = Get16(r + 4); + CObjectVector<CIdExtents> *extents = (forkType == 0) ? &FileExtents : &ResExtents; + if (extents->Size() == 0) + extents->Add(CIdExtents()); + else + { + CIdExtents &e = extents->Back(); + if (e.ID != id) + { + if (e.ID > id) + return S_FALSE; + extents->Add(CIdExtents()); + } + } + CIdExtents &e = extents->Back(); + for (UInt32 k = offs + 10 + 2; k + 8 <= offsNext; k += 8) + { + CExtent ee; + ee.Pos = Get32(p + nodeOffset + k); + ee.NumBlocks = Get32(p + nodeOffset + k * 4); + e.Extents.Add(ee); + } + } + node = desc.fLink; + } + */ + return S_OK; +} + + +HRESULT CDatabase::LoadCatalog(IInStream *inStream, CProgressVirt *progress) +{ + Items.Clear(); + IdToIndexMap.ClearAndFree(); + + CByteBuffer catalogBuf; + RINOK(ReadFile(Header.CatalogFile, catalogBuf, inStream)); + const Byte *p = (const Byte *)catalogBuf; + + // CNodeDescriptor nodeDesc; + // nodeDesc.Parse(p); + CHeaderRec hr; + hr.Parse(p + 14); + + // CaseSensetive = (Header.IsHfsX() && hr.KeyCompareType == 0xBC); + + if ((catalogBuf.GetCapacity() >> hr.NodeSizeLog) < hr.TotalNodes) + return S_FALSE; + + CByteBuffer usedBuf; + usedBuf.SetCapacity(hr.TotalNodes); + for (UInt32 i = 0; i < hr.TotalNodes; i++) + usedBuf[i] = 0; + + UInt32 node = hr.FirstLeafNode; + while (node != 0) + { + if (node >= hr.TotalNodes) + return S_FALSE; + if (usedBuf[node]) + return S_FALSE; + usedBuf[node] = 1; + size_t nodeOffset = (size_t)node << hr.NodeSizeLog; + CNodeDescriptor desc; + desc.Parse(p + nodeOffset); + if (desc.Kind != NODE_TYPE_LEAF) + return S_FALSE; + for (int i = 0; i < desc.NumRecords; i++) + { + UInt32 nodeSize = (1 << hr.NodeSizeLog); + UInt32 offs = Get16(p + nodeOffset + nodeSize - (i + 1) * 2); + UInt32 offsNext = Get16(p + nodeOffset + nodeSize - (i + 2) * 2); + UInt32 recSize = offsNext - offs; + if (offsNext >= nodeSize || offsNext < offs || recSize < 6) + return S_FALSE; + + CItem item; + + const Byte *r = p + nodeOffset + offs; + UInt32 keyLength = Get16(r); + item.ParentID = Get32(r + 2); + UString name; + if (keyLength < 6 || (keyLength & 1) != 0 || keyLength + 2 > recSize) + return S_FALSE; + r += 6; + recSize -= 6; + keyLength -= 6; + + int nameLength = Get16(r); + if (nameLength * 2 != (int)keyLength) + return S_FALSE; + r += 2; + recSize -= 2; + + wchar_t *pp = name.GetBuffer(nameLength + 1); + + int j; + for (j = 0; j < nameLength; j++) + pp[j] = ((wchar_t)r[j * 2] << 8) | r[j * 2 + 1]; + pp[j] = 0; + name.ReleaseBuffer(); + r += j * 2; + recSize -= j * 2; + + if (recSize < 2) + return S_FALSE; + item.Type = Get16(r); + + if (item.Type != RECORD_TYPE_FOLDER && item.Type != RECORD_TYPE_FILE) + continue; + if (recSize < 0x58) + return S_FALSE; + + // item.Flags = Get16(r + 2); + // item.Valence = Get32(r + 4); + item.ID = Get32(r + 8); + item.CTime = Get32(r + 0xC); + item.MTime = Get32(r + 0x10); + // item.AttrMTime = Get32(r + 0x14); + item.ATime = Get32(r + 0x18); + // item.BackupDate = Get32(r + 0x1C); + + /* + item.OwnerID = Get32(r + 0x20); + item.GroupID = Get32(r + 0x24); + item.AdminFlags = r[0x28]; + item.OwnerFlags = r[0x29]; + item.FileMode = Get16(r + 0x2A); + item.special.iNodeNum = Get16(r + 0x2C); + */ + + item.Name = name; + + if (item.IsDir()) + { + CIdIndexPair pair; + pair.ID = item.ID; + pair.Index = Items.Size(); + IdToIndexMap.Add(pair); + } + else + { + CFork fd; + recSize -= 0x58; + r += 0x58; + if (recSize < 0x50 * 2) + return S_FALSE; + fd.Parse(r); + item.Size = fd.Size; + item.NumBlocks = fd.NumBlocks; + UInt32 curBlock = 0; + for (int j = 0; j < 8; j++) + { + if (curBlock >= fd.NumBlocks) + break; + const CExtent &e = fd.Extents[j]; + item.Extents.Add(e); + curBlock += e.NumBlocks; + } + } + Items.Add(item); + if (progress && Items.Size() % 100 == 0) + { + RINOK(progress->SetCompleted(Items.Size())); + } + } + node = desc.fLink; + } + IdToIndexMap.Sort(CompareIdToIndex, NULL); + return S_OK; +} + +HRESULT CDatabase::Open(IInStream *inStream, CProgressVirt *progress) +{ + static const UInt32 kHeaderSize = 1024 + 512; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize)); + int i; + for (i = 0; i < 1024; i++) + if (buf[i] != 0) + return S_FALSE; + const Byte *p = buf + 1024; + CVolHeader &h = Header; + + h.Header[0] = p[0]; + h.Header[1] = p[1]; + if (p[0] != 'H' || (p[1] != '+' && p[1] != 'X')) + return S_FALSE; + h.Version = Get16(p + 2); + if (h.Version < 4 || h.Version > 5) + return S_FALSE; + + // h.Attr = Get32(p + 4); + // h.LastMountedVersion = Get32(p + 8); + // h.JournalInfoBlock = Get32(p + 0xC); + + h.CTime = Get32(p + 0x10); + h.MTime = Get32(p + 0x14); + // h.BackupTime = Get32(p + 0x18); + // h.CheckedTime = Get32(p + 0x1C); + + // h.NumFiles = Get32(p + 0x20); + // h.NumFolders = Get32(p + 0x24); + + UInt32 numFiles = Get32(p + 0x20); + UInt32 numFolders = Get32(p + 0x24);; + if (progress) + { + RINOK(progress->SetTotal(numFolders + numFiles)); + } + + UInt32 blockSize = Get32(p + 0x28); + + for (i = 9; ((UInt32)1 << i) != blockSize; i++) + if (i == 31) + return S_FALSE; + h.BlockSizeLog = i; + + h.NumBlocks = Get32(p + 0x2C); + h.NumFreeBlocks = Get32(p + 0x30); + + /* + h.WriteCount = Get32(p + 0x44); + for (i = 0; i < 6; i++) + h.FinderInfo[i] = Get32(p + 0x50 + i * 4); + h.VolID = Get64(p + 0x68); + */ + + UInt64 endPos; + RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos)); + if ((endPos >> h.BlockSizeLog) < h.NumBlocks) + return S_FALSE; + + // h.AllocationFile.Parse(p + 0x70 + 0x50 * 0); + h.ExtentsFile.Parse( p + 0x70 + 0x50 * 1); + h.CatalogFile.Parse( p + 0x70 + 0x50 * 2); + // h.AttributesFile.Parse(p + 0x70 + 0x50 * 3); + // h.StartupFile.Parse( p + 0x70 + 0x50 * 4); + + RINOK(LoadExtentFile(inStream)); + RINOK(LoadCatalog(inStream, progress)); + + // if (Header.NumFiles + Header.NumFolders != (UInt32)Items.Size()) return S_OK; + + return S_OK; +} + +}} |