| CPP/7zip/UI/Agent/Agent.cpp | 2 +- |
| CPP/7zip/UI/Client7z/Client7z.cpp | 19 ++++++++++- |
| CPP/7zip/UI/Common/ArchiveExtractCallback.cpp | 14 +++++++-- |
| CPP/7zip/UI/Common/ArchiveExtractCallback.h | 6 +++- |
| CPP/7zip/UI/Common/Extract.cpp | 2 +- |
| CPP/Windows/FileDir.cpp | 45 +++++++++++++++++++++++++-- |
| CPP/Windows/FileDir.h | 31 +++++++++++++++++- |
| 7 files changed, 109 insertions(+), 10 deletions(-) |
| |
| diff --git a/CPP/7zip/UI/Agent/Agent.cpp b/CPP/7zip/UI/Agent/Agent.cpp |
| index 201e82c..b0b8316 100644 |
| --- a/CPP/7zip/UI/Agent/Agent.cpp |
| +++ b/CPP/7zip/UI/Agent/Agent.cpp |
| @@ -1515,7 +1515,7 @@ STDMETHODIMP CAgentFolder::Extract(const UInt32 *indices, |
| HRESULT result = _agentSpec->GetArchive()->Extract(&realIndices.Front(), |
| realIndices.Size(), testMode, extractCallback); |
| if (result == S_OK) |
| - result = extractCallbackSpec->SetDirsTimes(); |
| + result = extractCallbackSpec->SetFinalAttribs(); |
| return result; |
| COM_TRY_END |
| } |
| diff --git a/CPP/7zip/UI/Client7z/Client7z.cpp b/CPP/7zip/UI/Client7z/Client7z.cpp |
| index c6522fe..1919389 100644 |
| --- a/CPP/7zip/UI/Client7z/Client7z.cpp |
| +++ b/CPP/7zip/UI/Client7z/Client7z.cpp |
| @@ -230,8 +230,11 @@ private: |
| COutFileStream *_outFileStreamSpec; |
| CMyComPtr<ISequentialOutStream> _outFileStream; |
| |
| + CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks; |
| + |
| public: |
| void Init(IInArchive *archiveHandler, const FString &directoryPath); |
| + HRESULT SetFinalAttribs(); |
| |
| UInt64 NumErrors; |
| bool PasswordIsDefined; |
| @@ -449,11 +452,23 @@ STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult) |
| } |
| _outFileStream.Release(); |
| if (_extractMode && _processedFileInfo.AttribDefined) |
| - SetFileAttrib(_diskFilePath, _processedFileInfo.Attrib); |
| + SetFileAttrib(_diskFilePath, _processedFileInfo.Attrib, &_delayedSymLinks); |
| PrintNewLine(); |
| return S_OK; |
| } |
| |
| +HRESULT CArchiveExtractCallback::SetFinalAttribs() |
| +{ |
| + HRESULT result = S_OK; |
| + |
| + for (int i = 0; i != _delayedSymLinks.Size(); ++i) |
| + if (!_delayedSymLinks[i].Create()) |
| + result = E_FAIL; |
| + |
| + _delayedSymLinks.Clear(); |
| + |
| + return result; |
| +} |
| |
| STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password) |
| { |
| @@ -914,6 +929,8 @@ int MY_CDECL main(int numArgs, const char *args[]) |
| // extractCallbackSpec->PasswordIsDefined = true; |
| // extractCallbackSpec->Password = L"1"; |
| HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback); |
| + if (result == S_OK) |
| + result = extractCallbackSpec->SetFinalAttribs(); |
| if (result != S_OK) |
| { |
| PrintError("Extract Error"); |
| diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp |
| index 877326b..05b13f3 100644 |
| --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp |
| +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp |
| @@ -1502,7 +1502,7 @@ STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 opRes) |
| NumFiles++; |
| |
| if (!_stdOutMode && _extractMode && _fi.AttribDefined) |
| - SetFileAttrib(_diskFilePath, _fi.Attrib); |
| + SetFileAttrib(_diskFilePath, _fi.Attrib, &_delayedSymLinks); |
| |
| RINOK(_extractCallback2->SetOperationResult(opRes, BoolToInt(_encrypted))); |
| |
| @@ -1584,8 +1584,9 @@ static unsigned GetNumSlashes(const FChar *s) |
| } |
| } |
| |
| -HRESULT CArchiveExtractCallback::SetDirsTimes() |
| +HRESULT CArchiveExtractCallback::SetFinalAttribs() |
| { |
| + HRESULT result = S_OK; |
| CRecordVector<CExtrRefSortPair> pairs; |
| pairs.ClearAndSetSize(_extractedFolderPaths.Size()); |
| unsigned i; |
| @@ -1622,5 +1623,12 @@ HRESULT CArchiveExtractCallback::SetDirsTimes() |
| (WriteATime && ATimeDefined) ? &ATime : NULL, |
| (WriteMTime && MTimeDefined) ? &MTime : (_arc->MTimeDefined ? &_arc->MTime : NULL)); |
| } |
| - return S_OK; |
| + |
| + for (int i = 0; i != _delayedSymLinks.Size(); ++i) |
| + if (!_delayedSymLinks[i].Create()) |
| + result = E_FAIL; |
| + |
| + _delayedSymLinks.Clear(); |
| + |
| + return result; |
| } |
| diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/CPP/7zip/UI/Common/ArchiveExtractCallback.h |
| index 1e9f0b6..b51d78f 100644 |
| --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.h |
| +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.h |
| @@ -6,6 +6,8 @@ |
| #include "../../../Common/MyCom.h" |
| #include "../../../Common/Wildcard.h" |
| |
| +#include "../../../Windows/FileDir.h" |
| + |
| #include "../../IPassword.h" |
| |
| #include "../../Common/FileStreams.h" |
| @@ -237,6 +239,8 @@ class CArchiveExtractCallback: |
| bool _saclEnabled; |
| #endif |
| |
| + CObjectVector<NWindows::NFile::NDir::CDelayedSymLink> _delayedSymLinks; |
| + |
| void CreateComplexDirectory(const UStringVector &dirPathParts, FString &fullPath); |
| HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined); |
| HRESULT GetUnpackSize(); |
| @@ -330,7 +334,7 @@ public: |
| } |
| #endif |
| |
| - HRESULT SetDirsTimes(); |
| + HRESULT SetFinalAttribs(); |
| }; |
| |
| bool CensorNode_CheckPath(const NWildcard::CCensorNode &node, const CReadArcItem &item); |
| diff --git a/CPP/7zip/UI/Common/Extract.cpp b/CPP/7zip/UI/Common/Extract.cpp |
| index 5b96071..98192fa 100644 |
| --- a/CPP/7zip/UI/Common/Extract.cpp |
| +++ b/CPP/7zip/UI/Common/Extract.cpp |
| @@ -207,7 +207,7 @@ static HRESULT DecompressArchive( |
| else |
| result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs); |
| if (result == S_OK && !options.StdInMode) |
| - result = ecs->SetDirsTimes(); |
| + result = ecs->SetFinalAttribs(); |
| return callback->ExtractResult(result); |
| } |
| |
| diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp |
| index 422edfc..baa6255 100644 |
| --- a/CPP/Windows/FileDir.cpp |
| +++ b/CPP/Windows/FileDir.cpp |
| @@ -347,7 +347,8 @@ static int convert_to_symlink(const char * name) { |
| return -1; |
| } |
| |
| -bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes) |
| +bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes, |
| + CObjectVector<CDelayedSymLink> *delayedSymLinks) |
| { |
| if (!fileName) { |
| SetLastError(ERROR_PATH_NOT_FOUND); |
| @@ -379,7 +380,9 @@ bool SetFileAttrib(CFSTR fileName, DWORD fileAttributes) |
| stat_info.st_mode = fileAttributes >> 16; |
| #ifdef ENV_HAVE_LSTAT |
| if (S_ISLNK(stat_info.st_mode)) { |
| - if ( convert_to_symlink(name) != 0) { |
| + if (delayedSymLinks) |
| + delayedSymLinks->Add(CDelayedSymLink(name)); |
| + else if ( convert_to_symlink(name) != 0) { |
| TRACEN((printf("SetFileAttrib(%s,%d) : false-3\n",(const char *)name,fileAttributes))) |
| return false; |
| } |
| @@ -814,6 +817,44 @@ bool CTempDir::Remove() |
| return !_mustBeDeleted; |
| } |
| |
| +#ifdef ENV_UNIX |
| + |
| +CDelayedSymLink::CDelayedSymLink(const char * source) |
| + : _source(source) |
| +{ |
| + struct stat st; |
| + |
| + if (lstat(_source, &st) == 0) { |
| + _dev = st.st_dev; |
| + _ino = st.st_ino; |
| + } else { |
| + _dev = 0; |
| + } |
| +} |
| + |
| +bool CDelayedSymLink::Create() |
| +{ |
| + struct stat st; |
| + |
| + if (_dev == 0) { |
| + errno = EPERM; |
| + return false; |
| + } |
| + if (lstat(_source, &st) != 0) |
| + return false; |
| + if (_dev != st.st_dev || _ino != st.st_ino) { |
| + // Placeholder file has been overwritten or moved by another |
| + // symbolic link creation |
| + errno = EPERM; |
| + return false; |
| + } |
| + |
| + return convert_to_symlink(_source) == 0; |
| +} |
| + |
| +#endif // ENV_UNIX |
| + |
| + |
| }}} |
| |
| #ifndef _SFX |
| diff --git a/CPP/Windows/FileDir.h b/CPP/Windows/FileDir.h |
| index b13d1cc..7429a81 100644 |
| --- a/CPP/Windows/FileDir.h |
| +++ b/CPP/Windows/FileDir.h |
| @@ -4,6 +4,7 @@ |
| #define __WINDOWS_FILE_DIR_H |
| |
| #include "../Common/MyString.h" |
| +#include "../Common/MyVector.h" |
| |
| #include "FileIO.h" |
| |
| @@ -11,11 +12,14 @@ namespace NWindows { |
| namespace NFile { |
| namespace NDir { |
| |
| +class CDelayedSymLink; |
| + |
| bool GetWindowsDir(FString &path); |
| bool GetSystemDir(FString &path); |
| |
| bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime); |
| -bool SetFileAttrib(CFSTR path, DWORD attrib); |
| +bool SetFileAttrib(CFSTR path, DWORD attrib, |
| + CObjectVector<CDelayedSymLink> *delayedSymLinks = 0); |
| bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); |
| |
| #ifndef UNDER_CE |
| @@ -76,6 +80,31 @@ public: |
| bool Remove(); |
| }; |
| |
| +// Symbolic links must be created last so that they can't be used to |
| +// create or overwrite files above the extraction directory. |
| +class CDelayedSymLink |
| +{ |
| +#ifdef ENV_UNIX |
| + // Where the symlink should be created. The target is specified in |
| + // the placeholder file. |
| + AString _source; |
| + |
| + // Device and inode of the placeholder file. Before creating the |
| + // symlink, we must check that these haven't been changed by creation |
| + // of another symlink. |
| + dev_t _dev; |
| + ino_t _ino; |
| + |
| +public: |
| + explicit CDelayedSymLink(const char * source); |
| + bool Create(); |
| +#else // !ENV_UNIX |
| +public: |
| + CDelayedSymLink(const char * source) {} |
| + bool Create() { return true; } |
| +#endif // ENV_UNIX |
| +}; |
| + |
| #if !defined(UNDER_CE) |
| class CCurrentDirRestorer |
| { |