blob: dc813174fb397325796fb49d0227131966058a83 [file] [log] [blame]
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
{