diff --git a/CPP/7zip/Common/FileStreams.cpp b/CPP/7zip/Common/FileStreams.cpp index b7e4fbe28..3d70f6c48 100644 --- a/CPP/7zip/Common/FileStreams.cpp +++ b/CPP/7zip/Common/FileStreams.cpp @@ -80,6 +80,7 @@ CInFileStream::CInFileStream(): #endif _info_WasLoaded(false), SupportHardLinks(false), + StoreOldestCTime(false), Callback(NULL), CallbackRef(0) { @@ -436,7 +437,9 @@ Z7_COM7F_IMF(CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aT */ { if (size) *size = (((UInt64)info.nFileSizeHigh) << 32) + info.nFileSizeLow; - if (cTime) *cTime = info.ftCreationTime; + if (cTime) *cTime = StoreOldestCTime ? + FiTime_Min3(info.ftCreationTime, info.ftLastAccessTime, info.ftLastWriteTime) : + info.ftCreationTime; if (aTime) *aTime = info.ftLastAccessTime; if (mTime) *mTime = info.ftLastWriteTime; if (attrib) *attrib = info.dwFileAttributes; @@ -603,7 +606,11 @@ Z7_COM7F_IMF(CInFileStream::GetProps(UInt64 *size, FILETIME *cTime, FILETIME *aT */ if (size) *size = (UInt64)st.st_size; - if (cTime) FiTime_To_FILETIME (ST_CTIME(st), *cTime); + if (cTime) + { + const CFiTime ct = StoreOldestCTime ? FiTime_Min3(ST_CTIME(st), ST_ATIME(st), ST_MTIME(st)) : ST_CTIME(st); + FiTime_To_FILETIME(ct, *cTime); + } if (aTime) FiTime_To_FILETIME (ST_ATIME(st), *aTime); if (mTime) FiTime_To_FILETIME (ST_MTIME(st), *mTime); if (attrib) *attrib = NWindows::NFile::NFind::Get_WinAttribPosix_From_PosixMode(st.st_mode); diff --git a/CPP/7zip/Common/FileStreams.h b/CPP/7zip/Common/FileStreams.h index 7f465cf62..aba799e99 100644 --- a/CPP/7zip/Common/FileStreams.h +++ b/CPP/7zip/Common/FileStreams.h @@ -94,6 +94,7 @@ Z7_class_final(CInFileStream) : bool _info_WasLoaded; bool SupportHardLinks; + bool StoreOldestCTime; IInFileStream_Callback *Callback; UINT_PTR CallbackRef; diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp index 73974e6a9..9fdf82b49 100644 --- a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -205,7 +205,8 @@ enum Enum kNameTrailReplace, kDeleteAfterCompressing, - kSetArcMTime + kSetArcMTime, + kOldestCTime #ifndef Z7_NO_CRYPTO , kPassword @@ -356,7 +357,8 @@ static const CSwitchForm kSwitchForms[] = { "snt", SWFRM_MINUS }, { "sdel", SWFRM_SIMPLE }, - { "stl", SWFRM_SIMPLE } + { "stl", SWFRM_SIMPLE }, + { "stc", SWFRM_SIMPLE } #ifndef Z7_NO_CRYPTO , { "p", SWFRM_STRING } @@ -1538,6 +1540,8 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) nt.PreserveATime = true; if (parser[NKey::kShareForWrite].ThereIs) nt.OpenShareForWrite = true; + if (parser[NKey::kOldestCTime].ThereIs) + nt.OldestCTime = true; } if (parser[NKey::kZoneFile].ThereIs) @@ -1699,6 +1703,15 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs; updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs; + if (parser[NKey::kOldestCTime].ThereIs) + { + updateOptions.StoreOldestCTime = true; + // force storing CTime into archive (same as "-mtc"): + CProperty prop; + prop.Name = L"tc"; + updateOptions.MethodMode.Properties.Add(prop); + } + if (updateOptions.StdOutMode && updateOptions.EMailMode) throw CArcCmdLineException("stdout mode and email mode cannot be combined"); diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp index 6631629a0..fd248d155 100644 --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp @@ -1061,7 +1061,7 @@ void CArchiveExtractCallback::CorrectPathParts() } -static void GetFiTimesCAM(const CProcessedFileInfo &fi, CFiTimesCAM &pt, const CArc &arc) +static void GetFiTimesCAM(const CProcessedFileInfo &fi, CFiTimesCAM &pt, const CArc &arc, bool oldestCTime) { pt.CTime_Defined = false; pt.ATime_Defined = false; @@ -1092,6 +1092,23 @@ static void GetFiTimesCAM(const CProcessedFileInfo &fi, CFiTimesCAM &pt, const C fi.ATime.Write_To_FiTime(pt.ATime); pt.ATime_Defined = true; } + + if (oldestCTime) + { + // the oldest of the defined times becomes the creation time + const CFiTime *oldest = NULL; + if (pt.CTime_Defined) + oldest = &pt.CTime; + if (pt.ATime_Defined && (!oldest || Compare_FiTime(&pt.ATime, oldest) < 0)) + oldest = &pt.ATime; + if (pt.MTime_Defined && (!oldest || Compare_FiTime(&pt.MTime, oldest) < 0)) + oldest = &pt.MTime; + if (oldest) + { + pt.CTime = *oldest; + pt.CTime_Defined = true; + } + } } @@ -1163,7 +1180,7 @@ void CArchiveExtractCallback::CreateFolders() return; CDirPathTime pt; - GetFiTimesCAM(_fi, pt, *_arc); + GetFiTimesCAM(_fi, pt, *_arc, _ntOptions.OldestCTime); if (pt.IsSomeTimeDefined()) { @@ -1972,7 +1989,7 @@ HRESULT CArchiveExtractCallback::CloseFile() #endif CFiTimesCAM t; - GetFiTimesCAM(_fi, t, *_arc); + GetFiTimesCAM(_fi, t, *_arc, _ntOptions.OldestCTime); // #ifdef _WIN32 if (t.IsSomeTimeDefined()) @@ -2987,7 +3004,7 @@ HRESULT CArchiveExtractCallback::SetPostLinks() const #endif CFiTimesCAM pt; - GetFiTimesCAM(link.item_FileInfo, pt, *_arc); + GetFiTimesCAM(link.item_FileInfo, pt, *_arc, _ntOptions.OldestCTime); if (pt.IsSomeTimeDefined()) pt.SetLinkFileTime_to_FS(link.fullProcessedPath_from); diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/CPP/7zip/UI/Common/ArchiveExtractCallback.h index 3c6276349..7559d5327 100644 --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.h +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.h @@ -64,6 +64,7 @@ struct CExtractNtOptions // used for hash arcs only, when we open external files bool PreserveATime; bool OpenShareForWrite; + bool OldestCTime; unsigned SymLinks_DangerousLevel; @@ -75,6 +76,7 @@ struct CExtractNtOptions ExtractOwner(false), PreserveATime(false), OpenShareForWrite(false), + OldestCTime(false), SymLinks_DangerousLevel(5), MemLimit((UInt64)(Int64)-1) { diff --git a/CPP/7zip/UI/Common/Update.cpp b/CPP/7zip/UI/Common/Update.cpp index 1c2754e9a..d53a4f559 100644 --- a/CPP/7zip/UI/Common/Update.cpp +++ b/CPP/7zip/UI/Common/Update.cpp @@ -621,6 +621,7 @@ static HRESULT Compress( CMyComPtr updateCallback(updateCallbackSpec); updateCallbackSpec->PreserveATime = options.PreserveATime; + updateCallbackSpec->StoreOldestCTime = options.StoreOldestCTime; updateCallbackSpec->ShareForWrite = options.OpenShareForWrite; updateCallbackSpec->StopAfterOpenError = options.StopAfterOpenError; updateCallbackSpec->StdInMode = options.StdInMode; diff --git a/CPP/7zip/UI/Common/Update.h b/CPP/7zip/UI/Common/Update.h index ae141e5e0..18b5e24aa 100644 --- a/CPP/7zip/UI/Common/Update.h +++ b/CPP/7zip/UI/Common/Update.h @@ -94,6 +94,7 @@ struct CUpdateOptions bool DeleteAfterCompressing; bool SetArcMTime; + bool StoreOldestCTime; bool RenameMode; CBoolPair NtSecurity; @@ -140,6 +141,7 @@ struct CUpdateOptions DeleteAfterCompressing(false), SetArcMTime(false), + StoreOldestCTime(false), RenameMode(false), ArcNameMode(k_ArcNameMode_Smart), diff --git a/CPP/7zip/UI/Common/UpdateCallback.cpp b/CPP/7zip/UI/Common/UpdateCallback.cpp index e2f186647..05d7f88b9 100644 --- a/CPP/7zip/UI/Common/UpdateCallback.cpp +++ b/CPP/7zip/UI/Common/UpdateCallback.cpp @@ -58,6 +58,7 @@ bool InitLocalPrivileges(); CArchiveUpdateCallback::CArchiveUpdateCallback(): PreserveATime(false), + StoreOldestCTime(false), ShareForWrite(false), StopAfterOpenError(false), StdInMode(false), @@ -167,7 +168,7 @@ Z7_COM7F_IMF(CArchiveUpdateCallback::GetRootProp(PROPID propID, PROPVARIANT *val { case kpidIsDir: prop = true; break; case kpidAttrib: if (ParentDirItem) prop = ParentDirItem->GetWinAttrib(); break; - case kpidCTime: if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, ParentDirItem->CTime); break; + case kpidCTime: if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, StoreOldestCTime ? FiTime_Min3(ParentDirItem->CTime, ParentDirItem->ATime, ParentDirItem->MTime) : ParentDirItem->CTime); break; case kpidATime: if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, ParentDirItem->ATime); break; case kpidMTime: if (ParentDirItem) PropVariant_SetFrom_FiTime(prop, ParentDirItem->MTime); break; case kpidArcFileName: if (!ArcFileName.IsEmpty()) prop = ArcFileName; break; @@ -514,7 +515,7 @@ Z7_COM7F_IMF(CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PR case kpidPath: prop = DirItems->GetLogPath((unsigned)up.DirIndex); break; case kpidIsDir: prop = di.IsDir(); break; case kpidSize: prop = (UInt64)(di.IsDir() ? (UInt64)0 : di.Size); break; - case kpidCTime: PropVariant_SetFrom_FiTime(prop, di.CTime); break; + case kpidCTime: PropVariant_SetFrom_FiTime(prop, StoreOldestCTime ? FiTime_Min3(di.CTime, di.ATime, di.MTime) : di.CTime); break; case kpidATime: PropVariant_SetFrom_FiTime(prop, di.ATime); break; case kpidMTime: PropVariant_SetFrom_FiTime(prop, di.MTime); break; case kpidAttrib: /* if (di.Attrib_IsDefined) */ prop = (UInt32)di.GetWinAttrib(); break; @@ -697,6 +698,7 @@ Z7_COM7F_IMF(CArchiveUpdateCallback::GetStream2(UInt32 index, ISequentialInStrea #endif inStreamSpec->SupportHardLinks = StoreHardLinks; + inStreamSpec->StoreOldestCTime = StoreOldestCTime; const bool preserveATime = (PreserveATime || mode == NUpdateNotifyOp::kAnalyze); // 22.00 : we don't change access time in Analyze pass. inStreamSpec->Set_PreserveATime(preserveATime); diff --git a/CPP/7zip/UI/Common/UpdateCallback.h b/CPP/7zip/UI/Common/UpdateCallback.h index 379b81462..ed07ba798 100644 --- a/CPP/7zip/UI/Common/UpdateCallback.h +++ b/CPP/7zip/UI/Common/UpdateCallback.h @@ -118,6 +118,7 @@ class CArchiveUpdateCallback Z7_final: public: bool PreserveATime; + bool StoreOldestCTime; bool ShareForWrite; bool StopAfterOpenError; bool StdInMode; diff --git a/CPP/7zip/UI/Common/ZipRegistry.cpp b/CPP/7zip/UI/Common/ZipRegistry.cpp index 936b8881b..9099244ad 100644 --- a/CPP/7zip/UI/Common/ZipRegistry.cpp +++ b/CPP/7zip/UI/Common/ZipRegistry.cpp @@ -224,6 +224,7 @@ static LPCTSTR const kAltStreams = TEXT("AltStreams"); static LPCTSTR const kHardLinks = TEXT("HardLinks"); static LPCTSTR const kSymLinks = TEXT("SymLinks"); static LPCTSTR const kPreserveATime = TEXT("PreserveATime"); +static LPCTSTR const kOldestCTime = TEXT("OldestCTime"); static LPCTSTR const kTimePrec = TEXT("TimePrec"); static LPCTSTR const kMTime = TEXT("MTime"); @@ -264,6 +265,7 @@ void CInfo::Save() const Key_Set_BoolPair_Delete_IfNotDef (key, kHardLinks, HardLinks); Key_Set_BoolPair_Delete_IfNotDef (key, kSymLinks, SymLinks); Key_Set_BoolPair_Delete_IfNotDef (key, kPreserveATime, PreserveATime); + Key_Set_BoolPair_Delete_IfNotDef (key, kOldestCTime, OldestCTime); key.SetValue(kShowPassword, ShowPassword); key.SetValue(kLevel, (UInt32)Level); @@ -325,6 +327,7 @@ void CInfo::Load() Key_Get_BoolPair(key, kHardLinks, HardLinks); Key_Get_BoolPair(key, kSymLinks, SymLinks); Key_Get_BoolPair(key, kPreserveATime, PreserveATime); + Key_Get_BoolPair(key, kOldestCTime, OldestCTime); key.GetValue_Strings(kArcHistory, ArcPaths); diff --git a/CPP/7zip/UI/Common/ZipRegistry.h b/CPP/7zip/UI/Common/ZipRegistry.h index ce084f4ea..70b63196f 100644 --- a/CPP/7zip/UI/Common/ZipRegistry.h +++ b/CPP/7zip/UI/Common/ZipRegistry.h @@ -145,6 +145,7 @@ namespace NCompression CBoolPair SymLinks; CBoolPair PreserveATime; + CBoolPair OldestCTime; UString ArcType; UStringVector ArcPaths; diff --git a/CPP/7zip/UI/Console/Main.cpp b/CPP/7zip/UI/Console/Main.cpp index 90e00a495..5a93b873d 100644 --- a/CPP/7zip/UI/Console/Main.cpp +++ b/CPP/7zip/UI/Console/Main.cpp @@ -194,6 +194,7 @@ static const char * const kHelpString = " -sse : stop archive creating, if it can't open some input file\n" " -ssp : do not change Last Access Time of source files while archiving\n" " -ssw : compress shared files\n" + " -stc : set creation time to oldest of access/modification/creation time\n" " -stl : set archive timestamp from the most recently modified file\n" " -stm{HexMask} : set CPU thread affinity mask (hexadecimal number)\n" " -stx{Type} : exclude archive type\n" diff --git a/CPP/7zip/UI/GUI/CompressDialog.cpp b/CPP/7zip/UI/GUI/CompressDialog.cpp index 53e56fe27..b9f629a30 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.cpp +++ b/CPP/7zip/UI/GUI/CompressDialog.cpp @@ -728,9 +728,11 @@ void CCompressDialog::FormatChanged(bool isChanged) SET_GUI_BOOL (AltStreams); SET_GUI_BOOL (NtSecurity); SET_GUI_BOOL (PreserveATime); + SET_GUI_BOOL (OldestCTime); } PreserveATime.Supported = true; + OldestCTime.Supported = true; { const CArcInfoEx &ai = Get_ArcInfoEx(); @@ -1194,6 +1196,7 @@ void CCompressDialog::OnOK() SET_FINAL_BOOL_PAIRS (NtSecurity); SET_FINAL_BOOL_PAIRS (PreserveATime); + SET_FINAL_BOOL_PAIRS (OldestCTime); } { @@ -3690,7 +3693,8 @@ static const UInt32 kLangIDs_Options[] = IDX_COMPRESS_CTIME, IDX_COMPRESS_ATIME, IDX_COMPRESS_ZTIME, - IDX_COMPRESS_PRESERVE_ATIME + IDX_COMPRESS_PRESERVE_ATIME, + IDX_COMPRESS_OLDEST_CTIME }; #endif @@ -3737,6 +3741,7 @@ bool COptionsDialog::OnInit() CheckButton_Bool1 ( IDX_COMPRESS_NT_SECUR, cd->NtSecurity); CheckButton_Bool1 (IDX_COMPRESS_PRESERVE_ATIME, cd->PreserveATime); + CheckButton_Bool1 (IDX_COMPRESS_OLDEST_CTIME, cd->OldestCTime); m_Prec.Attach (GetItem(IDC_COMPRESS_TIME_PREC)); @@ -3801,6 +3806,7 @@ void COptionsDialog::OnOK() GetButton_Bool1 (IDX_COMPRESS_NT_ALT_STREAMS, cd->AltStreams); GetButton_Bool1 (IDX_COMPRESS_NT_SECUR, cd->NtSecurity); GetButton_Bool1 (IDX_COMPRESS_PRESERVE_ATIME, cd->PreserveATime); + GetButton_Bool1 (IDX_COMPRESS_OLDEST_CTIME, cd->OldestCTime); Store_TimeBoxes(); { diff --git a/CPP/7zip/UI/GUI/CompressDialog.h b/CPP/7zip/UI/GUI/CompressDialog.h index e0f3aa536..debb4b287 100644 --- a/CPP/7zip/UI/GUI/CompressDialog.h +++ b/CPP/7zip/UI/GUI/CompressDialog.h @@ -62,6 +62,7 @@ namespace NCompressDialog CBoolPair NtSecurity; CBoolPair PreserveATime; + CBoolPair OldestCTime; UInt32 TimePrec; CBoolPair MTime; @@ -147,6 +148,7 @@ class CCompressDialog: public NWindows::NControl::CModalDialog CBool1 AltStreams; CBool1 NtSecurity; CBool1 PreserveATime; + CBool1 OldestCTime; private: bool _ramSize_Defined; @@ -468,7 +470,7 @@ class COptionsDialog: public NWindows::NControl::CModalDialog INT_PTR Create(HWND wndParent = NULL) { - BIG_DIALOG_SIZE(240, 232); + BIG_DIALOG_SIZE(240, 252); return CModalDialog::Create(SIZED_DIALOG(IDD_COMPRESS_OPTIONS), wndParent); } diff --git a/CPP/7zip/UI/GUI/CompressDialogRes.h b/CPP/7zip/UI/GUI/CompressDialogRes.h index d04d4b9c1..705a9e535 100644 --- a/CPP/7zip/UI/GUI/CompressDialogRes.h +++ b/CPP/7zip/UI/GUI/CompressDialogRes.h @@ -104,6 +104,7 @@ #define IDX_COMPRESS_ATIME 4084 #define IDX_COMPRESS_ZTIME 4085 #define IDX_COMPRESS_PRESERVE_ATIME 4086 +#define IDX_COMPRESS_OLDEST_CTIME 4087 #define IDS_COMPRESS_SEC 4090 #define IDS_COMPRESS_NS 4091 diff --git a/CPP/7zip/UI/GUI/CompressOptionsDialog.rc b/CPP/7zip/UI/GUI/CompressOptionsDialog.rc index 682775e9c..059a89538 100644 --- a/CPP/7zip/UI/GUI/CompressOptionsDialog.rc +++ b/CPP/7zip/UI/GUI/CompressOptionsDialog.rc @@ -2,7 +2,7 @@ #include "../../GuiCommon.rc" #define xc 240 -#define yc 232 +#define yc 252 #define g5x m #define g5x2 (g5x + m) @@ -35,7 +35,7 @@ BEGIN LTEXT "", IDT_COMPRESS_TIME_INFO, g5x, timePosY - 14, g5xs, 8 - GROUPBOX "Time", IDG_COMPRESS_TIME, g5x, timePosY, g5xs, 112 + GROUPBOX "Time", IDG_COMPRESS_TIME, g5x, timePosY, g5xs, 132 // MY_CONTROL_CHECKBOX ("Default", IDX_COMPRESS_TIME_DEFAULT, // ntPosX, timePosY + 10, ntSizeX) @@ -71,6 +71,11 @@ BEGIN "Do not change source files last access time", IDX_COMPRESS_PRESERVE_ATIME, ntPosX, timePosY + 92, ntSizeX) + + MY_CONTROL_CHECKBOX_2LINES( + "Set creation time to oldest of access/modification/creation time", + IDX_COMPRESS_OLDEST_CTIME, + ntPosX, timePosY + 112, ntSizeX) DEFPUSHBUTTON "OK", IDOK, bx3, by, bxs, bys, WS_GROUP diff --git a/CPP/7zip/UI/GUI/UpdateGUI.cpp b/CPP/7zip/UI/GUI/UpdateGUI.cpp index a600a8bc1..122e1842f 100644 --- a/CPP/7zip/UI/GUI/UpdateGUI.cpp +++ b/CPP/7zip/UI/GUI/UpdateGUI.cpp @@ -275,6 +275,8 @@ static void SetOutProperties( AddProp_BoolPair(properties, "tm", di.MTime); AddProp_BoolPair(properties, "tc", di.CTime); AddProp_BoolPair(properties, "ta", di.ATime); + if (di.OldestCTime.Def && di.OldestCTime.Val) + AddProp_bool(properties, "tc", true); // force storing CTime for "oldest as creation time" if (di.TimePrec != (UInt32)(Int32)-1) AddProp_UInt32(properties, "tp", di.TimePrec); @@ -437,6 +439,8 @@ static HRESULT ShowDialog( di.SetArcMTime.SetTrueTrue(); if (options.PreserveATime) di.PreserveATime.SetTrueTrue(); + if (options.StoreOldestCTime) + di.OldestCTime.SetTrueTrue(); if (callback->PasswordIsDefined) di.Password = callback->Password; @@ -466,6 +470,8 @@ static HRESULT ShowDialog( options.SetArcMTime = di.SetArcMTime.Val; if (di.PreserveATime.Def) options.PreserveATime = di.PreserveATime.Val; + if (di.OldestCTime.Def) + options.StoreOldestCTime = di.OldestCTime.Val; /* #if defined(_WIN32) && !defined(UNDER_CE) diff --git a/CPP/Windows/TimeUtils.h b/CPP/Windows/TimeUtils.h index 8e1e47897..6ee0f847a 100644 --- a/CPP/Windows/TimeUtils.h +++ b/CPP/Windows/TimeUtils.h @@ -81,6 +81,17 @@ inline bool FILETIME_IsZero(const FILETIME &ft) #endif +/* FiTime_Min3: returns the oldest (earliest) of three file times */ +inline CFiTime FiTime_Min3(const CFiTime &a, const CFiTime &b, const CFiTime &c) +{ + const CFiTime *m = &a; + if (Compare_FiTime(&b, m) < 0) + m = &b; + if (Compare_FiTime(&c, m) < 0) + m = &c; + return *m; +} + // void FiTime_Normalize_With_Prec(CFiTime &ft, unsigned prec); namespace NWindows {