123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497 |
- #include "rar.hpp"
- size_t Archive::ReadHeader()
- {
- // Once we failed to decrypt an encrypted block, there is no reason to
- // attempt to do it further. We'll never be successful and only generate
- // endless errors.
- if (FailedHeaderDecryption)
- return 0;
- CurBlockPos=Tell();
- // Other developers asked us to initialize it to suppress "may be used
- // uninitialized" warning in code below in some compilers.
- size_t ReadSize=0;
- switch(Format)
- {
- #ifndef SFX_MODULE
- case RARFMT14:
- ReadSize=ReadHeader14();
- break;
- #endif
- case RARFMT15:
- ReadSize=ReadHeader15();
- break;
- case RARFMT50:
- ReadSize=ReadHeader50();
- break;
- }
- // It is important to check ReadSize>0 here, because it is normal
- // for RAR2 and RAR3 archives without end of archive block to have
- // NextBlockPos==CurBlockPos after the end of archive has reached.
- if (ReadSize>0 && NextBlockPos<=CurBlockPos)
- {
- BrokenHeaderMsg();
- ReadSize=0;
- }
- if (ReadSize==0)
- CurHeaderType=HEAD_UNKNOWN;
- return ReadSize;
- }
- size_t Archive::SearchBlock(HEADER_TYPE HeaderType)
- {
- size_t Size,Count=0;
- while ((Size=ReadHeader())!=0 &&
- (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC))
- {
- if ((++Count & 127)==0)
- Wait();
- if (GetHeaderType()==HeaderType)
- return Size;
- SeekToNext();
- }
- return 0;
- }
- size_t Archive::SearchSubBlock(const wchar *Type)
- {
- size_t Size,Count=0;
- while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC)
- {
- if ((++Count & 127)==0)
- Wait();
- if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type))
- return Size;
- SeekToNext();
- }
- return 0;
- }
- size_t Archive::SearchRR()
- {
- // If locator extra field is available for recovery record, let's utilize it.
- if (MainHead.Locator && MainHead.RROffset!=0)
- {
- uint64 CurPos=Tell();
- Seek(MainHead.RROffset,SEEK_SET);
- size_t Size=ReadHeader();
- if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR))
- return Size;
- Seek(CurPos,SEEK_SET);
- }
- // Otherwise scan the entire archive to find the recovery record.
- return SearchSubBlock(SUBHEAD_TYPE_RR);
- }
- void Archive::UnexpEndArcMsg()
- {
- int64 ArcSize=FileLength();
- // If block positions are equal to file size, this is not an error.
- // It can happen when we reached the end of older RAR 1.5 archive,
- // which did not have the end of archive block.
- if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
- {
- uiMsg(UIERROR_UNEXPEOF,FileName);
- ErrHandler.SetErrorCode(RARX_WARNING);
- }
- }
- void Archive::BrokenHeaderMsg()
- {
- uiMsg(UIERROR_HEADERBROKEN,FileName);
- BrokenHeader=true;
- ErrHandler.SetErrorCode(RARX_CRC);
- }
- void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
- {
- uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
- ErrHandler.SetErrorCode(RARX_WARNING);
- }
- // Return f in case of signed integer overflow or negative parameters
- // or v1+v2 otherwise. We use it for file offsets, which are signed
- // for compatibility with off_t in POSIX file functions and third party code.
- // Signed integer overflow is the undefined behavior according to
- // C++ standard and it causes fuzzers to complain.
- inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
- {
- return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f;
- }
- size_t Archive::ReadHeader15()
- {
- RawRead Raw(this);
- bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3;
- if (Decrypt)
- {
- #ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
- return 0;
- #else
- RequestArcPassword();
- byte Salt[SIZE_SALT30];
- if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
- {
- UnexpEndArcMsg();
- return 0;
- }
- HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL);
- Raw.SetCrypt(&HeadersCrypt);
- #endif
- }
- Raw.Read(SIZEOF_SHORTBLOCKHEAD);
- if (Raw.Size()==0)
- {
- UnexpEndArcMsg();
- return 0;
- }
- ShortBlock.HeadCRC=Raw.Get2();
- ShortBlock.Reset();
- uint HeaderType=Raw.Get1();
- ShortBlock.Flags=Raw.Get2();
- ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0;
- ShortBlock.HeadSize=Raw.Get2();
- ShortBlock.HeaderType=(HEADER_TYPE)HeaderType;
- if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD)
- {
- BrokenHeaderMsg();
- return 0;
- }
- // For simpler further processing we map header types common
- // for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include
- // header types specific for RAR 1.5 - 4.x only.
- switch(ShortBlock.HeaderType)
- {
- case HEAD3_MAIN: ShortBlock.HeaderType=HEAD_MAIN; break;
- case HEAD3_FILE: ShortBlock.HeaderType=HEAD_FILE; break;
- case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE; break;
- case HEAD3_ENDARC: ShortBlock.HeaderType=HEAD_ENDARC; break;
- }
- CurHeaderType=ShortBlock.HeaderType;
- if (ShortBlock.HeaderType==HEAD3_CMT)
- {
- // Old style (up to RAR 2.9) comment header embedded into main
- // or file header. We must not read the entire ShortBlock.HeadSize here
- // to not break the comment processing logic later.
- Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD);
- }
- else
- if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0)
- {
- // Old style (up to RAR 2.9) main archive comment embedded into
- // the main archive header found. While we can read the entire
- // ShortBlock.HeadSize here and remove this part of "if", it would be
- // waste of memory, because we'll read and process this comment data
- // in other function anyway and we do not need them here now.
- Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD);
- }
- else
- Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD);
- NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
- switch(ShortBlock.HeaderType)
- {
- case HEAD_MAIN:
- MainHead.Reset();
- *(BaseBlock *)&MainHead=ShortBlock;
- MainHead.HighPosAV=Raw.Get2();
- MainHead.PosAV=Raw.Get4();
- Volume=(MainHead.Flags & MHD_VOLUME)!=0;
- Solid=(MainHead.Flags & MHD_SOLID)!=0;
- Locked=(MainHead.Flags & MHD_LOCK)!=0;
- Protected=(MainHead.Flags & MHD_PROTECT)!=0;
- Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0;
- Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0;
- MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0;
- // Only for encrypted 3.0+ archives. 2.x archives did not have this
- // flag, so for non-encrypted archives, we'll set it later based on
- // file attributes.
- FirstVolume=(MainHead.Flags & MHD_FIRSTVOLUME)!=0;
- NewNumbering=(MainHead.Flags & MHD_NEWNUMBERING)!=0;
- break;
- case HEAD_FILE:
- case HEAD_SERVICE:
- {
- bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
- FileHeader *hd=FileBlock ? &FileHead:&SubHead;
- hd->Reset();
- *(BaseBlock *)hd=ShortBlock;
- hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
- hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
- hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
- hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
- hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
- hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
- hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
- hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
- hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
- hd->Version=(hd->Flags & LHD_VERSION)!=0;
- hd->DataSize=Raw.Get4();
- uint LowUnpSize=Raw.Get4();
- hd->HostOS=Raw.Get1();
- hd->FileHash.Type=HASH_CRC32;
- hd->FileHash.CRC32=Raw.Get4();
- uint FileTime=Raw.Get4();
- hd->UnpVer=Raw.Get1();
- hd->Method=Raw.Get1()-0x30;
- size_t NameSize=Raw.Get2();
- hd->FileAttr=Raw.Get4();
- // RAR15 did not use the special dictionary size to mark dirs.
- if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0)
- hd->Dir=true;
- hd->CryptMethod=CRYPT_NONE;
- if (hd->Encrypted)
- switch(hd->UnpVer)
- {
- case 13: hd->CryptMethod=CRYPT_RAR13; break;
- case 15: hd->CryptMethod=CRYPT_RAR15; break;
- case 20:
- case 26: hd->CryptMethod=CRYPT_RAR20; break;
- default: hd->CryptMethod=CRYPT_RAR30; break;
- }
- hd->HSType=HSYS_UNKNOWN;
- if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS)
- hd->HSType=HSYS_UNIX;
- else
- if (hd->HostOS<HOST_MAX)
- hd->HSType=HSYS_WINDOWS;
- hd->RedirType=FSREDIR_NONE;
- // RAR 4.x Unix symlink.
- if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
- {
- hd->RedirType=FSREDIR_UNIXSYMLINK;
- *hd->RedirName=0;
- }
- hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
- hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
- uint HighPackSize,HighUnpSize;
- if (hd->LargeFile)
- {
- HighPackSize=Raw.Get4();
- HighUnpSize=Raw.Get4();
- hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
- }
- else
- {
- HighPackSize=HighUnpSize=0;
- // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
- // that we do not know the unpacked file size and must unpack it
- // until we find the end of file marker in compressed data.
- hd->UnknownUnpSize=(LowUnpSize==0xffffffff);
- }
- hd->PackSize=INT32TO64(HighPackSize,hd->DataSize);
- hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize);
- if (hd->UnknownUnpSize)
- hd->UnpSize=INT64NDF;
- char FileName[NM*4];
- size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
- Raw.GetB((byte *)FileName,ReadNameSize);
- FileName[ReadNameSize]=0;
- if (FileBlock)
- {
- *hd->FileName=0;
- if ((hd->Flags & LHD_UNICODE)!=0)
- {
- EncodeFileName NameCoder;
- size_t Length=strlen(FileName);
- Length++;
- if (ReadNameSize>Length)
- NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length,
- ReadNameSize-Length,hd->FileName,
- ASIZE(hd->FileName));
- }
- if (*hd->FileName==0)
- ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM);
- #ifndef SFX_MODULE
- ConvertNameCase(hd->FileName);
- #endif
- ConvertFileHeader(hd);
- }
- else
- {
- CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
- // Calculate the size of optional data.
- int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
- if ((hd->Flags & LHD_SALT)!=0)
- DataSize-=SIZE_SALT30;
- if (DataSize>0)
- {
- // Here we read optional additional fields for subheaders.
- // They are stored after the file name and before salt.
- hd->SubData.Alloc(DataSize);
- Raw.GetB(&hd->SubData[0],DataSize);
- }
- if (hd->CmpName(SUBHEAD_TYPE_CMT))
- MainComment=true;
- }
- if ((hd->Flags & LHD_SALT)!=0)
- Raw.GetB(hd->Salt,SIZE_SALT30);
- hd->mtime.SetDos(FileTime);
- if ((hd->Flags & LHD_EXTTIME)!=0)
- {
- ushort Flags=Raw.Get2();
- RarTime *tbl[4];
- tbl[0]=&FileHead.mtime;
- tbl[1]=&FileHead.ctime;
- tbl[2]=&FileHead.atime;
- tbl[3]=NULL; // Archive time is not used now.
- for (int I=0;I<4;I++)
- {
- RarTime *CurTime=tbl[I];
- uint rmode=Flags>>(3-I)*4;
- if ((rmode & 8)==0 || CurTime==NULL)
- continue;
- if (I!=0)
- {
- uint DosTime=Raw.Get4();
- CurTime->SetDos(DosTime);
- }
- RarLocalTime rlt;
- CurTime->GetLocal(&rlt);
- if (rmode & 4)
- rlt.Second++;
- rlt.Reminder=0;
- uint count=rmode&3;
- for (uint J=0;J<count;J++)
- {
- byte CurByte=Raw.Get1();
- rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8));
- }
- // Convert from 100ns RAR precision to REMINDER_PRECISION.
- rlt.Reminder*=RarTime::REMINDER_PRECISION/10000000;
- CurTime->SetLocal(&rlt);
- }
- }
- // Set to 0 in case of overflow, so end of ReadHeader cares about it.
- NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0);
- bool CRCProcessedOnly=hd->CommentInHeader;
- ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
- if (hd->HeadCRC!=HeaderCRC)
- {
- BrokenHeader=true;
- ErrHandler.SetErrorCode(RARX_WARNING);
- // If we have a broken encrypted header, we do not need to display
- // the error message here, because it will be displayed for such
- // headers later in this function. Also such headers are unlikely
- // to have anything sensible in file name field, so it is useless
- // to display the file name.
- if (!Decrypt)
- uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
- }
- }
- break;
- case HEAD_ENDARC:
- *(BaseBlock *)&EndArcHead=ShortBlock;
- EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
- EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
- EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
- EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0;
- if (EndArcHead.DataCRC)
- EndArcHead.ArcDataCRC=Raw.Get4();
- if (EndArcHead.StoreVolNumber)
- VolNumber=EndArcHead.VolNumber=Raw.Get2();
- break;
- #ifndef SFX_MODULE
- case HEAD3_CMT:
- *(BaseBlock *)&CommHead=ShortBlock;
- CommHead.UnpSize=Raw.Get2();
- CommHead.UnpVer=Raw.Get1();
- CommHead.Method=Raw.Get1();
- CommHead.CommCRC=Raw.Get2();
- break;
- case HEAD3_PROTECT:
- *(BaseBlock *)&ProtectHead=ShortBlock;
- ProtectHead.DataSize=Raw.Get4();
- ProtectHead.Version=Raw.Get1();
- ProtectHead.RecSectors=Raw.Get2();
- ProtectHead.TotalBlocks=Raw.Get4();
- Raw.GetB(ProtectHead.Mark,8);
- NextBlockPos+=ProtectHead.DataSize;
- break;
- case HEAD3_OLDSERVICE: // RAR 2.9 and earlier.
- *(BaseBlock *)&SubBlockHead=ShortBlock;
- SubBlockHead.DataSize=Raw.Get4();
- NextBlockPos+=SubBlockHead.DataSize;
- SubBlockHead.SubType=Raw.Get2();
- SubBlockHead.Level=Raw.Get1();
- switch(SubBlockHead.SubType)
- {
- case UO_HEAD:
- *(SubBlockHeader *)&UOHead=SubBlockHead;
- UOHead.OwnerNameSize=Raw.Get2();
- UOHead.GroupNameSize=Raw.Get2();
- if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
- UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
- if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
- UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
- Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
- Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
- UOHead.OwnerName[UOHead.OwnerNameSize]=0;
- UOHead.GroupName[UOHead.GroupNameSize]=0;
- break;
- case NTACL_HEAD:
- *(SubBlockHeader *)&EAHead=SubBlockHead;
- EAHead.UnpSize=Raw.Get4();
- EAHead.UnpVer=Raw.Get1();
- EAHead.Method=Raw.Get1();
- EAHead.EACRC=Raw.Get4();
- break;
- case STREAM_HEAD:
- *(SubBlockHeader *)&StreamHead=SubBlockHead;
- StreamHead.UnpSize=Raw.Get4();
- StreamHead.UnpVer=Raw.Get1();
- StreamHead.Method=Raw.Get1();
- StreamHead.StreamCRC=Raw.Get4();
- StreamHead.StreamNameSize=Raw.Get2();
- if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
- StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
- Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
- StreamHead.StreamName[StreamHead.StreamNameSize]=0;
- break;
- }
- break;
- #endif
- default:
- if (ShortBlock.Flags & LONG_BLOCK)
- NextBlockPos+=Raw.Get4();
- break;
- }
- ushort HeaderCRC=Raw.GetCRC15(false);
- // Old AV header does not have header CRC properly set.
- if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
- ShortBlock.HeaderType!=HEAD3_AV)
- {
- bool Recovered=false;
- if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
- {
- // Last 7 bytes of recovered volume can contain zeroes, because
- // REV files store its own information (volume number, etc.) here.
- int64 Length=Tell();
- Seek(Length-7,SEEK_SET);
- Recovered=true;
- for (int J=0;J<7;J++)
- if (GetByte()!=0)
- Recovered=false;
- }
- if (!Recovered)
- {
- BrokenHeader=true;
- ErrHandler.SetErrorCode(RARX_CRC);
- if (Decrypt)
- {
- uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
- FailedHeaderDecryption=true;
- return 0;
- }
- }
- }
- return Raw.Size();
- }
- size_t Archive::ReadHeader50()
- {
- RawRead Raw(this);
- bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5;
- if (Decrypt)
- {
- #if defined(RAR_NOCRYPT)
- return 0;
- #else
- if (Cmd->SkipEncrypted)
- {
- uiMsg(UIMSG_SKIPENCARC,FileName);
- FailedHeaderDecryption=true; // Suppress error messages and quit quietly.
- return 0;
- }
- byte HeadersInitV[SIZE_INITV];
- if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
- {
- UnexpEndArcMsg();
- return 0;
- }
- // We repeat the password request only for manually entered passwords
- // and not for -p<pwd>. Wrong password can be intentionally provided
- // in -p<pwd> to not stop batch processing for encrypted archives.
- bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
- while (true) // Repeat the password prompt for wrong passwords.
- {
- RequestArcPassword();
- byte PswCheck[SIZE_PSWCHECK];
- HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
- // Verify password validity.
- if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
- {
- if (GlobalPassword) // For -p<pwd> or Ctrl+P.
- {
- // This message is used by Android GUI to reset cached passwords.
- // Update appropriate code if changed.
- uiMsg(UIERROR_BADPSW,FileName,FileName);
- FailedHeaderDecryption=true;
- ErrHandler.SetErrorCode(RARX_BADPWD);
- return 0;
- }
- else // For passwords entered manually.
- {
- // This message is used by Android GUI and Windows GUI and SFX to
- // reset cached passwords. Update appropriate code if changed.
- uiMsg(UIWAIT_BADPSW,FileName,FileName);
- Cmd->Password.Clean();
- }
- #ifdef RARDLL
- // Avoid new requests for unrar.dll to prevent the infinite loop
- // if app always returns the same password.
- ErrHandler.SetErrorCode(RARX_BADPWD);
- Cmd->DllError=ERAR_BAD_PASSWORD;
- ErrHandler.Exit(RARX_BADPWD);
- #else
- continue; // Request a password again.
- #endif
- }
- break;
- }
- Raw.SetCrypt(&HeadersCrypt);
- #endif
- }
- // Header size must not occupy more than 3 variable length integer bytes
- // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5),
- // so here we read 4 byte CRC32 followed by 3 bytes or less of header size.
- const size_t FirstReadSize=7; // Smallest possible block size.
- if (Raw.Read(FirstReadSize)<FirstReadSize)
- {
- UnexpEndArcMsg();
- return 0;
- }
- ShortBlock.Reset();
- ShortBlock.HeadCRC=Raw.Get4();
- uint SizeBytes=Raw.GetVSize(4);
- uint64 BlockSize=Raw.GetV();
- if (BlockSize==0 || SizeBytes==0)
- {
- BrokenHeaderMsg();
- return 0;
- }
- int SizeToRead=int(BlockSize);
- SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
- uint HeaderSize=4+SizeBytes+(uint)BlockSize;
- if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5)
- {
- BrokenHeaderMsg();
- return 0;
- }
- Raw.Read(SizeToRead);
- if (Raw.Size()<HeaderSize)
- {
- UnexpEndArcMsg();
- return 0;
- }
- uint HeaderCRC=Raw.GetCRC50();
- ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV();
- ShortBlock.Flags=(uint)Raw.GetV();
- ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0;
- ShortBlock.HeadSize=HeaderSize;
- CurHeaderType=ShortBlock.HeaderType;
- bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC);
- if (BadCRC)
- {
- BrokenHeaderMsg(); // Report, but attempt to process.
- BrokenHeader=true;
- ErrHandler.SetErrorCode(RARX_CRC);
- if (Decrypt)
- {
- uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
- FailedHeaderDecryption=true;
- return 0;
- }
- }
- uint64 ExtraSize=0;
- if ((ShortBlock.Flags & HFL_EXTRA)!=0)
- {
- ExtraSize=Raw.GetV();
- if (ExtraSize>=ShortBlock.HeadSize)
- {
- BrokenHeaderMsg();
- return 0;
- }
- }
- uint64 DataSize=0;
- if ((ShortBlock.Flags & HFL_DATA)!=0)
- DataSize=Raw.GetV();
- NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
- // Set to 0 in case of overflow, so end of ReadHeader cares about it.
- NextBlockPos=SafeAdd(NextBlockPos,DataSize,0);
- switch(ShortBlock.HeaderType)
- {
- case HEAD_CRYPT:
- {
- *(BaseBlock *)&CryptHead=ShortBlock;
- uint CryptVersion=(uint)Raw.GetV();
- if (CryptVersion>CRYPT_VERSION)
- {
- wchar Info[20];
- swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
- UnkEncVerMsg(FileName,Info);
- return 0;
- }
- uint EncFlags=(uint)Raw.GetV();
- CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0;
- CryptHead.Lg2Count=Raw.Get1();
- if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
- {
- wchar Info[20];
- swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
- UnkEncVerMsg(FileName,Info);
- return 0;
- }
- Raw.GetB(CryptHead.Salt,SIZE_SALT50);
- if (CryptHead.UsePswCheck)
- {
- Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK);
- byte csum[SIZE_PSWCHECK_CSUM];
- Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
- sha256_context ctx;
- sha256_init(&ctx);
- sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
- byte Digest[SHA256_DIGEST_SIZE];
- sha256_done(&ctx, Digest);
- CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
- }
- Encrypted=true;
- }
- break;
- case HEAD_MAIN:
- {
- MainHead.Reset();
- *(BaseBlock *)&MainHead=ShortBlock;
- uint ArcFlags=(uint)Raw.GetV();
- Volume=(ArcFlags & MHFL_VOLUME)!=0;
- Solid=(ArcFlags & MHFL_SOLID)!=0;
- Locked=(ArcFlags & MHFL_LOCK)!=0;
- Protected=(ArcFlags & MHFL_PROTECT)!=0;
- Signed=false;
- NewNumbering=true;
- if ((ArcFlags & MHFL_VOLNUMBER)!=0)
- VolNumber=(uint)Raw.GetV();
- else
- VolNumber=0;
- FirstVolume=Volume && VolNumber==0;
- if (ExtraSize!=0)
- ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead);
- #ifdef USE_QOPEN
- if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
- {
- // We seek to QO block in the end of archive when processing
- // QOpen.Load, so we need to preserve current block positions
- // to not break normal archive processing by calling function.
- int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
- HEADER_TYPE SaveCurHeaderType=CurHeaderType;
- QOpen.Init(this,false);
- QOpen.Load(MainHead.QOpenOffset);
- CurBlockPos=SaveCurBlockPos;
- NextBlockPos=SaveNextBlockPos;
- CurHeaderType=SaveCurHeaderType;
- }
- #endif
- }
- break;
- case HEAD_FILE:
- case HEAD_SERVICE:
- {
- FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
- hd->Reset(); // Clear hash, time fields and other stuff like flags.
- *(BaseBlock *)hd=ShortBlock;
- bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
- hd->LargeFile=true;
- hd->PackSize=DataSize;
- hd->FileFlags=(uint)Raw.GetV();
- hd->UnpSize=Raw.GetV();
- hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
- if (hd->UnknownUnpSize)
- hd->UnpSize=INT64NDF;
- hd->MaxSize=Max(hd->PackSize,hd->UnpSize);
- hd->FileAttr=(uint)Raw.GetV();
- if ((hd->FileFlags & FHFL_UTIME)!=0)
- hd->mtime.SetUnix((time_t)Raw.Get4());
- hd->FileHash.Type=HASH_NONE;
- if ((hd->FileFlags & FHFL_CRC32)!=0)
- {
- hd->FileHash.Type=HASH_CRC32;
- hd->FileHash.CRC32=Raw.Get4();
- }
- hd->RedirType=FSREDIR_NONE;
- uint CompInfo=(uint)Raw.GetV();
- hd->Method=(CompInfo>>7) & 7;
- // "+ 50" to not mix with old RAR format algorithms. For example,
- // we may need to use the compression algorithm 15 in the future,
- // but it was already used in RAR 1.5 and Unpack needs to distinguish
- // them.
- hd->UnpVer=(CompInfo & 0x3f) + 50;
- if (hd->UnpVer!=50) // Only 5.0 compression is known now.
- hd->UnpVer=VER_UNKNOWN;
- hd->HostOS=(byte)Raw.GetV();
- size_t NameSize=(size_t)Raw.GetV();
- hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0;
- hd->HSType=HSYS_UNKNOWN;
- if (hd->HostOS==HOST5_UNIX)
- hd->HSType=HSYS_UNIX;
- else
- if (hd->HostOS==HOST5_WINDOWS)
- hd->HSType=HSYS_WINDOWS;
- hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0;
- hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0;
- hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
- hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
- hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
- hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
- hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
- char FileName[NM*4];
- size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
- Raw.GetB((byte *)FileName,ReadNameSize);
- FileName[ReadNameSize]=0;
- UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName));
- // Should do it before converting names, because extra fields can
- // affect name processing, like in case of NTFS streams.
- if (ExtraSize!=0)
- ProcessExtra50(&Raw,(size_t)ExtraSize,hd);
- if (FileBlock)
- {
- #ifndef SFX_MODULE
- ConvertNameCase(hd->FileName);
- #endif
- ConvertFileHeader(hd);
- }
- if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
- MainComment=true;
- #if 0
- // For RAR5 format we read the user specified recovery percent here.
- // It would be useful to do it for shell extension too, so we display
- // the correct recovery record size in archive properties. But then
- // we would need to include the entire recovery record processing
- // code to shell extension, which is not done now.
- if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0)
- {
- // It is stored as a single byte up to RAR 6.02 and as vint since
- // 6.10, where we extended the maximum RR size from 99% to 1000%.
- RawRead RawPercent;
- RawPercent.Read(&hd->SubData[0],hd->SubData.Size());
- RecoveryPercent=(int)RawPercent.GetV();
- RSBlockHeader Header;
- GetRRInfo(this,&Header);
- RecoverySize=Header.RecSectionSize*Header.RecCount;
- }
- #endif
- if (BadCRC) // Add the file name to broken header message displayed above.
- uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
- }
- break;
- case HEAD_ENDARC:
- {
- *(BaseBlock *)&EndArcHead=ShortBlock;
- uint ArcFlags=(uint)Raw.GetV();
- EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
- EndArcHead.StoreVolNumber=false;
- EndArcHead.DataCRC=false;
- EndArcHead.RevSpace=false;
- }
- break;
- }
- return Raw.Size();
- }
- #if !defined(RAR_NOCRYPT)
- void Archive::RequestArcPassword()
- {
- if (!Cmd->Password.IsSet())
- {
- #ifdef RARDLL
- if (Cmd->Callback!=NULL)
- {
- wchar PasswordW[MAXPASSWORD];
- *PasswordW=0;
- if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
- *PasswordW=0;
- if (*PasswordW==0)
- {
- char PasswordA[MAXPASSWORD];
- *PasswordA=0;
- if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
- *PasswordA=0;
- GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
- cleandata(PasswordA,sizeof(PasswordA));
- }
- Cmd->Password.Set(PasswordW);
- cleandata(PasswordW,sizeof(PasswordW));
- }
- if (!Cmd->Password.IsSet())
- {
- Close();
- Cmd->DllError=ERAR_MISSING_PASSWORD;
- ErrHandler.Exit(RARX_USERBREAK);
- }
- #else
- if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password))
- {
- Close();
- uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
- ErrHandler.Exit(RARX_USERBREAK);
- }
- #endif
- Cmd->ManualPassword=true;
- }
- }
- #endif
- void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
- {
- // Read extra data from the end of block skipping any fields before it.
- size_t ExtraStart=Raw->Size()-ExtraSize;
- if (ExtraStart<Raw->GetPos())
- return;
- Raw->SetPos(ExtraStart);
- while (Raw->DataLeft()>=2)
- {
- int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative.
- if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
- break;
- size_t NextPos=size_t(Raw->GetPos()+FieldSize);
- uint64 FieldType=Raw->GetV();
- FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields.
- if (FieldSize<0) // FieldType is longer than expected extra field size.
- break;
- if (bb->HeaderType==HEAD_MAIN)
- {
- MainHeader *hd=(MainHeader *)bb;
- if (FieldType==MHEXTRA_LOCATOR)
- {
- hd->Locator=true;
- uint Flags=(uint)Raw->GetV();
- if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
- {
- uint64 Offset=Raw->GetV();
- if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
- hd->QOpenOffset=Offset+CurBlockPos;
- }
- if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
- {
- uint64 Offset=Raw->GetV();
- if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
- hd->RROffset=Offset+CurBlockPos;
- }
- }
- }
- if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
- {
- FileHeader *hd=(FileHeader *)bb;
- switch(FieldType)
- {
- case FHEXTRA_CRYPT:
- {
- FileHeader *hd=(FileHeader *)bb;
- uint EncVersion=(uint)Raw->GetV();
- if (EncVersion>CRYPT_VERSION)
- {
- wchar Info[20];
- swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
- UnkEncVerMsg(hd->FileName,Info);
- }
- else
- {
- uint Flags=(uint)Raw->GetV();
- hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
- hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
- hd->Lg2Count=Raw->Get1();
- if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
- {
- wchar Info[20];
- swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
- UnkEncVerMsg(hd->FileName,Info);
- }
- Raw->GetB(hd->Salt,SIZE_SALT50);
- Raw->GetB(hd->InitV,SIZE_INITV);
- if (hd->UsePswCheck)
- {
- Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
- // It is important to know if password check data is valid.
- // If it is damaged and header CRC32 fails to detect it,
- // archiver would refuse to decompress a possibly valid file.
- // Since we want to be sure distinguishing a wrong password
- // or corrupt file data, we use 64-bit password check data
- // and to control its validity we use 32 bits of password
- // check data SHA-256 additionally to 32-bit header CRC32.
- byte csum[SIZE_PSWCHECK_CSUM];
- Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
- sha256_context ctx;
- sha256_init(&ctx);
- sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
- byte Digest[SHA256_DIGEST_SIZE];
- sha256_done(&ctx, Digest);
- hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
- // RAR 5.21 and earlier set PswCheck field in service records to 0
- // even if UsePswCheck was present.
- if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
- hd->UsePswCheck=0;
- }
- hd->SaltSet=true;
- hd->CryptMethod=CRYPT_RAR50;
- hd->Encrypted=true;
- }
- }
- break;
- case FHEXTRA_HASH:
- {
- FileHeader *hd=(FileHeader *)bb;
- uint Type=(uint)Raw->GetV();
- if (Type==FHEXTRA_HASH_BLAKE2)
- {
- hd->FileHash.Type=HASH_BLAKE2;
- Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
- }
- }
- break;
- case FHEXTRA_HTIME:
- if (FieldSize>=5)
- {
- byte Flags=(byte)Raw->GetV();
- bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
- if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
- if (UnixTime)
- hd->mtime.SetUnix(Raw->Get4());
- else
- hd->mtime.SetWin(Raw->Get8());
- if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
- if (UnixTime)
- hd->ctime.SetUnix(Raw->Get4());
- else
- hd->ctime.SetWin(Raw->Get8());
- if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
- if (UnixTime)
- hd->atime.SetUnix((time_t)Raw->Get4());
- else
- hd->atime.SetWin(Raw->Get8());
- if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds.
- {
- uint ns;
- if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
- hd->mtime.Adjust(ns);
- if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
- hd->ctime.Adjust(ns);
- if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
- hd->atime.Adjust(ns);
- }
- }
- break;
- case FHEXTRA_VERSION:
- if (FieldSize>=1)
- {
- Raw->GetV(); // Skip flags field.
- uint Version=(uint)Raw->GetV();
- if (Version!=0)
- {
- hd->Version=true;
- wchar VerText[20];
- swprintf(VerText,ASIZE(VerText),L";%u",Version);
- wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
- }
- }
- break;
- case FHEXTRA_REDIR:
- {
- hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
- uint Flags=(uint)Raw->GetV();
- hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
- size_t NameSize=(size_t)Raw->GetV();
- char UtfName[NM*4];
- *UtfName=0;
- if (NameSize<ASIZE(UtfName)-1)
- {
- Raw->GetB(UtfName,NameSize);
- UtfName[NameSize]=0;
- }
- #ifdef _WIN_ALL
- UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
- #endif
- UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
- }
- break;
- case FHEXTRA_UOWNER:
- {
- uint Flags=(uint)Raw->GetV();
- hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0;
- hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0;
- *hd->UnixOwnerName=*hd->UnixGroupName=0;
- if ((Flags & FHEXTRA_UOWNER_UNAME)!=0)
- {
- size_t Length=(size_t)Raw->GetV();
- Length=Min(Length,ASIZE(hd->UnixOwnerName)-1);
- Raw->GetB(hd->UnixOwnerName,Length);
- hd->UnixOwnerName[Length]=0;
- }
- if ((Flags & FHEXTRA_UOWNER_GNAME)!=0)
- {
- size_t Length=(size_t)Raw->GetV();
- Length=Min(Length,ASIZE(hd->UnixGroupName)-1);
- Raw->GetB(hd->UnixGroupName,Length);
- hd->UnixGroupName[Length]=0;
- }
- #ifdef _UNIX
- if (hd->UnixOwnerNumeric)
- hd->UnixOwnerID=(uid_t)Raw->GetV();
- if (hd->UnixGroupNumeric)
- hd->UnixGroupID=(gid_t)Raw->GetV();
- #else
- // Need these fields in Windows too for 'list' command,
- // but uid_t and gid_t are not defined.
- if (hd->UnixOwnerNumeric)
- hd->UnixOwnerID=(uint)Raw->GetV();
- if (hd->UnixGroupNumeric)
- hd->UnixGroupID=(uint)Raw->GetV();
- #endif
- hd->UnixOwnerSet=true;
- }
- break;
- case FHEXTRA_SUBDATA:
- {
- // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than
- // required. It did not hurt extraction, because UnRAR 5.21
- // and earlier ignored this field and set FieldSize as data left
- // in entire extra area. But now we set the correct field size
- // and set FieldSize based on the actual extra record size,
- // so we need to adjust it for those older archives here.
- // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE
- // and always is last in extra area. So since its size is by 1
- // less than needed, we always have 1 byte left in extra area,
- // which fact we use here to detect such archives.
- if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1)
- FieldSize++;
- // We cannot allocate too much memory here, because above
- // we check FieldSize againt Raw size and we control that Raw size
- // is sensible when reading headers.
- hd->SubData.Alloc((size_t)FieldSize);
- Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
- }
- break;
- }
- }
- Raw->SetPos(NextPos);
- }
- }
- #ifndef SFX_MODULE
- size_t Archive::ReadHeader14()
- {
- RawRead Raw(this);
- if (CurBlockPos<=(int64)SFXSize)
- {
- Raw.Read(SIZEOF_MAINHEAD14);
- MainHead.Reset();
- byte Mark[4];
- Raw.GetB(Mark,4);
- uint HeadSize=Raw.Get2();
- if (HeadSize<7)
- return false;
- byte Flags=Raw.Get1();
- NextBlockPos=CurBlockPos+HeadSize;
- CurHeaderType=HEAD_MAIN;
- Volume=(Flags & MHD_VOLUME)!=0;
- Solid=(Flags & MHD_SOLID)!=0;
- Locked=(Flags & MHD_LOCK)!=0;
- MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
- MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
- }
- else
- {
- Raw.Read(SIZEOF_FILEHEAD14);
- FileHead.Reset();
- FileHead.HeaderType=HEAD_FILE;
- FileHead.DataSize=Raw.Get4();
- FileHead.UnpSize=Raw.Get4();
- FileHead.FileHash.Type=HASH_RAR14;
- FileHead.FileHash.CRC32=Raw.Get2();
- FileHead.HeadSize=Raw.Get2();
- if (FileHead.HeadSize<21)
- return false;
- uint FileTime=Raw.Get4();
- FileHead.FileAttr=Raw.Get1();
- FileHead.Flags=Raw.Get1()|LONG_BLOCK;
- FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10;
- size_t NameSize=Raw.Get1();
- FileHead.Method=Raw.Get1();
- FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0;
- FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0;
- FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0;
- FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE;
- FileHead.PackSize=FileHead.DataSize;
- FileHead.WinSize=0x10000;
- FileHead.Dir=(FileHead.FileAttr & 0x10)!=0;
- FileHead.HostOS=HOST_MSDOS;
- FileHead.HSType=HSYS_WINDOWS;
- FileHead.mtime.SetDos(FileTime);
- Raw.Read(NameSize);
- char FileName[NM];
- size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
- Raw.GetB((byte *)FileName,ReadNameSize);
- FileName[ReadNameSize]=0;
- IntToExt(FileName,FileName,ASIZE(FileName));
- CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
- ConvertNameCase(FileHead.FileName);
- ConvertFileHeader(&FileHead);
- if (Raw.Size()!=0)
- NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
- CurHeaderType=HEAD_FILE;
- }
- return NextBlockPos>CurBlockPos ? Raw.Size() : 0;
- }
- #endif
- #ifndef SFX_MODULE
- void Archive::ConvertNameCase(wchar *Name)
- {
- if (Cmd->ConvertNames==NAMES_UPPERCASE)
- wcsupper(Name);
- if (Cmd->ConvertNames==NAMES_LOWERCASE)
- wcslower(Name);
- }
- #endif
- bool Archive::IsArcDir()
- {
- return FileHead.Dir;
- }
- void Archive::ConvertAttributes()
- {
- #if defined(_WIN_ALL) || defined(_EMX)
- if (FileHead.HSType!=HSYS_WINDOWS)
- FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
- #endif
- #ifdef _UNIX
- // umask defines which permission bits must not be set by default
- // when creating a file or directory. The typical default value
- // for the process umask is S_IWGRP | S_IWOTH (octal 022),
- // resulting in 0644 mode for new files.
- // Normally umask is applied automatically when creating a file,
- // but we set attributes with chmod later, so we need to calculate
- // resulting attributes here. We do it only for non-Unix archives.
- // We restore native Unix attributes as is, because it can be backup.
- static mode_t mask = (mode_t) -1;
- if (mask == (mode_t) -1)
- {
- // umask call returns the current umask value. Argument (022) is not
- // really important here.
- mask = umask(022);
- // Restore the original umask value, which was changed to 022 above.
- umask(mask);
- }
- switch(FileHead.HSType)
- {
- case HSYS_WINDOWS:
- {
- // Mapping MSDOS, OS/2 and Windows file attributes to Unix.
- if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
- {
- // For directories we use 0777 mask.
- FileHead.FileAttr=0777 & ~mask;
- }
- else
- if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY
- {
- // For read only files we use 0444 mask with 'w' bits turned off.
- FileHead.FileAttr=0444 & ~mask;
- }
- else
- {
- // umask does not set +x for regular files, so we use 0666
- // instead of 0777 as for directories.
- FileHead.FileAttr=0666 & ~mask;
- }
- }
- break;
- case HSYS_UNIX:
- break;
- default:
- if (FileHead.Dir)
- FileHead.FileAttr=0x41ff & ~mask;
- else
- FileHead.FileAttr=0x81b6 & ~mask;
- break;
- }
- #endif
- }
- void Archive::ConvertFileHeader(FileHeader *hd)
- {
- if (hd->HSType==HSYS_UNKNOWN)
- if (hd->Dir)
- hd->FileAttr=0x10;
- else
- hd->FileAttr=0x20;
- #ifdef _WIN_ALL
- if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
- ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
- #endif
- for (wchar *s=hd->FileName;*s!=0;s++)
- {
- #ifdef _UNIX
- // Backslash is the invalid character for Windows file headers,
- // but it can present in Unix file names extracted in Unix.
- if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS)
- *s='_';
- #endif
- #if defined(_WIN_ALL) || defined(_EMX)
- // RAR 5.0 archives do not use '\' as path separator, so if we see it,
- // it means that it is a part of Unix file name, which we cannot
- // extract in Windows.
- if (*s=='\\' && Format==RARFMT50)
- *s='_';
- // ':' in file names is allowed in Unix, but not in Windows.
- // Even worse, file data will be written to NTFS stream on NTFS,
- // so automatic name correction on file create error in extraction
- // routine does not work. In Windows and DOS versions we better
- // replace ':' now.
- if (*s==':')
- *s='_';
- #endif
- // This code must be performed only after other path separator checks,
- // because it produces backslashes illegal for some of checks above.
- // Backslash is allowed in file names in Unix, but not in Windows.
- // Still, RAR 4.x uses backslashes as path separator even in Unix.
- // Forward slash is not allowed in both systems. In RAR 5.0 we use
- // the forward slash as universal path separator.
- if (*s=='/' || *s=='\\' && Format!=RARFMT50)
- *s=CPATHDIVIDER;
- }
- }
- int64 Archive::GetStartPos()
- {
- int64 StartPos=SFXSize+MarkHead.HeadSize;
- if (Format==RARFMT15)
- StartPos+=MainHead.HeadSize;
- else // RAR 5.0.
- StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize);
- return StartPos;
- }
- bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
- {
- if (BrokenHeader)
- {
- uiMsg(UIERROR_SUBHEADERBROKEN,FileName);
- ErrHandler.SetErrorCode(RARX_CRC);
- return false;
- }
- if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
- {
- uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
- return false;
- }
- if (SubHead.PackSize==0 && !SubHead.SplitAfter)
- return true;
- SubDataIO.Init();
- Unpack Unpack(&SubDataIO);
- Unpack.Init(SubHead.WinSize,false);
- if (DestFile==NULL)
- {
- if (SubHead.UnpSize>0x1000000)
- {
- // So huge allocation must never happen in valid archives.
- uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
- return false;
- }
- if (UnpData==NULL)
- SubDataIO.SetTestMode(true);
- else
- {
- UnpData->Alloc((size_t)SubHead.UnpSize);
- SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
- }
- }
- if (SubHead.Encrypted)
- if (Cmd->Password.IsSet())
- SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password,
- SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV,
- SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck);
- else
- return false;
- SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1);
- SubDataIO.SetPackedSizeToRead(SubHead.PackSize);
- SubDataIO.EnableShowProgress(false);
- SubDataIO.SetFiles(this,DestFile);
- SubDataIO.SetTestMode(TestMode);
- SubDataIO.UnpVolume=SubHead.SplitAfter;
- SubDataIO.SetSubHeader(&SubHead,NULL);
- Unpack.SetDestSize(SubHead.UnpSize);
- if (SubHead.Method==0)
- CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize);
- else
- Unpack.DoUnpack(SubHead.UnpVer,false);
- if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL))
- {
- uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
- ErrHandler.SetErrorCode(RARX_CRC);
- if (UnpData!=NULL)
- UnpData->Reset();
- return false;
- }
- return true;
- }
|