// EnumDirItems.cpp#include"StdAfx.h"#include<wchar.h>#include"../../../Common/Wildcard.h"#include"../../../Windows/FileDir.h"#include"../../../Windows/FileIO.h"#include"../../../Windows/FileName.h"#if defined(_WIN32) && !defined(UNDER_CE)#define _USE_SECURITY_CODE#include"../../../Windows/SecurityUtils.h"#endif#include"EnumDirItems.h"usingnamespaceNWindows;usingnamespaceNFile;usingnamespaceNName;voidCDirItems::AddDirFileInfo(intphyParent,intlogParent,intsecureIndex,constNFind::CFileInfo&fi){CDirItemdi;di.Size=fi.Size;di.CTime=fi.CTime;di.ATime=fi.ATime;di.MTime=fi.MTime;di.Attrib=fi.Attrib;di.IsAltStream=fi.IsAltStream;di.PhyParent=phyParent;di.LogParent=logParent;di.SecureIndex=secureIndex;di.Name=fs2us(fi.Name);#if defined(_WIN32) && !defined(UNDER_CE)// di.ShortName = fs2us(fi.ShortName);#endifItems.Add(di);if(fi.IsDir())Stat.NumDirs++;elseif(fi.IsAltStream){Stat.NumAltStreams++;Stat.AltStreamsSize+=fi.Size;}else{Stat.NumFiles++;Stat.FilesSize+=fi.Size;}}HRESULTCDirItems::AddError(constFString&path,DWORDerrorCode){Stat.NumErrors++;if(Callback)returnCallback->ScanError(path,errorCode);returnS_OK;}HRESULTCDirItems::AddError(constFString&path){returnAddError(path,::GetLastError());}staticconstunsignedkScanProgressStepMask=(1<<12)-1;HRESULTCDirItems::ScanProgress(constFString&dirPath){if(Callback)returnCallback->ScanProgress(Stat,dirPath,true);returnS_OK;}UStringCDirItems::GetPrefixesPath(constCIntVector&parents,intindex,constUString&name)const{UStringpath;unsignedlen=name.Len();inti;for(i=index;i>=0;i=parents[i])len+=Prefixes[i].Len();wchar_t*p=path.GetBuf_SetEnd(len)+len;p-=name.Len();wmemcpy(p,(constwchar_t*)name,name.Len());for(i=index;i>=0;i=parents[i]){constUString&s=Prefixes[i];p-=s.Len();wmemcpy(p,(constwchar_t*)s,s.Len());}returnpath;}FStringCDirItems::GetPhyPath(unsignedindex)const{constCDirItem&di=Items[index];returnus2fs(GetPrefixesPath(PhyParents,di.PhyParent,di.Name));}UStringCDirItems::GetLogPath(unsignedindex)const{constCDirItem&di=Items[index];returnGetPrefixesPath(LogParents,di.LogParent,di.Name);}voidCDirItems::ReserveDown(){Prefixes.ReserveDown();PhyParents.ReserveDown();LogParents.ReserveDown();Items.ReserveDown();}unsignedCDirItems::AddPrefix(intphyParent,intlogParent,constUString&prefix){PhyParents.Add(phyParent);LogParents.Add(logParent);returnPrefixes.Add(prefix);}voidCDirItems::DeleteLastPrefix(){PhyParents.DeleteBack();LogParents.DeleteBack();Prefixes.DeleteBack();}boolInitLocalPrivileges();CDirItems::CDirItems():SymLinks(false),ScanAltStreams(false)#ifdef _USE_SECURITY_CODE,ReadSecure(false)#endif,Callback(NULL){#ifdef _USE_SECURITY_CODE_saclEnabled=InitLocalPrivileges();#endif}#ifdef _USE_SECURITY_CODEHRESULTCDirItems::AddSecurityItem(constFString&path,int&secureIndex){secureIndex=-1;SECURITY_INFORMATIONsecurInfo=DACL_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|OWNER_SECURITY_INFORMATION;if(_saclEnabled)securInfo|=SACL_SECURITY_INFORMATION;DWORDerrorCode=0;DWORDsecureSize;BOOLres=::GetFileSecurityW(fs2us(path),securInfo,(PSECURITY_DESCRIPTOR)(Byte*)TempSecureBuf,(DWORD)TempSecureBuf.Size(),&secureSize);if(res){if(secureSize==0)returnS_OK;if(secureSize>TempSecureBuf.Size())errorCode=ERROR_INVALID_FUNCTION;}else{errorCode=GetLastError();if(errorCode==ERROR_INSUFFICIENT_BUFFER){if(secureSize<=TempSecureBuf.Size())errorCode=ERROR_INVALID_FUNCTION;else{TempSecureBuf.Alloc(secureSize);res=::GetFileSecurityW(fs2us(path),securInfo,(PSECURITY_DESCRIPTOR)(Byte*)TempSecureBuf,(DWORD)TempSecureBuf.Size(),&secureSize);if(res){if(secureSize!=TempSecureBuf.Size())errorCode=ERROR_INVALID_FUNCTION;;}elseerrorCode=GetLastError();}}}if(res){secureIndex=SecureBlocks.AddUniq(TempSecureBuf,secureSize);returnS_OK;}if(errorCode==0)errorCode=ERROR_INVALID_FUNCTION;returnAddError(path,errorCode);}#endifHRESULTCDirItems::EnumerateDir(intphyParent,intlogParent,constFString&phyPrefix){RINOK(ScanProgress(phyPrefix));NFind::CEnumeratorenumerator(phyPrefix+FCHAR_ANY_MASK);for(unsignedttt=0;;ttt++){NFind::CFileInfofi;boolfound;if(!enumerator.Next(fi,found)){returnAddError(phyPrefix);}if(!found)returnS_OK;intsecureIndex=-1;#ifdef _USE_SECURITY_CODEif(ReadSecure){RINOK(AddSecurityItem(phyPrefix+fi.Name,secureIndex));}#endifAddDirFileInfo(phyParent,logParent,secureIndex,fi);if(Callback&&(ttt&kScanProgressStepMask)==kScanProgressStepMask){RINOK(ScanProgress(phyPrefix));}if(fi.IsDir()){constFStringname2=fi.Name+FCHAR_PATH_SEPARATOR;unsignedparent=AddPrefix(phyParent,logParent,fs2us(name2));RINOK(EnumerateDir(parent,parent,phyPrefix+name2));}}}HRESULTCDirItems::EnumerateItems2(constFString&phyPrefix,constUString&logPrefix,constFStringVector&filePaths,FStringVector*requestedPaths){intphyParent=phyPrefix.IsEmpty()?-1:AddPrefix(-1,-1,fs2us(phyPrefix));intlogParent=logPrefix.IsEmpty()?-1:AddPrefix(-1,-1,logPrefix);FOR_VECTOR(i,filePaths){constFString&filePath=filePaths[i];NFind::CFileInfofi;constFStringphyPath=phyPrefix+filePath;if(!fi.Find(phyPath)){RINOK(AddError(phyPath));continue;}if(requestedPaths)requestedPaths->Add(phyPath);intdelimiter=filePath.ReverseFind_PathSepar();FStringphyPrefixCur;intphyParentCur=phyParent;if(delimiter>=0){phyPrefixCur.SetFrom(filePath,delimiter+1);phyParentCur=AddPrefix(phyParent,logParent,fs2us(phyPrefixCur));}intsecureIndex=-1;#ifdef _USE_SECURITY_CODEif(ReadSecure){RINOK(AddSecurityItem(phyPath,secureIndex));}#endifAddDirFileInfo(phyParentCur,logParent,secureIndex,fi);if(fi.IsDir()){constFStringname2=fi.Name+FCHAR_PATH_SEPARATOR;unsignedparent=AddPrefix(phyParentCur,logParent,fs2us(name2));RINOK(EnumerateDir(parent,parent,phyPrefix+phyPrefixCur+name2));}}ReserveDown();returnS_OK;}staticHRESULTEnumerateDirItems(constNWildcard::CCensorNode&curNode,intphyParent,intlogParent,constFString&phyPrefix,constUStringVector&addArchivePrefix,CDirItems&dirItems,boolenterToSubFolders);staticHRESULTEnumerateDirItems_Spec(constNWildcard::CCensorNode&curNode,intphyParent,intlogParent,constFString&curFolderName,constFString&phyPrefix,constUStringVector&addArchivePrefix,CDirItems&dirItems,boolenterToSubFolders){constFStringname2=curFolderName+FCHAR_PATH_SEPARATOR;unsignedparent=dirItems.AddPrefix(phyParent,logParent,fs2us(name2));unsignednumItems=dirItems.Items.Size();HRESULTres=EnumerateDirItems(curNode,parent,parent,phyPrefix+name2,addArchivePrefix,dirItems,enterToSubFolders);if(numItems==dirItems.Items.Size())dirItems.DeleteLastPrefix();returnres;}#ifndef UNDER_CE#ifdef _WIN32staticHRESULTEnumerateAltStreams(constNFind::CFileInfo&fi,constNWildcard::CCensorNode&curNode,intphyParent,intlogParent,constFString&fullPath,constUStringVector&addArchivePrefix,// prefix from curNodeCDirItems&dirItems){NFind::CStreamEnumeratorenumerator(fullPath);for(;;){NFind::CStreamInfosi;boolfound;if(!enumerator.Next(si,found)){returndirItems.AddError(fullPath+FTEXT(":*"));// , (DWORD)E_FAIL}if(!found)returnS_OK;if(si.IsMainStream())continue;UStringVectoraddArchivePrefixNew=addArchivePrefix;UStringreducedName=si.GetReducedName();addArchivePrefixNew.Back()+=reducedName;if(curNode.CheckPathToRoot(false,addArchivePrefixNew,true))continue;NFind::CFileInfofi2=fi;fi2.Name+=us2fs(reducedName);fi2.Size=si.Size;fi2.Attrib&=~FILE_ATTRIBUTE_DIRECTORY;fi2.IsAltStream=true;dirItems.AddDirFileInfo(phyParent,logParent,-1,fi2);}}#endifHRESULTCDirItems::SetLinkInfo(CDirItem&dirItem,constNFind::CFileInfo&fi,constFString&phyPrefix){if(!SymLinks||!fi.HasReparsePoint())returnS_OK;constFStringpath=phyPrefix+fi.Name;CByteBuffer&buf=dirItem.ReparseData;if(NIO::GetReparseData(path,buf)){CReparseAttrattr;if(attr.Parse(buf,buf.Size()))returnS_OK;}DWORDres=::GetLastError();buf.Free();returnAddError(path,res);}#endifstaticHRESULTEnumerateForItem(NFind::CFileInfo&fi,constNWildcard::CCensorNode&curNode,intphyParent,intlogParent,constFString&phyPrefix,constUStringVector&addArchivePrefix,// prefix from curNodeCDirItems&dirItems,boolenterToSubFolders){constUStringname=fs2us(fi.Name);boolenterToSubFolders2=enterToSubFolders;UStringVectoraddArchivePrefixNew=addArchivePrefix;addArchivePrefixNew.Add(name);{UStringVectoraddArchivePrefixNewTemp(addArchivePrefixNew);if(curNode.CheckPathToRoot(false,addArchivePrefixNewTemp,!fi.IsDir()))returnS_OK;}intdirItemIndex=-1;if(curNode.CheckPathToRoot(true,addArchivePrefixNew,!fi.IsDir())){intsecureIndex=-1;#ifdef _USE_SECURITY_CODEif(dirItems.ReadSecure){RINOK(dirItems.AddSecurityItem(phyPrefix+fi.Name,secureIndex));}#endifdirItemIndex=dirItems.Items.Size();dirItems.AddDirFileInfo(phyParent,logParent,secureIndex,fi);if(fi.IsDir())enterToSubFolders2=true;}#ifndef UNDER_CEif(dirItems.ScanAltStreams){RINOK(EnumerateAltStreams(fi,curNode,phyParent,logParent,phyPrefix+fi.Name,addArchivePrefixNew,dirItems));}if(dirItemIndex>=0){CDirItem&dirItem=dirItems.Items[dirItemIndex];RINOK(dirItems.SetLinkInfo(dirItem,fi,phyPrefix));if(dirItem.ReparseData.Size()!=0)returnS_OK;}#endifif(!fi.IsDir())returnS_OK;constNWildcard::CCensorNode*nextNode=0;if(addArchivePrefix.IsEmpty()){intindex=curNode.FindSubNode(name);if(index>=0)nextNode=&curNode.SubNodes[index];}if(!enterToSubFolders2&&nextNode==0)returnS_OK;addArchivePrefixNew=addArchivePrefix;if(nextNode==0){nextNode=&curNode;addArchivePrefixNew.Add(name);}returnEnumerateDirItems_Spec(*nextNode,phyParent,logParent,fi.Name,phyPrefix,addArchivePrefixNew,dirItems,enterToSubFolders2);}staticboolCanUseFsDirect(constNWildcard::CCensorNode&curNode){FOR_VECTOR(i,curNode.IncludeItems){constNWildcard::CItem&item=curNode.IncludeItems[i];if(item.Recursive||item.PathParts.Size()!=1)returnfalse;constUString&name=item.PathParts.Front();/* if (name.IsEmpty()) return false; *//* Windows doesn't support file name with wildcard But if another system supports file name with wildcard, and wildcard mode is disabled, we can ignore wildcard in name *//* if (!item.WildcardParsing) continue; */if(DoesNameContainWildcard(name))returnfalse;}returntrue;}#if defined(_WIN32) && !defined(UNDER_CE)staticboolIsVirtualFsFolder(constFString&prefix,constUString&name){UStrings=fs2us(prefix);s+=name;s.Add_PathSepar();returnIsPathSepar(s[0])&&GetRootPrefixSize(s)==0;}#endifstaticHRESULTEnumerateDirItems(constNWildcard::CCensorNode&curNode,intphyParent,intlogParent,constFString&phyPrefix,constUStringVector&addArchivePrefix,// prefix from curNodeCDirItems&dirItems,boolenterToSubFolders){if(!enterToSubFolders)if(curNode.NeedCheckSubDirs())enterToSubFolders=true;RINOK(dirItems.ScanProgress(phyPrefix));// try direct_names case at firstif(addArchivePrefix.IsEmpty()&&!enterToSubFolders){if(CanUseFsDirect(curNode)){// all names are direct (no wildcards)// so we don't need file_system's dir enumeratorCRecordVector<bool>needEnterVector;unsignedi;for(i=0;i<curNode.IncludeItems.Size();i++){constNWildcard::CItem&item=curNode.IncludeItems[i];constUString&name=item.PathParts.Front();FStringfullPath=phyPrefix+us2fs(name);#if defined(_WIN32) && !defined(UNDER_CE)boolneedAltStreams=true;#endif#ifdef _USE_SECURITY_CODEboolneedSecurity=true;#endifif(phyPrefix.IsEmpty()){if(!item.ForFile){/* we don't like some names for alt streams inside archive: ":sname" for "\" "c:::sname" for "C:\" So we ignore alt streams for these cases */if(name.IsEmpty()){#if defined(_WIN32) && !defined(UNDER_CE)needAltStreams=false;#endif/* // do we need to ignore security info for "\\" folder ? #ifdef _USE_SECURITY_CODE needSecurity = false; #endif */fullPath=FCHAR_PATH_SEPARATOR;}#if defined(_WIN32) && !defined(UNDER_CE)elseif(item.IsDriveItem()){needAltStreams=false;fullPath.Add_PathSepar();}#endif}}NFind::CFileInfofi;#if defined(_WIN32) && !defined(UNDER_CE)if(IsVirtualFsFolder(phyPrefix,name)){fi.SetAsDir();fi.Name=us2fs(name);}else#endifif(!fi.Find(fullPath)){RINOK(dirItems.AddError(fullPath));continue;}boolisDir=fi.IsDir();if(isDir&&!item.ForDir||!isDir&&!item.ForFile){RINOK(dirItems.AddError(fullPath,(DWORD)E_FAIL));continue;}{UStringVectorpathParts;pathParts.Add(fs2us(fi.Name));if(curNode.CheckPathToRoot(false,pathParts,!isDir))continue;}intsecureIndex=-1;#ifdef _USE_SECURITY_CODEif(needSecurity&&dirItems.ReadSecure){RINOK(dirItems.AddSecurityItem(fullPath,secureIndex));}#endifdirItems.AddDirFileInfo(phyParent,logParent,secureIndex,fi);#ifndef UNDER_CE{CDirItem&dirItem=dirItems.Items.Back();RINOK(dirItems.SetLinkInfo(dirItem,fi,phyPrefix));if(dirItem.ReparseData.Size()!=0){if(fi.IsAltStream)dirItems.Stat.AltStreamsSize-=fi.Size;elsedirItems.Stat.FilesSize-=fi.Size;continue;}}#endif#ifndef UNDER_CEif(needAltStreams&&dirItems.ScanAltStreams){UStringVectorpathParts;pathParts.Add(fs2us(fi.Name));RINOK(EnumerateAltStreams(fi,curNode,phyParent,logParent,fullPath,pathParts,dirItems));}#endifif(!isDir)continue;UStringVectoraddArchivePrefixNew;constNWildcard::CCensorNode*nextNode=0;intindex=curNode.FindSubNode(name);if(index>=0){for(intt=needEnterVector.Size();t<=index;t++)needEnterVector.Add(true);needEnterVector[index]=false;nextNode=&curNode.SubNodes[index];}else{nextNode=&curNode;addArchivePrefixNew.Add(name);// don't change it to fi.Name. It's for shortnames support}RINOK(EnumerateDirItems_Spec(*nextNode,phyParent,logParent,fi.Name,phyPrefix,addArchivePrefixNew,dirItems,true));}for(i=0;i<curNode.SubNodes.Size();i++){if(i<needEnterVector.Size())if(!needEnterVector[i])continue;constNWildcard::CCensorNode&nextNode=curNode.SubNodes[i];FStringfullPath=phyPrefix+us2fs(nextNode.Name);NFind::CFileInfofi;if(phyPrefix.IsEmpty()){{if(nextNode.Name.IsEmpty())fullPath=FCHAR_PATH_SEPARATOR;#ifdef _WIN32elseif(NWildcard::IsDriveColonName(nextNode.Name))fullPath.Add_PathSepar();#endif}}// we don't want to call fi.Find() for root folder or virtual folderif(phyPrefix.IsEmpty()&&nextNode.Name.IsEmpty()#if defined(_WIN32) && !defined(UNDER_CE)||IsVirtualFsFolder(phyPrefix,nextNode.Name)#endif){fi.SetAsDir();fi.Name=us2fs(nextNode.Name);}else{if(!fi.Find(fullPath)){if(!nextNode.AreThereIncludeItems())continue;RINOK(dirItems.AddError(fullPath));continue;}if(!fi.IsDir()){RINOK(dirItems.AddError(fullPath,(DWORD)E_FAIL));continue;}}RINOK(EnumerateDirItems_Spec(nextNode,phyParent,logParent,fi.Name,phyPrefix,UStringVector(),dirItems,false));}returnS_OK;}}#ifdef _WIN32#ifndef UNDER_CE// scan drives, if wildcard is "*:\"if(phyPrefix.IsEmpty()&&curNode.IncludeItems.Size()>0){unsignedi;for(i=0;i<curNode.IncludeItems.Size();i++){constNWildcard::CItem&item=curNode.IncludeItems[i];if(item.PathParts.Size()<1)break;constUString&name=item.PathParts.Front();if(name.Len()!=2||name[1]!=':')break;if(item.PathParts.Size()==1)if(item.ForFile||!item.ForDir)break;if(NWildcard::IsDriveColonName(name))continue;if(name[0]!='*'&&name[0]!='?')break;}if(i==curNode.IncludeItems.Size()){FStringVectordriveStrings;NFind::MyGetLogicalDriveStrings(driveStrings);for(i=0;i<driveStrings.Size();i++){FStringdriveName=driveStrings[i];if(driveName.Len()<3||driveName.Back()!='\\')returnE_FAIL;driveName.DeleteBack();NFind::CFileInfofi;fi.SetAsDir();fi.Name=driveName;RINOK(EnumerateForItem(fi,curNode,phyParent,logParent,phyPrefix,addArchivePrefix,dirItems,enterToSubFolders));}returnS_OK;}}#endif#endifNFind::CEnumeratorenumerator(phyPrefix+FCHAR_ANY_MASK);for(unsignedttt=0;;ttt++){NFind::CFileInfofi;boolfound;if(!enumerator.Next(fi,found)){RINOK(dirItems.AddError(phyPrefix));break;}if(!found)break;if(dirItems.Callback&&(ttt&kScanProgressStepMask)==kScanProgressStepMask){RINOK(dirItems.ScanProgress(phyPrefix));}RINOK(EnumerateForItem(fi,curNode,phyParent,logParent,phyPrefix,addArchivePrefix,dirItems,enterToSubFolders));}returnS_OK;}HRESULTEnumerateItems(constNWildcard::CCensor&censor,constNWildcard::ECensorPathModepathMode,constUString&addPathPrefix,CDirItems&dirItems){FOR_VECTOR(i,censor.Pairs){constNWildcard::CPair&pair=censor.Pairs[i];intphyParent=pair.Prefix.IsEmpty()?-1:dirItems.AddPrefix(-1,-1,pair.Prefix);intlogParent=-1;if(pathMode==NWildcard::k_AbsPath)logParent=phyParent;else{if(!addPathPrefix.IsEmpty())logParent=dirItems.AddPrefix(-1,-1,addPathPrefix);}RINOK(EnumerateDirItems(pair.Head,phyParent,logParent,us2fs(pair.Prefix),UStringVector(),dirItems,false// enterToSubFolders));}dirItems.ReserveDown();#if defined(_WIN32) && !defined(UNDER_CE)dirItems.FillFixedReparse();#endifreturnS_OK;}#if defined(_WIN32) && !defined(UNDER_CE)voidCDirItems::FillFixedReparse(){/* imagex/WIM reduces absolute pathes in links (raparse data), if we archive non root folder. We do same thing here */if(!SymLinks)return;FOR_VECTOR(i,Items){CDirItem&item=Items[i];if(item.ReparseData.Size()==0)continue;CReparseAttrattr;if(!attr.Parse(item.ReparseData,item.ReparseData.Size()))continue;if(attr.IsRelative())continue;constUString&link=attr.GetPath();if(!IsDrivePath(link))continue;// maybe we need to support networks paths also ?FStringfullPathF;if(!NDir::MyGetFullPathName(GetPhyPath(i),fullPathF))continue;UStringfullPath=fs2us(fullPathF);constUStringlogPath=GetLogPath(i);if(logPath.Len()>=fullPath.Len())continue;if(CompareFileNames(logPath,fullPath.RightPtr(logPath.Len()))!=0)continue;constUStringprefix=fullPath.Left(fullPath.Len()-logPath.Len());if(!IsPathSepar(prefix.Back()))continue;unsignedrootPrefixSize=GetRootPrefixSize(prefix);if(rootPrefixSize==0)continue;if(rootPrefixSize==prefix.Len())continue;// simple case: paths are from rootif(link.Len()<=prefix.Len())continue;if(CompareFileNames(link.Left(prefix.Len()),prefix)!=0)continue;UStringnewLink=prefix.Left(rootPrefixSize);newLink+=link.Ptr(prefix.Len());CByteBufferdata;if(!FillLinkData(data,newLink,attr.IsSymLink()))continue;item.ReparseData2=data;}}#endif