aboutsummaryrefslogtreecommitdiff
path: root/CPP/Windows
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/Windows')
-rw-r--r--CPP/Windows/FileDir.cpp211
-rw-r--r--CPP/Windows/FileDir.h41
-rw-r--r--CPP/Windows/FileFind.cpp9
-rw-r--r--CPP/Windows/FileIO.h43
-rw-r--r--CPP/Windows/FileLink.cpp244
-rw-r--r--CPP/Windows/FileName.cpp79
-rw-r--r--CPP/Windows/FileName.h13
-rw-r--r--CPP/Windows/Registry.cpp295
-rw-r--r--CPP/Windows/Registry.h48
-rw-r--r--CPP/Windows/System.cpp169
-rw-r--r--CPP/Windows/System.h63
-rw-r--r--CPP/Windows/SystemInfo.cpp125
-rw-r--r--CPP/Windows/SystemInfo.h2
-rw-r--r--CPP/Windows/Thread.h8
-rw-r--r--CPP/Windows/TimeUtils.cpp3
15 files changed, 983 insertions, 370 deletions
diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp
index dfeed82..ad0d8c9 100644
--- a/CPP/Windows/FileDir.cpp
+++ b/CPP/Windows/FileDir.cpp
@@ -15,8 +15,9 @@
15#include <sys/stat.h> 15#include <sys/stat.h>
16#include <sys/types.h> 16#include <sys/types.h>
17 17
18#include "../Common/StringConvert.h"
19#include "../Common/C_FileIO.h" 18#include "../Common/C_FileIO.h"
19#include "../Common/MyBuffer2.h"
20#include "../Common/StringConvert.h"
20#endif 21#endif
21 22
22#include "FileDir.h" 23#include "FileDir.h"
@@ -123,7 +124,7 @@ bool GetSystemDir(FString &path)
123#endif // UNDER_CE 124#endif // UNDER_CE
124 125
125 126
126bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) 127static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD dwFlagsAndAttributes)
127{ 128{
128 #ifndef _UNICODE 129 #ifndef _UNICODE
129 if (!g_IsNT) 130 if (!g_IsNT)
@@ -136,14 +137,14 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
136 HANDLE hDir = INVALID_HANDLE_VALUE; 137 HANDLE hDir = INVALID_HANDLE_VALUE;
137 IF_USE_MAIN_PATH 138 IF_USE_MAIN_PATH
138 hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 139 hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
139 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 140 NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
140 #ifdef Z7_LONG_PATH 141 #ifdef Z7_LONG_PATH
141 if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH) 142 if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
142 { 143 {
143 UString superPath; 144 UString superPath;
144 if (GetSuperPath(path, superPath, USE_MAIN_PATH)) 145 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
145 hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 146 hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
146 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 147 NULL, OPEN_EXISTING, dwFlagsAndAttributes, NULL);
147 } 148 }
148 #endif 149 #endif
149 150
@@ -156,6 +157,15 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
156 return res; 157 return res;
157} 158}
158 159
160bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
161{
162 return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS);
163}
164
165bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
166{
167 return SetFileTime_Base(path, cTime, aTime, mTime, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT);
168}
159 169
160 170
161bool SetFileAttrib(CFSTR path, DWORD attrib) 171bool SetFileAttrib(CFSTR path, DWORD attrib)
@@ -222,6 +232,8 @@ bool RemoveDir(CFSTR path)
222} 232}
223 233
224 234
235// When moving a directory, oldFile and newFile must be on the same drive.
236
225bool MyMoveFile(CFSTR oldFile, CFSTR newFile) 237bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
226{ 238{
227 #ifndef _UNICODE 239 #ifndef _UNICODE
@@ -250,6 +262,59 @@ bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
250 return false; 262 return false;
251} 263}
252 264
265#if defined(Z7_WIN32_WINNT_MIN) && Z7_WIN32_WINNT_MIN >= 0x0500
266static DWORD WINAPI CopyProgressRoutine_to_ICopyFileProgress(
267 LARGE_INTEGER TotalFileSize, // file size
268 LARGE_INTEGER TotalBytesTransferred, // bytes transferred
269 LARGE_INTEGER /* StreamSize */, // bytes in stream
270 LARGE_INTEGER /* StreamBytesTransferred */, // bytes transferred for stream
271 DWORD /* dwStreamNumber */, // current stream
272 DWORD /* dwCallbackReason */, // callback reason
273 HANDLE /* hSourceFile */, // handle to source file
274 HANDLE /* hDestinationFile */, // handle to destination file
275 LPVOID lpData // from CopyFileEx
276)
277{
278 return ((ICopyFileProgress *)lpData)->CopyFileProgress(
279 (UInt64)TotalFileSize.QuadPart,
280 (UInt64)TotalBytesTransferred.QuadPart);
281}
282#endif
283
284bool MyMoveFile_with_Progress(CFSTR oldFile, CFSTR newFile,
285 ICopyFileProgress *progress)
286{
287#if defined(Z7_WIN32_WINNT_MIN) && Z7_WIN32_WINNT_MIN >= 0x0500
288#ifndef _UNICODE
289 if (g_IsNT)
290#endif
291 if (progress)
292 {
293 IF_USE_MAIN_PATH_2(oldFile, newFile)
294 {
295 if (::MoveFileWithProgressW(fs2us(oldFile), fs2us(newFile),
296 CopyProgressRoutine_to_ICopyFileProgress, progress, MOVEFILE_COPY_ALLOWED))
297 return true;
298 if (::GetLastError() == ERROR_REQUEST_ABORTED)
299 return false;
300 }
301 #ifdef Z7_LONG_PATH
302 if (USE_SUPER_PATH_2)
303 {
304 UString d1, d2;
305 if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
306 return BOOLToBool(::MoveFileWithProgressW(d1, d2,
307 CopyProgressRoutine_to_ICopyFileProgress, progress, MOVEFILE_COPY_ALLOWED));
308 }
309 #endif
310 return false;
311 }
312#else
313 UNUSED_VAR(progress)
314#endif
315 return MyMoveFile(oldFile, newFile);
316}
317
253#ifndef UNDER_CE 318#ifndef UNDER_CE
254#if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0500 // Win2000 319#if !defined(Z7_WIN32_WINNT_MIN) || Z7_WIN32_WINNT_MIN < 0x0500 // Win2000
255#define Z7_USE_DYN_CreateHardLink 320#define Z7_USE_DYN_CreateHardLink
@@ -595,6 +660,35 @@ bool RemoveDirWithSubItems(const FString &path)
595 return RemoveDir(path); 660 return RemoveDir(path);
596} 661}
597 662
663bool RemoveDirAlways_if_Empty(const FString &path)
664{
665 const DWORD attrib = NFind::GetFileAttrib(path);
666 if (attrib != INVALID_FILE_ATTRIBUTES
667 && (attrib & FILE_ATTRIBUTE_READONLY))
668 {
669 bool need_ClearAttrib = true;
670 if ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
671 {
672 FString s (path);
673 s.Add_PathSepar();
674 NFind::CEnumerator enumerator;
675 enumerator.SetDirPrefix(s);
676 NFind::CDirEntry fi;
677 if (enumerator.Next(fi))
678 {
679 // we don't want to change attributes, if there are files
680 // in directory, because RemoveDir(path) will fail.
681 need_ClearAttrib = false;
682 // SetLastError(ERROR_DIR_NOT_EMPTY);
683 // return false;
684 }
685 }
686 if (need_ClearAttrib)
687 SetFileAttrib(path, 0); // we clear read-only attrib to remove read-only dir
688 }
689 return RemoveDir(path);
690}
691
598#endif // _WIN32 692#endif // _WIN32
599 693
600#ifdef UNDER_CE 694#ifdef UNDER_CE
@@ -878,9 +972,9 @@ bool CTempFile::Remove()
878 return !_mustBeDeleted; 972 return !_mustBeDeleted;
879} 973}
880 974
881bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore) 975bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore,
976 ICopyFileProgress *progress)
882{ 977{
883 // DWORD attrib = 0;
884 if (deleteDestBefore) 978 if (deleteDestBefore)
885 { 979 {
886 if (NFind::DoesFileExist_Raw(name)) 980 if (NFind::DoesFileExist_Raw(name))
@@ -891,8 +985,8 @@ bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
891 } 985 }
892 } 986 }
893 DisableDeleting(); 987 DisableDeleting();
894 return MyMoveFile(_path, name); 988 // if (!progress) return MyMoveFile(_path, name);
895 989 return MyMoveFile_with_Progress(_path, name, progress);
896 /* 990 /*
897 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY)) 991 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
898 { 992 {
@@ -941,34 +1035,59 @@ bool RemoveDir(CFSTR path)
941} 1035}
942 1036
943 1037
944static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile) 1038static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile, ICopyFileProgress *progress)
945{ 1039{
946 NWindows::NFile::NIO::COutFile outFile;
947 if (!outFile.Create_NEW(newFile))
948 return FALSE;
949
950 NWindows::NFile::NIO::CInFile inFile;
951 if (!inFile.Open(oldFile))
952 return FALSE;
953
954 char buf[1 << 14];
955
956 for (;;)
957 { 1040 {
958 const ssize_t num = inFile.read_part(buf, sizeof(buf)); 1041 NIO::COutFile outFile;
959 if (num == 0) 1042 if (!outFile.Create_NEW(newFile))
960 return TRUE;
961 if (num < 0)
962 return FALSE; 1043 return FALSE;
963 size_t processed; 1044 NIO::CInFile inFile;
964 const ssize_t num2 = outFile.write_full(buf, (size_t)num, processed); 1045 if (!inFile.Open(oldFile))
965 if (num2 != num || processed != (size_t)num)
966 return FALSE; 1046 return FALSE;
1047
1048 const size_t k_BufSize = 1 << 16;
1049 CAlignedBuffer1 buf(k_BufSize);
1050
1051 UInt64 length = 0;
1052 if (progress && !inFile.GetLength(length))
1053 length = 0;
1054 UInt64 prev = 0;
1055 UInt64 cur = 0;
1056 for (;;)
1057 {
1058 const ssize_t num = inFile.read_part(buf, k_BufSize);
1059 if (num == 0)
1060 return TRUE;
1061 if (num < 0)
1062 break;
1063 size_t processed;
1064 const ssize_t num2 = outFile.write_full(buf, (size_t)num, processed);
1065 if (num2 != num || processed != (size_t)num)
1066 break;
1067 cur += (size_t)num2;
1068 if (progress && cur - prev >= (1u << 20))
1069 {
1070 prev = cur;
1071 if (progress->CopyFileProgress(length, cur) != PROGRESS_CONTINUE)
1072 {
1073 errno = EINTR; // instead of WIN32::ERROR_REQUEST_ABORTED
1074 break;
1075 }
1076 }
1077 }
967 } 1078 }
1079 // There is file IO error or process was interrupted by user.
1080 // We close output file and delete it.
1081 // DeleteFileAlways doesn't change errno (if successed), but we restore errno.
1082 const int errno_save = errno;
1083 DeleteFileAlways(newFile);
1084 errno = errno_save;
1085 return FALSE;
968} 1086}
969 1087
970 1088
971bool MyMoveFile(CFSTR oldFile, CFSTR newFile) 1089bool MyMoveFile_with_Progress(CFSTR oldFile, CFSTR newFile,
1090 ICopyFileProgress *progress)
972{ 1091{
973 int res = rename(oldFile, newFile); 1092 int res = rename(oldFile, newFile);
974 if (res == 0) 1093 if (res == 0)
@@ -976,7 +1095,7 @@ bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
976 if (errno != EXDEV) // (oldFile and newFile are not on the same mounted filesystem) 1095 if (errno != EXDEV) // (oldFile and newFile are not on the same mounted filesystem)
977 return false; 1096 return false;
978 1097
979 if (My_CopyFile(oldFile, newFile) == FALSE) 1098 if (My_CopyFile(oldFile, newFile, progress) == FALSE)
980 return false; 1099 return false;
981 1100
982 struct stat info_file; 1101 struct stat info_file;
@@ -990,6 +1109,11 @@ bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
990 return (unlink(oldFile) == 0); 1109 return (unlink(oldFile) == 0);
991} 1110}
992 1111
1112bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
1113{
1114 return MyMoveFile_with_Progress(oldFile, newFile, NULL);
1115}
1116
993 1117
994bool CreateDir(CFSTR path) 1118bool CreateDir(CFSTR path)
995{ 1119{
@@ -1058,17 +1182,15 @@ bool GetCurrentDir(FString &path)
1058 1182
1059 1183
1060 1184
1061bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) 1185static bool SetFileTime_Base(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, const int flags)
1062{ 1186{
1063 // need testing 1187 // need testing
1064 /* 1188 /*
1065 struct utimbuf buf; 1189 struct utimbuf buf;
1066 struct stat st; 1190 struct stat st;
1067 UNUSED_VAR(cTime) 1191 UNUSED_VAR(cTime)
1068
1069 printf("\nstat = %s\n", path); 1192 printf("\nstat = %s\n", path);
1070 int ret = stat(path, &st); 1193 int ret = stat(path, &st);
1071
1072 if (ret == 0) 1194 if (ret == 0)
1073 { 1195 {
1074 buf.actime = st.st_atime; 1196 buf.actime = st.st_atime;
@@ -1080,47 +1202,42 @@ bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CF
1080 buf.actime = cur_time; 1202 buf.actime = cur_time;
1081 buf.modtime = cur_time; 1203 buf.modtime = cur_time;
1082 } 1204 }
1083
1084 if (aTime) 1205 if (aTime)
1085 { 1206 {
1086 UInt32 ut; 1207 UInt32 ut;
1087 if (NTime::FileTimeToUnixTime(*aTime, ut)) 1208 if (NTime::FileTimeToUnixTime(*aTime, ut))
1088 buf.actime = ut; 1209 buf.actime = ut;
1089 } 1210 }
1090
1091 if (mTime) 1211 if (mTime)
1092 { 1212 {
1093 UInt32 ut; 1213 UInt32 ut;
1094 if (NTime::FileTimeToUnixTime(*mTime, ut)) 1214 if (NTime::FileTimeToUnixTime(*mTime, ut))
1095 buf.modtime = ut; 1215 buf.modtime = ut;
1096 } 1216 }
1097
1098 return utime(path, &buf) == 0; 1217 return utime(path, &buf) == 0;
1099 */ 1218 */
1100 1219
1101 // if (!aTime && !mTime) return true; 1220 // if (!aTime && !mTime) return true;
1102
1103 struct timespec times[2]; 1221 struct timespec times[2];
1104 UNUSED_VAR(cTime) 1222 UNUSED_VAR(cTime)
1105
1106 bool needChange; 1223 bool needChange;
1107 needChange = FiTime_To_timespec(aTime, times[0]); 1224 needChange = FiTime_To_timespec(aTime, times[0]);
1108 needChange |= FiTime_To_timespec(mTime, times[1]); 1225 needChange |= FiTime_To_timespec(mTime, times[1]);
1109 1226 // if (mTime) { printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec); }
1110 /*
1111 if (mTime)
1112 {
1113 printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec);
1114 }
1115 */
1116
1117 if (!needChange) 1227 if (!needChange)
1118 return true; 1228 return true;
1119 const int flags = 0; // follow link
1120 // = AT_SYMLINK_NOFOLLOW; // don't follow link
1121 return utimensat(AT_FDCWD, path, times, flags) == 0; 1229 return utimensat(AT_FDCWD, path, times, flags) == 0;
1122} 1230}
1123 1231
1232bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
1233{
1234 return SetFileTime_Base(path, cTime, aTime, mTime, 0); // (flags = 0) means follow_link
1235}
1236
1237bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
1238{
1239 return SetFileTime_Base(path, cTime, aTime, mTime, AT_SYMLINK_NOFOLLOW);
1240}
1124 1241
1125 1242
1126struct C_umask 1243struct C_umask
diff --git a/CPP/Windows/FileDir.h b/CPP/Windows/FileDir.h
index 573ffa2..9ba98fc 100644
--- a/CPP/Windows/FileDir.h
+++ b/CPP/Windows/FileDir.h
@@ -18,9 +18,20 @@ bool GetSystemDir(FString &path);
18WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file 18WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file
19but linux : allows unix time = 0 in filesystem 19but linux : allows unix time = 0 in filesystem
20*/ 20*/
21 21/*
22SetDirTime() can be used to set time for file or for dir.
23If path is symbolic link, SetDirTime() will follow symbolic link,
24and it will set timestamps of symbolic link's target file or dir.
25*/
22bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); 26bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
23 27
28/*
29SetLinkFileTime() doesn't follow symbolic link,
30and it sets timestamps for symbolic link file itself.
31If (path) is not symbolic link, it still can work (at least in some new OS versions).
32*/
33bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime);
34
24 35
25#ifdef _WIN32 36#ifdef _WIN32
26 37
@@ -41,7 +52,26 @@ int my_chown(CFSTR path, uid_t owner, gid_t group);
41bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib); 52bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib);
42 53
43 54
55#ifndef _WIN32
56#define PROGRESS_CONTINUE 0
57#define PROGRESS_CANCEL 1
58// #define PROGRESS_STOP 2
59// #define PROGRESS_QUIET 3
60#endif
61Z7_PURE_INTERFACES_BEGIN
62DECLARE_INTERFACE(ICopyFileProgress)
63{
64 // in: total, current: include all/processed alt streams.
65 // it returns PROGRESS_CONTINUE or PROGRESS_CANCEL.
66 virtual DWORD CopyFileProgress(UInt64 total, UInt64 current) = 0;
67};
68Z7_PURE_INTERFACES_END
69
44bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); 70bool MyMoveFile(CFSTR existFileName, CFSTR newFileName);
71// (progress == NULL) is allowed
72bool MyMoveFile_with_Progress(CFSTR oldFile, CFSTR newFile,
73 ICopyFileProgress *progress);
74
45 75
46#ifndef UNDER_CE 76#ifndef UNDER_CE
47bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName); 77bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName);
@@ -59,6 +89,11 @@ bool CreateComplexDir(CFSTR path);
59 89
60bool DeleteFileAlways(CFSTR name); 90bool DeleteFileAlways(CFSTR name);
61bool RemoveDirWithSubItems(const FString &path); 91bool RemoveDirWithSubItems(const FString &path);
92#ifdef _WIN32
93bool RemoveDirAlways_if_Empty(const FString &path);
94#else
95#define RemoveDirAlways_if_Empty RemoveDir
96#endif
62 97
63bool MyGetFullPathName(CFSTR path, FString &resFullPath); 98bool MyGetFullPathName(CFSTR path, FString &resFullPath);
64bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName); 99bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName);
@@ -87,7 +122,9 @@ public:
87 bool Create(CFSTR pathPrefix, NIO::COutFile *outFile); // pathPrefix is not folder prefix 122 bool Create(CFSTR pathPrefix, NIO::COutFile *outFile); // pathPrefix is not folder prefix
88 bool CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile); 123 bool CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile);
89 bool Remove(); 124 bool Remove();
90 bool MoveTo(CFSTR name, bool deleteDestBefore); 125 // bool MoveTo(CFSTR name, bool deleteDestBefore);
126 bool MoveTo(CFSTR name, bool deleteDestBefore,
127 ICopyFileProgress *progress);
91}; 128};
92 129
93 130
diff --git a/CPP/Windows/FileFind.cpp b/CPP/Windows/FileFind.cpp
index ca387f6..64075ab 100644
--- a/CPP/Windows/FileFind.cpp
+++ b/CPP/Windows/FileFind.cpp
@@ -731,7 +731,7 @@ bool CFileInfo::Find(CFSTR path, bool followLink)
731 bool isOK = false; 731 bool isOK = false;
732 if (finder.FindFirst(s, *this)) 732 if (finder.FindFirst(s, *this))
733 { 733 {
734 if (Name == FTEXT(".")) 734 if (Name.IsEqualTo("."))
735 { 735 {
736 Name = path + prefixSize; 736 Name = path + prefixSize;
737 return true; 737 return true;
@@ -769,6 +769,13 @@ bool CFileInfo::Find(CFSTR path, bool followLink)
769 769
770 // return FollowReparse(path, IsDir()); 770 // return FollowReparse(path, IsDir());
771 return Fill_From_ByHandleFileInfo(path); 771 return Fill_From_ByHandleFileInfo(path);
772/*
773 // Fill_From_ByHandleFileInfo returns false (with Access Denied error),
774 // if there is reparse link file (not directory reparse item).
775 if (Fill_From_ByHandleFileInfo(path))
776 return true;
777 return HasReparsePoint();
778*/
772} 779}
773 780
774bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path) 781bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path)
diff --git a/CPP/Windows/FileIO.h b/CPP/Windows/FileIO.h
index 6ba40eb..26edef4 100644
--- a/CPP/Windows/FileIO.h
+++ b/CPP/Windows/FileIO.h
@@ -11,8 +11,7 @@
11 11
12#define Z7_WIN_SYMLINK_FLAG_RELATIVE 1 12#define Z7_WIN_SYMLINK_FLAG_RELATIVE 1
13 13
14// what the meaning of that FLAG or field (2)? 14#define Z7_WIN_LX_SYMLINK_VERSION_2 2
15#define Z7_WIN_LX_SYMLINK_FLAG 2
16 15
17#ifdef _WIN32 16#ifdef _WIN32
18 17
@@ -44,7 +43,33 @@ namespace NWindows {
44namespace NFile { 43namespace NFile {
45 44
46#if defined(_WIN32) && !defined(UNDER_CE) 45#if defined(_WIN32) && !defined(UNDER_CE)
47bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL); 46/*
47 in: (CByteBuffer &dest) is empty
48 in: (path) uses Windows path separator (\).
49 out: (path) uses Linux path separator (/).
50 if (isAbsPath == true), then "c:\\" prefix is replaced to "/mnt/c/" prefix
51*/
52void Convert_WinPath_to_WslLinuxPath(FString &path, bool convertDrivePath);
53// (path) must use Linux path separator (/).
54void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path);
55
56/*
57 in: (CByteBuffer &dest) is empty
58 if (isSymLink == false) : MOUNT_POINT : (path) must be absolute.
59 if (isSymLink == true) : SYMLINK : Windows
60 (path) must use Windows path separator (\).
61 (path) must be without link "\\??\\" prefix.
62 link "\\??\\" prefix will be added inside FillLinkData(), if path is absolute.
63*/
64void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink);
65// in: (CByteBuffer &dest) is empty
66inline void FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL)
67{
68 if (isWSL)
69 FillLinkData_WslLink(dest, path);
70 else
71 FillLinkData_WinLink(dest, path, isSymLink);
72}
48#endif 73#endif
49 74
50struct CReparseShortInfo 75struct CReparseShortInfo
@@ -61,7 +86,6 @@ struct CReparseAttr
61 UInt32 Flags; 86 UInt32 Flags;
62 UString SubsName; 87 UString SubsName;
63 UString PrintName; 88 UString PrintName;
64
65 AString WslName; 89 AString WslName;
66 90
67 bool HeaderError; 91 bool HeaderError;
@@ -71,8 +95,7 @@ struct CReparseAttr
71 95
72 CReparseAttr(): Tag(0), Flags(0) {} 96 CReparseAttr(): Tag(0), Flags(0) {}
73 97
74 // Parse() 98 // returns (true) and (ErrorCode = 0), if (it's correct known link)
75 // returns (true) and (ErrorCode = 0), if (it'a correct known link)
76 // returns (false) and (ErrorCode = ERROR_REPARSE_TAG_INVALID), if unknown tag 99 // returns (false) and (ErrorCode = ERROR_REPARSE_TAG_INVALID), if unknown tag
77 bool Parse(const Byte *p, size_t size); 100 bool Parse(const Byte *p, size_t size);
78 101
@@ -80,18 +103,14 @@ struct CReparseAttr
80 bool IsSymLink_Win() const { return Tag == Z7_WIN_IO_REPARSE_TAG_SYMLINK; } 103 bool IsSymLink_Win() const { return Tag == Z7_WIN_IO_REPARSE_TAG_SYMLINK; }
81 bool IsSymLink_WSL() const { return Tag == Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK; } 104 bool IsSymLink_WSL() const { return Tag == Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK; }
82 105
106 // note: "/dir1/path" is marked as relative.
83 bool IsRelative_Win() const { return Flags == Z7_WIN_SYMLINK_FLAG_RELATIVE; } 107 bool IsRelative_Win() const { return Flags == Z7_WIN_SYMLINK_FLAG_RELATIVE; }
84 108
85 bool IsRelative_WSL() const 109 bool IsRelative_WSL() const
86 { 110 {
87 if (WslName.IsEmpty()) 111 return WslName[0] != '/'; // WSL uses unix path separator
88 return true;
89 char c = WslName[0];
90 return !IS_PATH_SEPAR(c);
91 } 112 }
92 113
93 // bool IsVolume() const;
94
95 bool IsOkNamePair() const; 114 bool IsOkNamePair() const;
96 UString GetPath() const; 115 UString GetPath() const;
97}; 116};
diff --git a/CPP/Windows/FileLink.cpp b/CPP/Windows/FileLink.cpp
index bb380ec..2883c82 100644
--- a/CPP/Windows/FileLink.cpp
+++ b/CPP/Windows/FileLink.cpp
@@ -39,12 +39,24 @@ namespace NFile {
39using namespace NName; 39using namespace NName;
40 40
41/* 41/*
42Win10 Junctions/SymLinks:
43 - (/) slash doesn't work as path separator
44 - Win10 preinstalled junctions don't use tail backslash, but tail backslashes also work.
45 - double backslash works only after drive prefix "c:\\dir1\dir2\",
46 and doesn't work in another places.
47 - absolute path without \??\ prefix doesn't work
48 - absolute path "c:" doesn't work
49*/
50
51/*
42 Reparse Points (Junctions and Symbolic Links): 52 Reparse Points (Junctions and Symbolic Links):
43 struct 53 struct
44 { 54 {
45 UInt32 Tag; 55 UInt32 Tag;
46 UInt16 Size; // not including starting 8 bytes 56 UInt16 Size; // not including starting 8 bytes
47 UInt16 Reserved; // = 0 57 UInt16 Reserved; // = 0, DOCs: // Length, in bytes, of the unparsed portion of
58 // the file name pointed to by the FileName member of the associated file object.
59 // This member is only valid for create operations when the I/O fails with STATUS_REPARSE.
48 60
49 UInt16 SubstituteOffset; // offset in bytes from start of namesChars 61 UInt16 SubstituteOffset; // offset in bytes from start of namesChars
50 UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL 62 UInt16 SubstituteLen; // size in bytes, it doesn't include tailed NUL
@@ -68,6 +80,16 @@ using namespace NName;
68 2) Default Order in table: 80 2) Default Order in table:
69 Print Path 81 Print Path
70 Substitute Path 82 Substitute Path
83
84DOCS:
85 The print name SHOULD be an informative pathname, suitable for display
86 to a user, that also identifies the target of the mount point.
87 Neither of these pathnames can contain dot directory names.
88
89reparse tags, with the exception of IO_REPARSE_TAG_SYMLINK,
90are processed on the server and are not processed by a client
91after transmission over the wire.
92Clients SHOULD treat associated reparse data as opaque data.
71*/ 93*/
72 94
73/* 95/*
@@ -93,7 +115,8 @@ static const UInt32 kReparseFlags_Microsoft = ((UInt32)1 << 31);
93#define Get16(p) GetUi16(p) 115#define Get16(p) GetUi16(p)
94#define Get32(p) GetUi32(p) 116#define Get32(p) GetUi32(p)
95 117
96static const wchar_t * const k_LinkPrefix = L"\\??\\"; 118static const char * const k_LinkPrefix = "\\??\\";
119static const char * const k_LinkPrefix_UNC = "\\??\\UNC\\";
97static const unsigned k_LinkPrefix_Size = 4; 120static const unsigned k_LinkPrefix_Size = 4;
98 121
99static bool IsLinkPrefix(const wchar_t *s) 122static bool IsLinkPrefix(const wchar_t *s)
@@ -102,7 +125,7 @@ static bool IsLinkPrefix(const wchar_t *s)
102} 125}
103 126
104/* 127/*
105static const wchar_t * const k_VolumePrefix = L"Volume{"; 128static const char * const k_VolumePrefix = "Volume{";
106static const bool IsVolumeName(const wchar_t *s) 129static const bool IsVolumeName(const wchar_t *s)
107{ 130{
108 return IsString1PrefixedByString2(s, k_VolumePrefix); 131 return IsString1PrefixedByString2(s, k_VolumePrefix);
@@ -118,7 +141,7 @@ static void WriteString(Byte *dest, const wchar_t *path)
118{ 141{
119 for (;;) 142 for (;;)
120 { 143 {
121 wchar_t c = *path++; 144 const wchar_t c = *path++;
122 if (c == 0) 145 if (c == 0)
123 return; 146 return;
124 Set16(dest, (UInt16)c) 147 Set16(dest, (UInt16)c)
@@ -126,62 +149,103 @@ static void WriteString(Byte *dest, const wchar_t *path)
126 } 149 }
127} 150}
128 151
129bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL) 152#ifdef _WIN32
153void Convert_WinPath_to_WslLinuxPath(FString &s, bool convertDrivePath)
130{ 154{
131 bool isAbs = IsAbsolutePath(path); 155 if (convertDrivePath && IsDrivePath(s))
132 if (!isAbs && !isSymLink)
133 return false;
134
135 if (isWSL)
136 { 156 {
137 // unsupported characters probably use Replacement Character UTF-16 0xFFFD 157 FChar c = s[0];
138 AString utf; 158 c = MyCharLower_Ascii(c);
139 ConvertUnicodeToUTF8(path, utf); 159 s.DeleteFrontal(2);
140 const size_t size = 4 + utf.Len(); 160 s.InsertAtFront(c);
141 if (size != (UInt16)size) 161 s.Insert(0, FTEXT("/mnt/"));
142 return false;
143 dest.Alloc(8 + size);
144 Byte *p = dest;
145 Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK)
146 Set16(p + 4, (UInt16)(size))
147 Set16(p + 6, 0)
148 Set32(p + 8, Z7_WIN_LX_SYMLINK_FLAG)
149 memcpy(p + 12, utf.Ptr(), utf.Len());
150 return true;
151 } 162 }
163 s.Replace(FCHAR_PATH_SEPARATOR, FTEXT('/'));
164}
165#endif
152 166
153 // usual symbolic LINK (NOT WSL)
154 167
155 bool needPrintName = true; 168static const unsigned k_Link_Size_Limit = 1u << 16; // 16-bit field is used for size.
169
170void FillLinkData_WslLink(CByteBuffer &dest, const wchar_t *path)
171{
172 // dest.Free(); // it's empty already
173 // WSL probably uses Replacement Character UTF-16 0xFFFD for unsupported characters?
174 AString utf;
175 ConvertUnicodeToUTF8(path, utf);
176 const unsigned size = 4 + utf.Len();
177 if (size >= k_Link_Size_Limit)
178 return;
179 dest.Alloc(8 + size);
180 Byte *p = dest;
181 Set32(p, Z7_WIN_IO_REPARSE_TAG_LX_SYMLINK)
182 // Set32(p + 4, (UInt32)size)
183 Set16(p + 4, (UInt16)size)
184 Set16(p + 6, 0)
185 Set32(p + 8, Z7_WIN_LX_SYMLINK_VERSION_2)
186 memcpy(p + 12, utf.Ptr(), utf.Len());
187}
188
156 189
157 if (IsSuperPath(path)) 190void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink)
191{
192 // dest.Free(); // it's empty already
193 bool isAbs = false;
194 if (IS_PATH_SEPAR(path[0]))
158 { 195 {
159 path += kSuperPathPrefixSize; 196 // root paths "\dir1\path" are marked as relative
160 if (!IsDrivePath(path)) 197 if (IS_PATH_SEPAR(path[1]))
161 needPrintName = false; 198 isAbs = true;
199 }
200 else
201 isAbs = IsAbsolutePath(path);
202 if (!isAbs && !isSymLink)
203 {
204 // Win10 allows us to create relative MOUNT_POINT.
205 // But relative MOUNT_POINT will not work when accessing it.
206 // So we prevent useless creation of a relative MOUNT_POINT.
207 return;
162 } 208 }
163 209
164 const unsigned add_Prefix_Len = isAbs ? k_LinkPrefix_Size : 0; 210 bool needPrintName = true;
165 211 UString subs (path);
212 if (isAbs)
213 {
214 const bool isSuperPath = IsSuperPath(path);
215 if (!isSuperPath && NName::IsNetworkPath(us2fs(path)))
216 {
217 subs = k_LinkPrefix_UNC;
218 subs += (path + 2);
219 }
220 else
221 {
222 if (isSuperPath)
223 {
224 // we remove super prefix:
225 path += kSuperPathPrefixSize;
226 // we want to get correct abolute path in PrintName still.
227 if (!IsDrivePath(path))
228 needPrintName = false; // we need "\\server\path" for print name.
229 }
230 subs = k_LinkPrefix;
231 subs += path;
232 }
233 }
234 const size_t len1 = subs.Len() * 2;
166 size_t len2 = (size_t)MyStringLen(path) * 2; 235 size_t len2 = (size_t)MyStringLen(path) * 2;
167 const size_t len1 = len2 + add_Prefix_Len * 2;
168 if (!needPrintName) 236 if (!needPrintName)
169 len2 = 0; 237 len2 = 0;
170 238 size_t totalNamesSize = len1 + len2;
171 size_t totalNamesSize = (len1 + len2);
172
173 /* some WIM imagex software uses old scheme for symbolic links. 239 /* some WIM imagex software uses old scheme for symbolic links.
174 so we can old scheme for byte to byte compatibility */ 240 so we can use old scheme for byte to byte compatibility */
175 241 const bool newOrderScheme = isSymLink;
176 bool newOrderScheme = isSymLink;
177 // newOrderScheme = false; 242 // newOrderScheme = false;
178
179 if (!newOrderScheme) 243 if (!newOrderScheme)
180 totalNamesSize += 2 * 2; 244 totalNamesSize += 2 * 2; // we use NULL terminators in old scheme.
181 245
182 const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize; 246 const size_t size = 8 + 8 + (isSymLink ? 4 : 0) + totalNamesSize;
183 if (size != (UInt16)size) 247 if (size >= k_Link_Size_Limit)
184 return false; 248 return;
185 dest.Alloc(size); 249 dest.Alloc(size);
186 memset(dest, 0, size); 250 memset(dest, 0, size);
187 const UInt32 tag = isSymLink ? 251 const UInt32 tag = isSymLink ?
@@ -189,6 +253,7 @@ bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool i
189 Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT; 253 Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT;
190 Byte *p = dest; 254 Byte *p = dest;
191 Set32(p, tag) 255 Set32(p, tag)
256 // Set32(p + 4, (UInt32)(size - 8))
192 Set16(p + 4, (UInt16)(size - 8)) 257 Set16(p + 4, (UInt16)(size - 8))
193 Set16(p + 6, 0) 258 Set16(p + 6, 0)
194 p += 8; 259 p += 8;
@@ -204,21 +269,16 @@ bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool i
204 Set16(p + 2, (UInt16)len1) 269 Set16(p + 2, (UInt16)len1)
205 Set16(p + 4, (UInt16)printOffs) 270 Set16(p + 4, (UInt16)printOffs)
206 Set16(p + 6, (UInt16)len2) 271 Set16(p + 6, (UInt16)len2)
207
208 p += 8; 272 p += 8;
209 if (isSymLink) 273 if (isSymLink)
210 { 274 {
211 UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE; 275 const UInt32 flags = isAbs ? 0 : Z7_WIN_SYMLINK_FLAG_RELATIVE;
212 Set32(p, flags) 276 Set32(p, flags)
213 p += 4; 277 p += 4;
214 } 278 }
215 279 WriteString(p + subOffs, subs);
216 if (add_Prefix_Len != 0)
217 WriteString(p + subOffs, k_LinkPrefix);
218 WriteString(p + subOffs + add_Prefix_Len * 2, path);
219 if (needPrintName) 280 if (needPrintName)
220 WriteString(p + printOffs, path); 281 WriteString(p + printOffs, path);
221 return true;
222} 282}
223 283
224#endif // defined(_WIN32) && !defined(UNDER_CE) 284#endif // defined(_WIN32) && !defined(UNDER_CE)
@@ -230,7 +290,7 @@ static void GetString(const Byte *p, unsigned len, UString &res)
230 unsigned i; 290 unsigned i;
231 for (i = 0; i < len; i++) 291 for (i = 0; i < len; i++)
232 { 292 {
233 wchar_t c = Get16(p + i * 2); 293 const wchar_t c = Get16(p + (size_t)i * 2);
234 if (c == 0) 294 if (c == 0)
235 break; 295 break;
236 s[i] = c; 296 s[i] = c;
@@ -239,6 +299,7 @@ static void GetString(const Byte *p, unsigned len, UString &res)
239 res.ReleaseBuf_SetLen(i); 299 res.ReleaseBuf_SetLen(i);
240} 300}
241 301
302
242bool CReparseAttr::Parse(const Byte *p, size_t size) 303bool CReparseAttr::Parse(const Byte *p, size_t size)
243{ 304{
244 ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA; 305 ErrorCode = (DWORD)ERROR_INVALID_REPARSE_DATA;
@@ -250,7 +311,12 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
250 return false; 311 return false;
251 Tag = Get32(p); 312 Tag = Get32(p);
252 if (Get16(p + 6) != 0) // padding 313 if (Get16(p + 6) != 0) // padding
253 return false; 314 {
315 // DOCs: Reserved : the field SHOULD be set to 0
316 // and MUST be ignored (by parser).
317 // Win10 ignores it.
318 MinorError = true; // optional
319 }
254 unsigned len = Get16(p + 4); 320 unsigned len = Get16(p + 4);
255 p += 8; 321 p += 8;
256 size -= 8; 322 size -= 8;
@@ -262,8 +328,6 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
262 (type & kReparseFlags_Microsoft) == 0 || 328 (type & kReparseFlags_Microsoft) == 0 ||
263 (type & 0xFFFF) != 3) 329 (type & 0xFFFF) != 3)
264 */ 330 */
265
266
267 HeaderError = false; 331 HeaderError = false;
268 332
269 if ( Tag != Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT 333 if ( Tag != Z7_WIN_IO_REPARSE_TAG_MOUNT_POINT
@@ -282,8 +346,7 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
282 { 346 {
283 if (len < 4) 347 if (len < 4)
284 return false; 348 return false;
285 Flags = Get32(p); // maybe it's not Flags 349 if (Get32(p) != Z7_WIN_LX_SYMLINK_VERSION_2)
286 if (Flags != Z7_WIN_LX_SYMLINK_FLAG)
287 return false; 350 return false;
288 len -= 4; 351 len -= 4;
289 p += 4; 352 p += 4;
@@ -291,12 +354,13 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
291 unsigned i; 354 unsigned i;
292 for (i = 0; i < len; i++) 355 for (i = 0; i < len; i++)
293 { 356 {
294 char c = (char)p[i]; 357 const char c = (char)p[i];
295 s[i] = c; 358 s[i] = c;
296 if (c == 0) 359 if (c == 0)
297 break; 360 break;
298 } 361 }
299 WslName.ReleaseBuf_SetEnd(i); 362 s[i] = 0;
363 WslName.ReleaseBuf_SetLen(i);
300 MinorError = (i != len); 364 MinorError = (i != len);
301 ErrorCode = 0; 365 ErrorCode = 0;
302 return true; 366 return true;
@@ -304,10 +368,10 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
304 368
305 if (len < 8) 369 if (len < 8)
306 return false; 370 return false;
307 unsigned subOffs = Get16(p); 371 const unsigned subOffs = Get16(p);
308 unsigned subLen = Get16(p + 2); 372 const unsigned subLen = Get16(p + 2);
309 unsigned printOffs = Get16(p + 4); 373 const unsigned printOffs = Get16(p + 4);
310 unsigned printLen = Get16(p + 6); 374 const unsigned printLen = Get16(p + 6);
311 len -= 8; 375 len -= 8;
312 p += 8; 376 p += 8;
313 377
@@ -335,15 +399,17 @@ bool CReparseAttr::Parse(const Byte *p, size_t size)
335 399
336bool CReparseShortInfo::Parse(const Byte *p, size_t size) 400bool CReparseShortInfo::Parse(const Byte *p, size_t size)
337{ 401{
338 const Byte *start = p; 402 const Byte * const start = p;
339 Offset= 0; 403 Offset = 0;
340 Size = 0; 404 Size = 0;
341 if (size < 8) 405 if (size < 8)
342 return false; 406 return false;
343 UInt32 Tag = Get32(p); 407 const UInt32 Tag = Get32(p);
344 UInt32 len = Get16(p + 4); 408 UInt32 len = Get16(p + 4);
409 /*
345 if (len + 8 > size) 410 if (len + 8 > size)
346 return false; 411 return false;
412 */
347 /* 413 /*
348 if ((type & kReparseFlags_Alias) == 0 || 414 if ((type & kReparseFlags_Alias) == 0 ||
349 (type & kReparseFlags_Microsoft) == 0 || 415 (type & kReparseFlags_Microsoft) == 0 ||
@@ -353,16 +419,14 @@ bool CReparseShortInfo::Parse(const Byte *p, size_t size)
353 Tag != Z7_WIN_IO_REPARSE_TAG_SYMLINK) 419 Tag != Z7_WIN_IO_REPARSE_TAG_SYMLINK)
354 // return true; 420 // return true;
355 return false; 421 return false;
356 422 /*
357 if (Get16(p + 6) != 0) // padding 423 if (Get16(p + 6) != 0) // padding
358 return false; 424 return false;
359 425 */
360 p += 8; 426 p += 8;
361 size -= 8; 427 size -= 8;
362
363 if (len != size) // do we need that check? 428 if (len != size) // do we need that check?
364 return false; 429 return false;
365
366 if (len < 8) 430 if (len < 8)
367 return false; 431 return false;
368 unsigned subOffs = Get16(p); 432 unsigned subOffs = Get16(p);
@@ -396,10 +460,14 @@ bool CReparseAttr::IsOkNamePair() const
396{ 460{
397 if (IsLinkPrefix(SubsName)) 461 if (IsLinkPrefix(SubsName))
398 { 462 {
463 if (PrintName == GetPath())
464 return true;
465/*
399 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size))) 466 if (!IsDrivePath(SubsName.Ptr(k_LinkPrefix_Size)))
400 return PrintName.IsEmpty(); 467 return PrintName.IsEmpty();
401 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0) 468 if (wcscmp(SubsName.Ptr(k_LinkPrefix_Size), PrintName) == 0)
402 return true; 469 return true;
470*/
403 } 471 }
404 return wcscmp(SubsName, PrintName) == 0; 472 return wcscmp(SubsName, PrintName) == 0;
405} 473}
@@ -415,21 +483,26 @@ bool CReparseAttr::IsVolume() const
415 483
416UString CReparseAttr::GetPath() const 484UString CReparseAttr::GetPath() const
417{ 485{
486 UString s (SubsName);
418 if (IsSymLink_WSL()) 487 if (IsSymLink_WSL())
419 { 488 {
420 UString u;
421 // if (CheckUTF8(attr.WslName) 489 // if (CheckUTF8(attr.WslName)
422 if (!ConvertUTF8ToUnicode(WslName, u)) 490 if (!ConvertUTF8ToUnicode(WslName, s))
423 MultiByteToUnicodeString2(u, WslName); 491 MultiByteToUnicodeString2(s, WslName);
424 return u;
425 } 492 }
426 493 else if (IsLinkPrefix(s))
427 UString s (SubsName);
428 if (IsLinkPrefix(s))
429 { 494 {
430 s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\" 495 if (IsString1PrefixedByString2_NoCase_Ascii(s.Ptr(), k_LinkPrefix_UNC))
431 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size))) 496 {
432 s.DeleteFrontal(k_LinkPrefix_Size); 497 s.DeleteFrontal(6);
498 s.ReplaceOneCharAtPos(0, '\\');
499 }
500 else
501 {
502 s.ReplaceOneCharAtPos(1, '\\'); // we normalize prefix from "\??\" to "\\?\"
503 if (IsDrivePath(s.Ptr(k_LinkPrefix_Size)))
504 s.DeleteFrontal(k_LinkPrefix_Size);
505 }
433 } 506 }
434 return s; 507 return s;
435} 508}
@@ -468,7 +541,7 @@ bool GetReparseData(CFSTR path, CByteBuffer &reparseData, BY_HANDLE_FILE_INFORMA
468static bool CreatePrefixDirOfFile(CFSTR path) 541static bool CreatePrefixDirOfFile(CFSTR path)
469{ 542{
470 FString path2 (path); 543 FString path2 (path);
471 int pos = path2.ReverseFind_PathSepar(); 544 const int pos = path2.ReverseFind_PathSepar();
472 if (pos < 0) 545 if (pos < 0)
473 return true; 546 return true;
474 #ifdef _WIN32 547 #ifdef _WIN32
@@ -494,6 +567,8 @@ static bool OutIoReparseData(DWORD controlCode, CFSTR path, void *data, DWORD si
494} 567}
495 568
496 569
570// MOUNT_POINT (Junction Point) and LX_SYMLINK (WSL) can be written without administrator rights.
571// SYMLINK requires administrator rights.
497// If there is Reparse data already, it still writes new Reparse data 572// If there is Reparse data already, it still writes new Reparse data
498bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) 573bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size)
499{ 574{
@@ -540,10 +615,11 @@ bool DeleteReparseData(CFSTR path)
540 SetLastError(ERROR_INVALID_REPARSE_DATA); 615 SetLastError(ERROR_INVALID_REPARSE_DATA);
541 return false; 616 return false;
542 } 617 }
543 BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE]; 618 // BYTE buf[my_REPARSE_DATA_BUFFER_HEADER_SIZE];
544 memset(buf, 0, sizeof(buf)); 619 // memset(buf, 0, sizeof(buf));
545 memcpy(buf, reparseData, 4); // tag 620 // memcpy(buf, reparseData, 4); // tag
546 return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, buf, sizeof(buf)); 621 memset(reparseData + 4, 0, my_REPARSE_DATA_BUFFER_HEADER_SIZE - 4);
622 return OutIoReparseData(my_FSCTL_DELETE_REPARSE_POINT, path, reparseData, my_REPARSE_DATA_BUFFER_HEADER_SIZE);
547} 623}
548 624
549} 625}
diff --git a/CPP/Windows/FileName.cpp b/CPP/Windows/FileName.cpp
index c16b3d4..eb62567 100644
--- a/CPP/Windows/FileName.cpp
+++ b/CPP/Windows/FileName.cpp
@@ -65,8 +65,15 @@ void NormalizeDirPathPrefix(UString &dirPath)
65 dirPath.Add_PathSepar(); 65 dirPath.Add_PathSepar();
66} 66}
67 67
68
69#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a')))
70bool IsDrivePath (const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); }
71// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; }
72
68#ifdef _WIN32 73#ifdef _WIN32
69 74
75bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; }
76
70#ifndef USE_UNICODE_FSTRING 77#ifndef USE_UNICODE_FSTRING
71#ifdef Z7_LONG_PATH 78#ifdef Z7_LONG_PATH
72static void NormalizeDirSeparators(UString &s) 79static void NormalizeDirSeparators(UString &s)
@@ -87,13 +94,6 @@ void NormalizeDirSeparators(FString &s)
87 s.ReplaceOneCharAtPos(i, FCHAR_PATH_SEPARATOR); 94 s.ReplaceOneCharAtPos(i, FCHAR_PATH_SEPARATOR);
88} 95}
89 96
90#endif
91
92
93#define IS_LETTER_CHAR(c) ((((unsigned)(int)(c) | 0x20) - (unsigned)'a' <= (unsigned)('z' - 'a')))
94
95bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); }
96
97bool IsAltPathPrefix(CFSTR s) throw() 97bool IsAltPathPrefix(CFSTR s) throw()
98{ 98{
99 unsigned len = MyStringLen(s); 99 unsigned len = MyStringLen(s);
@@ -117,16 +117,23 @@ bool IsAltPathPrefix(CFSTR s) throw()
117 return true; 117 return true;
118} 118}
119 119
120#if defined(_WIN32) && !defined(UNDER_CE) 120#endif // _WIN32
121
121 122
122const char * const kSuperPathPrefix = "\\\\?\\"; 123const char * const kSuperPathPrefix =
124 STRING_PATH_SEPARATOR
125 STRING_PATH_SEPARATOR "?"
126 STRING_PATH_SEPARATOR;
123#ifdef Z7_LONG_PATH 127#ifdef Z7_LONG_PATH
124static const char * const kSuperUncPrefix = "\\\\?\\UNC\\"; 128static const char * const kSuperUncPrefix =
129 STRING_PATH_SEPARATOR
130 STRING_PATH_SEPARATOR "?"
131 STRING_PATH_SEPARATOR "UNC"
132 STRING_PATH_SEPARATOR;
125#endif 133#endif
126 134
127#define IS_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '.' && IS_SEPAR((s)[3])) 135#define IS_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '.' && IS_SEPAR((s)[3]))
128#define IS_SUPER_PREFIX(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '?' && IS_SEPAR((s)[3])) 136#define IS_SUPER_PREFIX(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && (s)[2] == '?' && IS_SEPAR((s)[3]))
129#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3]))
130 137
131#define IS_UNC_WITH_SLASH(s) ( \ 138#define IS_UNC_WITH_SLASH(s) ( \
132 ((s)[0] == 'U' || (s)[0] == 'u') \ 139 ((s)[0] == 'U' || (s)[0] == 'u') \
@@ -134,6 +141,16 @@ static const char * const kSuperUncPrefix = "\\\\?\\UNC\\";
134 && ((s)[2] == 'C' || (s)[2] == 'c') \ 141 && ((s)[2] == 'C' || (s)[2] == 'c') \
135 && IS_SEPAR((s)[3])) 142 && IS_SEPAR((s)[3]))
136 143
144static const unsigned kDrivePrefixSize = 3; /* c:\ */
145
146bool IsSuperPath(const wchar_t *s) throw();
147bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); }
148// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
149
150#if defined(_WIN32) && !defined(UNDER_CE)
151
152#define IS_SUPER_OR_DEVICE_PATH(s) (IS_SEPAR((s)[0]) && IS_SEPAR((s)[1]) && ((s)[2] == '?' || (s)[2] == '.') && IS_SEPAR((s)[3]))
153bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
137bool IsDevicePath(CFSTR s) throw() 154bool IsDevicePath(CFSTR s) throw()
138{ 155{
139 #ifdef UNDER_CE 156 #ifdef UNDER_CE
@@ -154,7 +171,7 @@ bool IsDevicePath(CFSTR s) throw()
154 171
155 if (!IS_DEVICE_PATH(s)) 172 if (!IS_DEVICE_PATH(s))
156 return false; 173 return false;
157 unsigned len = MyStringLen(s); 174 const unsigned len = MyStringLen(s);
158 if (len == 6 && s[5] == ':') 175 if (len == 6 && s[5] == ':')
159 return true; 176 return true;
160 if (len < 18 || len > 22 || !IsString1PrefixedByString2(s + kDevicePathPrefixSize, "PhysicalDrive")) 177 if (len < 18 || len > 22 || !IsString1PrefixedByString2(s + kDevicePathPrefixSize, "PhysicalDrive"))
@@ -174,7 +191,7 @@ bool IsNetworkPath(CFSTR s) throw()
174 return false; 191 return false;
175 if (IsSuperUncPath(s)) 192 if (IsSuperUncPath(s))
176 return true; 193 return true;
177 FChar c = s[2]; 194 const FChar c = s[2];
178 return (c != '.' && c != '?'); 195 return (c != '.' && c != '?');
179} 196}
180 197
@@ -187,7 +204,7 @@ unsigned GetNetworkServerPrefixSize(CFSTR s) throw()
187 prefixSize = kSuperUncPathPrefixSize; 204 prefixSize = kSuperUncPathPrefixSize;
188 else 205 else
189 { 206 {
190 FChar c = s[2]; 207 const FChar c = s[2];
191 if (c == '.' || c == '?') 208 if (c == '.' || c == '?')
192 return 0; 209 return 0;
193 } 210 }
@@ -209,14 +226,6 @@ bool IsNetworkShareRootPath(CFSTR s) throw()
209 return s[(unsigned)pos + 1] == 0; 226 return s[(unsigned)pos + 1] == 0;
210} 227}
211 228
212static const unsigned kDrivePrefixSize = 3; /* c:\ */
213
214bool IsDrivePath2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':'; }
215// bool IsDriveName2(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && s[2] == 0; }
216bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); }
217bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); }
218// bool IsSuperUncPath(const wchar_t *s) throw() { return (IS_SUPER_PREFIX(s) && IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)); }
219
220bool IsAltStreamPrefixWithColon(const UString &s) throw() 229bool IsAltStreamPrefixWithColon(const UString &s) throw()
221{ 230{
222 if (s.IsEmpty()) 231 if (s.IsEmpty())
@@ -278,12 +287,14 @@ bool IsAbsolutePath(const wchar_t *s) throw()
278int FindAltStreamColon(CFSTR path) throw() 287int FindAltStreamColon(CFSTR path) throw()
279{ 288{
280 unsigned i = 0; 289 unsigned i = 0;
281 if (IsDrivePath2(path)) 290 if (IsSuperPath(path))
282 i = 2; 291 i = kSuperPathPrefixSize;
292 if (IsDrivePath2(path + i))
293 i += 2;
283 int colonPos = -1; 294 int colonPos = -1;
284 for (;; i++) 295 for (;; i++)
285 { 296 {
286 FChar c = path[i]; 297 const FChar c = path[i];
287 if (c == 0) 298 if (c == 0)
288 return colonPos; 299 return colonPos;
289 if (c == ':') 300 if (c == ':')
@@ -347,14 +358,16 @@ unsigned GetRootPrefixSize(CFSTR s) throw()
347} 358}
348 359
349#endif // USE_UNICODE_FSTRING 360#endif // USE_UNICODE_FSTRING
361#endif // _WIN32
362
350 363
351static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw() 364static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw()
352{ 365{
353 // Network path: we look "server\path\" as root prefix 366 // Network path: we look "server\path\" as root prefix
354 int pos = FindSepar(s); 367 const int pos = FindSepar(s);
355 if (pos < 0) 368 if (pos < 0)
356 return 0; 369 return 0;
357 int pos2 = FindSepar(s + (unsigned)pos + 1); 370 const int pos2 = FindSepar(s + (unsigned)pos + 1);
358 if (pos2 < 0) 371 if (pos2 < 0)
359 return 0; 372 return 0;
360 return (unsigned)(pos + pos2 + 2); 373 return (unsigned)(pos + pos2 + 2);
@@ -368,7 +381,7 @@ static unsigned GetRootPrefixSize_Of_SimplePath(const wchar_t *s) throw()
368 return 0; 381 return 0;
369 if (s[1] == 0 || !IS_SEPAR(s[1])) 382 if (s[1] == 0 || !IS_SEPAR(s[1]))
370 return 1; 383 return 1;
371 unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2); 384 const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + 2);
372 return (size == 0) ? 0 : 2 + size; 385 return (size == 0) ? 0 : 2 + size;
373} 386}
374 387
@@ -376,17 +389,21 @@ static unsigned GetRootPrefixSize_Of_SuperPath(const wchar_t *s) throw()
376{ 389{
377 if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize)) 390 if (IS_UNC_WITH_SLASH(s + kSuperPathPrefixSize))
378 { 391 {
379 unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize); 392 const unsigned size = GetRootPrefixSize_Of_NetworkPath(s + kSuperUncPathPrefixSize);
380 return (size == 0) ? 0 : kSuperUncPathPrefixSize + size; 393 return (size == 0) ? 0 : kSuperUncPathPrefixSize + size;
381 } 394 }
382 // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\" 395 // we support \\?\c:\ paths and volume GUID paths \\?\Volume{GUID}\"
383 int pos = FindSepar(s + kSuperPathPrefixSize); 396 const int pos = FindSepar(s + kSuperPathPrefixSize);
384 if (pos < 0) 397 if (pos < 0)
385 return 0; 398 return 0;
386 return kSuperPathPrefixSize + (unsigned)(pos + 1); 399 return kSuperPathPrefixSize + (unsigned)(pos + 1);
387} 400}
388 401
402#ifdef _WIN32
389unsigned GetRootPrefixSize(const wchar_t *s) throw() 403unsigned GetRootPrefixSize(const wchar_t *s) throw()
404#else
405unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw()
406#endif
390{ 407{
391 if (IS_DEVICE_PATH(s)) 408 if (IS_DEVICE_PATH(s))
392 return kDevicePathPrefixSize; 409 return kDevicePathPrefixSize;
@@ -395,7 +412,7 @@ unsigned GetRootPrefixSize(const wchar_t *s) throw()
395 return GetRootPrefixSize_Of_SimplePath(s); 412 return GetRootPrefixSize_Of_SimplePath(s);
396} 413}
397 414
398#else // _WIN32 415#ifndef _WIN32
399 416
400bool IsAbsolutePath(const wchar_t *s) throw() { return IS_SEPAR(s[0]); } 417bool IsAbsolutePath(const wchar_t *s) throw() { return IS_SEPAR(s[0]); }
401 418
diff --git a/CPP/Windows/FileName.h b/CPP/Windows/FileName.h
index 219b656..ce26e78 100644
--- a/CPP/Windows/FileName.h
+++ b/CPP/Windows/FileName.h
@@ -25,13 +25,13 @@ bool IsDrivePath(const wchar_t *s) throw(); // first 3 chars are drive chars li
25 25
26bool IsAltPathPrefix(CFSTR s) throw(); /* name: */ 26bool IsAltPathPrefix(CFSTR s) throw(); /* name: */
27 27
28#if defined(_WIN32) && !defined(UNDER_CE)
29
30extern const char * const kSuperPathPrefix; /* \\?\ */ 28extern const char * const kSuperPathPrefix; /* \\?\ */
31const unsigned kDevicePathPrefixSize = 4; 29const unsigned kDevicePathPrefixSize = 4;
32const unsigned kSuperPathPrefixSize = 4; 30const unsigned kSuperPathPrefixSize = 4;
33const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4; 31const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4;
34 32
33#if defined(_WIN32) && !defined(UNDER_CE)
34
35bool IsDevicePath(CFSTR s) throw(); /* \\.\ */ 35bool IsDevicePath(CFSTR s) throw(); /* \\.\ */
36bool IsSuperUncPath(CFSTR s) throw(); /* \\?\UNC\ */ 36bool IsSuperUncPath(CFSTR s) throw(); /* \\?\UNC\ */
37bool IsNetworkPath(CFSTR s) throw(); /* \\?\UNC\ or \\SERVER */ 37bool IsNetworkPath(CFSTR s) throw(); /* \\?\UNC\ or \\SERVER */
@@ -86,6 +86,15 @@ int FindAltStreamColon(CFSTR path) throw();
86bool IsAbsolutePath(const wchar_t *s) throw(); 86bool IsAbsolutePath(const wchar_t *s) throw();
87unsigned GetRootPrefixSize(const wchar_t *s) throw(); 87unsigned GetRootPrefixSize(const wchar_t *s) throw();
88 88
89#ifndef _WIN32
90/* GetRootPrefixSize_WINDOWS() is called in linux, but it parses path by windows rules.
91 It supports only paths system (linux) slash separators (STRING_PATH_SEPARATOR),
92 It doesn't parses paths with backslash (windows) separators.
93 "c:/dir/file" is supported.
94*/
95unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw();
96#endif
97
89#ifdef Z7_LONG_PATH 98#ifdef Z7_LONG_PATH
90 99
91const int kSuperPathType_UseOnlyMain = 0; 100const int kSuperPathType_UseOnlyMain = 0;
diff --git a/CPP/Windows/Registry.cpp b/CPP/Windows/Registry.cpp
index c8b1709..a94a50f 100644
--- a/CPP/Windows/Registry.cpp
+++ b/CPP/Windows/Registry.cpp
@@ -78,7 +78,7 @@ LONG CKey::Close() throw()
78 return res; 78 return res;
79} 79}
80 80
81// win95, win98: deletes sunkey and all its subkeys 81// win95, win98: deletes subkey and all its subkeys
82// winNT to be deleted must not have subkeys 82// winNT to be deleted must not have subkeys
83LONG CKey::DeleteSubKey(LPCTSTR subKeyName) throw() 83LONG CKey::DeleteSubKey(LPCTSTR subKeyName) throw()
84{ 84{
@@ -88,22 +88,36 @@ LONG CKey::DeleteSubKey(LPCTSTR subKeyName) throw()
88 88
89LONG CKey::RecurseDeleteKey(LPCTSTR subKeyName) throw() 89LONG CKey::RecurseDeleteKey(LPCTSTR subKeyName) throw()
90{ 90{
91 CKey key;
92 LONG res = key.Open(_object, subKeyName, KEY_READ | KEY_WRITE);
93 if (res != ERROR_SUCCESS)
94 return res;
95 FILETIME fileTime;
96 const UInt32 kBufSize = MAX_PATH + 1; // 256 in ATL
97 DWORD size = kBufSize;
98 TCHAR buffer[kBufSize];
99 while (RegEnumKeyEx(key._object, 0, buffer, &size, NULL, NULL, NULL, &fileTime) == ERROR_SUCCESS)
100 { 91 {
101 res = key.RecurseDeleteKey(buffer); 92 CKey key;
93 LONG res = key.Open(_object, subKeyName, KEY_READ | KEY_WRITE);
102 if (res != ERROR_SUCCESS) 94 if (res != ERROR_SUCCESS)
103 return res; 95 return res;
104 size = kBufSize; 96 FILETIME fileTime;
97 const UInt32 kBufSize = MAX_PATH + 1; // 256 in ATL
98 TCHAR buffer[kBufSize];
99 // we use loop limit here for some unexpected code failure
100 for (unsigned loop_cnt = 0; loop_cnt < (1u << 26); loop_cnt++)
101 {
102 DWORD size = kBufSize;
103 // we always request starting item (index==0) in each iteration,
104 // because we remove starting item (index==0) in each loop iteration.
105 res = RegEnumKeyEx(key._object, 0, buffer, &size, NULL, NULL, NULL, &fileTime);
106 if (res != ERROR_SUCCESS)
107 {
108 // possible return codes:
109 // ERROR_NO_MORE_ITEMS : are no more subkeys available
110 // ERROR_MORE_DATA : name buffer is too small
111 // we can try to remove (subKeyName), even if there is non ERROR_NO_MORE_ITEMS error.
112 // if (res != ERROR_NO_MORE_ITEMS) return res;
113 break;
114 }
115 res = key.RecurseDeleteKey(buffer);
116 if (res != ERROR_SUCCESS)
117 return res;
118 }
119 // key.Close();
105 } 120 }
106 key.Close();
107 return DeleteSubKey(subKeyName); 121 return DeleteSubKey(subKeyName);
108} 122}
109 123
@@ -127,7 +141,7 @@ LONG CKey::DeleteValue(LPCWSTR name)
127 MY_ASSUME(_object != NULL); 141 MY_ASSUME(_object != NULL);
128 if (g_IsNT) 142 if (g_IsNT)
129 return ::RegDeleteValueW(_object, name); 143 return ::RegDeleteValueW(_object, name);
130 return DeleteValue(name == 0 ? 0 : (LPCSTR)GetSystemString(name)); 144 return DeleteValue(name == NULL ? NULL : (LPCSTR)GetSystemString(name));
131} 145}
132#endif 146#endif
133 147
@@ -143,12 +157,15 @@ LONG CKey::SetValue(LPCTSTR name, bool value) throw()
143 return SetValue(name, BoolToUINT32(value)); 157 return SetValue(name, BoolToUINT32(value));
144} 158}
145 159
160
161// value must be string that is NULL terminated
146LONG CKey::SetValue(LPCTSTR name, LPCTSTR value) throw() 162LONG CKey::SetValue(LPCTSTR name, LPCTSTR value) throw()
147{ 163{
148 MYASSERT(value != NULL); 164 MYASSERT(value != NULL);
149 MY_ASSUME(_object != NULL); 165 MY_ASSUME(_object != NULL);
166 // note: RegSetValueEx supports (value == NULL), if (cbData == 0)
150 return RegSetValueEx(_object, name, 0, REG_SZ, 167 return RegSetValueEx(_object, name, 0, REG_SZ,
151 (const BYTE *)value, ((DWORD)lstrlen(value) + 1) * sizeof(TCHAR)); 168 (const BYTE *)value, (DWORD)(((DWORD)lstrlen(value) + 1) * sizeof(TCHAR)));
152} 169}
153 170
154/* 171/*
@@ -156,7 +173,7 @@ LONG CKey::SetValue(LPCTSTR name, const CSysString &value)
156{ 173{
157 MYASSERT(value != NULL); 174 MYASSERT(value != NULL);
158 MY_ASSUME(_object != NULL); 175 MY_ASSUME(_object != NULL);
159 return RegSetValueEx(_object, name, NULL, REG_SZ, 176 return RegSetValueEx(_object, name, 0, REG_SZ,
160 (const BYTE *)(const TCHAR *)value, (value.Len() + 1) * sizeof(TCHAR)); 177 (const BYTE *)(const TCHAR *)value, (value.Len() + 1) * sizeof(TCHAR));
161} 178}
162*/ 179*/
@@ -169,9 +186,10 @@ LONG CKey::SetValue(LPCWSTR name, LPCWSTR value)
169 MY_ASSUME(_object != NULL); 186 MY_ASSUME(_object != NULL);
170 if (g_IsNT) 187 if (g_IsNT)
171 return RegSetValueExW(_object, name, 0, REG_SZ, 188 return RegSetValueExW(_object, name, 0, REG_SZ,
172 (const BYTE * )value, (DWORD)((wcslen(value) + 1) * sizeof(wchar_t))); 189 (const BYTE *)value, (DWORD)(((DWORD)wcslen(value) + 1) * sizeof(wchar_t)));
173 return SetValue(name == 0 ? 0 : (LPCSTR)GetSystemString(name), 190 return SetValue(name == NULL ? NULL :
174 value == 0 ? 0 : (LPCSTR)GetSystemString(value)); 191 (LPCSTR)GetSystemString(name),
192 (LPCSTR)GetSystemString(value));
175} 193}
176 194
177#endif 195#endif
@@ -205,99 +223,137 @@ LONG CKey::SetKeyValue(LPCTSTR keyName, LPCTSTR valueName, LPCTSTR value) throw(
205 return res; 223 return res;
206} 224}
207 225
208LONG CKey::QueryValue(LPCTSTR name, UInt32 &value) throw()
209{
210 DWORD type = 0;
211 DWORD count = sizeof(DWORD);
212 LONG res = RegQueryValueEx(_object, name, NULL, &type,
213 (LPBYTE)&value, &count);
214 MYASSERT((res != ERROR_SUCCESS) || (type == REG_DWORD));
215 MYASSERT((res != ERROR_SUCCESS) || (count == sizeof(UInt32)));
216 return res;
217}
218 226
219LONG CKey::QueryValue(LPCTSTR name, bool &value) throw() 227LONG CKey::GetValue_UInt32_IfOk(LPCTSTR name, UInt32 &value) throw()
220{ 228{
221 UInt32 uintValue = BoolToUINT32(value); 229 DWORD type = 0;
222 LONG res = QueryValue(name, uintValue); 230 DWORD count = sizeof(value);
223 value = UINT32ToBool(uintValue); 231 UInt32 value2; // = value;
232 const LONG res = QueryValueEx(name, &type, (LPBYTE)&value2, &count);
233 if (res == ERROR_SUCCESS)
234 {
235 // ERROR_UNSUPPORTED_TYPE
236 if (count != sizeof(value) || type != REG_DWORD)
237 return ERROR_UNSUPPORTED_TYPE; // ERROR_INVALID_DATA;
238 value = value2;
239 }
224 return res; 240 return res;
225} 241}
226 242
227LONG CKey::GetValue_IfOk(LPCTSTR name, UInt32 &value) throw() 243LONG CKey::GetValue_UInt64_IfOk(LPCTSTR name, UInt64 &value) throw()
228{ 244{
229 UInt32 newVal; 245 DWORD type = 0;
230 LONG res = QueryValue(name, newVal); 246 DWORD count = sizeof(value);
247 UInt64 value2; // = value;
248 const LONG res = QueryValueEx(name, &type, (LPBYTE)&value2, &count);
231 if (res == ERROR_SUCCESS) 249 if (res == ERROR_SUCCESS)
232 value = newVal; 250 {
251 if (count != sizeof(value) || type != REG_QWORD)
252 return ERROR_UNSUPPORTED_TYPE;
253 value = value2;
254 }
233 return res; 255 return res;
234} 256}
235 257
236LONG CKey::GetValue_IfOk(LPCTSTR name, bool &value) throw() 258LONG CKey::GetValue_bool_IfOk(LPCTSTR name, bool &value) throw()
237{ 259{
238 bool newVal = false; 260 UInt32 uintValue;
239 LONG res = QueryValue(name, newVal); 261 const LONG res = GetValue_UInt32_IfOk(name, uintValue);
240 if (res == ERROR_SUCCESS) 262 if (res == ERROR_SUCCESS)
241 value = newVal; 263 value = UINT32ToBool(uintValue);
242 return res; 264 return res;
243} 265}
244 266
245LONG CKey::QueryValue(LPCTSTR name, LPTSTR value, UInt32 &count) throw() 267
246{
247 DWORD type = 0;
248 LONG res = RegQueryValueEx(_object, name, NULL, &type, (LPBYTE)value, (DWORD *)&count);
249 MYASSERT((res != ERROR_SUCCESS) || (type == REG_SZ) || (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
250 return res;
251}
252 268
253LONG CKey::QueryValue(LPCTSTR name, CSysString &value) 269LONG CKey::QueryValue(LPCTSTR name, CSysString &value)
254{ 270{
255 value.Empty(); 271 value.Empty();
256 DWORD type = 0; 272 LONG res = ERROR_SUCCESS;
257 DWORD curSize = 0; 273 {
258 LONG res = RegQueryValueEx(_object, name, NULL, &type, NULL, &curSize); 274 // if we don't want multiple calls here,
259 if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) 275 // we can use big value (264) here.
260 return res; 276 // 3 is default available length in new string.
261 UInt32 curSize2 = curSize; 277 DWORD size_prev = 3 * sizeof(TCHAR);
262 res = QueryValue(name, value.GetBuf(curSize), curSize2); 278 // at least 2 attempts are required. But we use more attempts for cases,
263 if (curSize > curSize2) 279 // where string can be changed by anothner process
264 curSize = curSize2; 280 for (unsigned i = 0; i < 2 + 2; i++)
265 value.ReleaseBuf_CalcLen(curSize / sizeof(TCHAR)); 281 {
282 DWORD type = 0;
283 DWORD size = size_prev;
284 {
285 LPBYTE buf = (LPBYTE)value.GetBuf(size / sizeof(TCHAR));
286 res = QueryValueEx(name, &type, size == 0 ? NULL : buf, &size);
287 // if (size_prev == 0), then (res == ERROR_SUCCESS) is expected here, because we requested only size.
288 }
289 if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA)
290 {
291 if (type != REG_SZ && type != REG_EXPAND_SZ)
292 {
293 res = ERROR_UNSUPPORTED_TYPE;
294 size = 0;
295 }
296 }
297 else
298 size = 0;
299 if (size > size_prev)
300 {
301 size_prev = size;
302 size = 0;
303 res = ERROR_MORE_DATA;
304 }
305 value.ReleaseBuf_CalcLen(size / sizeof(TCHAR));
306 if (res != ERROR_MORE_DATA)
307 return res;
308 }
309 }
266 return res; 310 return res;
267} 311}
268 312
269 313
270#ifndef _UNICODE 314#ifndef _UNICODE
271 315
272LONG CKey::QueryValue(LPCWSTR name, LPWSTR value, UInt32 &count)
273{
274 DWORD type = 0;
275 LONG res = RegQueryValueExW(_object, name, NULL, &type, (LPBYTE)value, (DWORD *)&count);
276 MYASSERT((res != ERROR_SUCCESS) || (type == REG_SZ) || (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
277 return res;
278}
279
280LONG CKey::QueryValue(LPCWSTR name, UString &value) 316LONG CKey::QueryValue(LPCWSTR name, UString &value)
281{ 317{
282 value.Empty(); 318 value.Empty();
283 DWORD type = 0; 319 LONG res = ERROR_SUCCESS;
284 DWORD curSize = 0;
285 LONG res;
286 if (g_IsNT) 320 if (g_IsNT)
287 { 321 {
288 res = RegQueryValueExW(_object, name, NULL, &type, NULL, &curSize); 322 DWORD size_prev = 3 * sizeof(wchar_t);
289 if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) 323 for (unsigned i = 0; i < 2 + 2; i++)
290 return res; 324 {
291 UInt32 curSize2 = curSize; 325 DWORD type = 0;
292 res = QueryValue(name, value.GetBuf(curSize), curSize2); 326 DWORD size = size_prev;
293 if (curSize > curSize2) 327 {
294 curSize = curSize2; 328 LPBYTE buf = (LPBYTE)value.GetBuf(size / sizeof(wchar_t));
295 value.ReleaseBuf_CalcLen(curSize / sizeof(wchar_t)); 329 res = RegQueryValueExW(_object, name, NULL, &type,
330 size == 0 ? NULL : buf, &size);
331 }
332 if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA)
333 {
334 if (type != REG_SZ && type != REG_EXPAND_SZ)
335 {
336 res = ERROR_UNSUPPORTED_TYPE;
337 size = 0;
338 }
339 }
340 else
341 size = 0;
342 if (size > size_prev)
343 {
344 size_prev = size;
345 size = 0;
346 res = ERROR_MORE_DATA;
347 }
348 value.ReleaseBuf_CalcLen(size / sizeof(wchar_t));
349 if (res != ERROR_MORE_DATA)
350 return res;
351 }
296 } 352 }
297 else 353 else
298 { 354 {
299 AString vTemp; 355 AString vTemp;
300 res = QueryValue(name == 0 ? 0 : (LPCSTR)GetSystemString(name), vTemp); 356 res = QueryValue(name == NULL ? NULL : (LPCSTR)GetSystemString(name), vTemp);
301 value = GetUnicodeString(vTemp); 357 value = GetUnicodeString(vTemp);
302 } 358 }
303 return res; 359 return res;
@@ -306,26 +362,43 @@ LONG CKey::QueryValue(LPCWSTR name, UString &value)
306#endif 362#endif
307 363
308 364
309LONG CKey::QueryValue(LPCTSTR name, void *value, UInt32 &count) throw() 365LONG CKey::QueryValue_Binary(LPCTSTR name, CByteBuffer &value)
310{ 366{
311 DWORD type = 0; 367 // value.Free();
312 LONG res = RegQueryValueEx(_object, name, NULL, &type, (LPBYTE)value, (DWORD *)&count); 368 DWORD size_prev = 0;
313 MYASSERT((res != ERROR_SUCCESS) || (type == REG_BINARY)); 369 LONG res = ERROR_SUCCESS;
370 for (unsigned i = 0; i < 2 + 2; i++)
371 {
372 DWORD type = 0;
373 DWORD size = size_prev;
374 value.Alloc(size_prev);
375 res = QueryValueEx(name, &type, value.NonConstData(), &size);
376 // if (size_prev == 0), then (res == ERROR_SUCCESS) is expected here, because we requested only size.
377 if (res == ERROR_SUCCESS || res == ERROR_MORE_DATA)
378 {
379 if (type != REG_BINARY)
380 {
381 res = ERROR_UNSUPPORTED_TYPE;
382 size = 0;
383 }
384 }
385 else
386 size = 0;
387 if (size > size_prev)
388 {
389 size_prev = size;
390 size = 0;
391 res = ERROR_MORE_DATA;
392 }
393 if (size < value.Size())
394 value.ChangeSize_KeepData(size, size);
395 if (res != ERROR_MORE_DATA)
396 return res;
397 }
314 return res; 398 return res;
315} 399}
316 400
317 401
318LONG CKey::QueryValue(LPCTSTR name, CByteBuffer &value, UInt32 &dataSize)
319{
320 DWORD type = 0;
321 dataSize = 0;
322 LONG res = RegQueryValueEx(_object, name, NULL, &type, NULL, (DWORD *)&dataSize);
323 if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA)
324 return res;
325 value.Alloc(dataSize);
326 return QueryValue(name, (BYTE *)value, dataSize);
327}
328
329LONG CKey::EnumKeys(CSysStringVector &keyNames) 402LONG CKey::EnumKeys(CSysStringVector &keyNames)
330{ 403{
331 keyNames.Clear(); 404 keyNames.Clear();
@@ -334,23 +407,23 @@ LONG CKey::EnumKeys(CSysStringVector &keyNames)
334 { 407 {
335 const unsigned kBufSize = MAX_PATH + 1; // 256 in ATL 408 const unsigned kBufSize = MAX_PATH + 1; // 256 in ATL
336 FILETIME lastWriteTime; 409 FILETIME lastWriteTime;
337 UInt32 nameSize = kBufSize; 410 DWORD nameSize = kBufSize;
338 LONG result = ::RegEnumKeyEx(_object, index, keyName.GetBuf(kBufSize), 411 const LONG res = ::RegEnumKeyEx(_object, index,
339 (DWORD *)&nameSize, NULL, NULL, NULL, &lastWriteTime); 412 keyName.GetBuf(kBufSize), &nameSize,
413 NULL, NULL, NULL, &lastWriteTime);
340 keyName.ReleaseBuf_CalcLen(kBufSize); 414 keyName.ReleaseBuf_CalcLen(kBufSize);
341 if (result == ERROR_NO_MORE_ITEMS) 415 if (res == ERROR_NO_MORE_ITEMS)
342 break; 416 return ERROR_SUCCESS;
343 if (result != ERROR_SUCCESS) 417 if (res != ERROR_SUCCESS)
344 return result; 418 return res;
345 keyNames.Add(keyName); 419 keyNames.Add(keyName);
346 } 420 }
347 return ERROR_SUCCESS;
348} 421}
349 422
423
350LONG CKey::SetValue_Strings(LPCTSTR valueName, const UStringVector &strings) 424LONG CKey::SetValue_Strings(LPCTSTR valueName, const UStringVector &strings)
351{ 425{
352 size_t numChars = 0; 426 size_t numChars = 0;
353
354 unsigned i; 427 unsigned i;
355 428
356 for (i = 0; i < strings.Size(); i++) 429 for (i = 0; i < strings.Size(); i++)
@@ -362,10 +435,11 @@ LONG CKey::SetValue_Strings(LPCTSTR valueName, const UStringVector &strings)
362 for (i = 0; i < strings.Size(); i++) 435 for (i = 0; i < strings.Size(); i++)
363 { 436 {
364 const UString &s = strings[i]; 437 const UString &s = strings[i];
365 size_t size = s.Len() + 1; 438 const size_t size = s.Len() + 1;
366 wmemcpy(buffer + pos, s, size); 439 wmemcpy(buffer + pos, s, size);
367 pos += size; 440 pos += size;
368 } 441 }
442 // if (pos != numChars) return E_FAIL;
369 return SetValue(valueName, buffer, (UInt32)numChars * sizeof(wchar_t)); 443 return SetValue(valueName, buffer, (UInt32)numChars * sizeof(wchar_t));
370} 444}
371 445
@@ -373,20 +447,18 @@ LONG CKey::GetValue_Strings(LPCTSTR valueName, UStringVector &strings)
373{ 447{
374 strings.Clear(); 448 strings.Clear();
375 CByteBuffer buffer; 449 CByteBuffer buffer;
376 UInt32 dataSize = 0; 450 const LONG res = QueryValue_Binary(valueName, buffer);
377 const LONG res = QueryValue(valueName, buffer, dataSize);
378 if (res != ERROR_SUCCESS) 451 if (res != ERROR_SUCCESS)
379 return res; 452 return res;
380 if (dataSize > buffer.Size()) 453 const size_t dataSize = buffer.Size();
381 return E_FAIL; 454 if (dataSize % sizeof(wchar_t))
382 if (dataSize % sizeof(wchar_t) != 0) 455 return ERROR_INVALID_DATA;
383 return E_FAIL;
384
385 const wchar_t *data = (const wchar_t *)(const void *)(const Byte *)buffer; 456 const wchar_t *data = (const wchar_t *)(const void *)(const Byte *)buffer;
386 const size_t numChars = dataSize / sizeof(wchar_t); 457 const size_t numChars = dataSize / sizeof(wchar_t);
458 // we can check that all names are finished
459 // if (numChars != 0 && data[numChars - 1] != 0) return ERROR_INVALID_DATA;
387 size_t prev = 0; 460 size_t prev = 0;
388 UString s; 461 UString s;
389
390 for (size_t i = 0; i < numChars; i++) 462 for (size_t i = 0; i < numChars; i++)
391 { 463 {
392 if (data[i] == 0) 464 if (data[i] == 0)
@@ -396,7 +468,6 @@ LONG CKey::GetValue_Strings(LPCTSTR valueName, UStringVector &strings)
396 prev = i + 1; 468 prev = i + 1;
397 } 469 }
398 } 470 }
399
400 return res; 471 return res;
401} 472}
402 473
diff --git a/CPP/Windows/Registry.h b/CPP/Windows/Registry.h
index 0d3b4fc..74ee919 100644
--- a/CPP/Windows/Registry.h
+++ b/CPP/Windows/Registry.h
@@ -14,6 +14,13 @@ LONG SetValue(HKEY parentKey, LPCTSTR keyName, LPCTSTR valueName, LPCTSTR value)
14class CKey 14class CKey
15{ 15{
16 HKEY _object; 16 HKEY _object;
17
18 LONG QueryValueEx(LPCTSTR lpValueName, LPDWORD lpType,
19 LPBYTE lpData, LPDWORD lpcbData)
20 {
21 return RegQueryValueEx(_object, lpValueName, NULL, lpType, lpData, lpcbData);
22 }
23
17public: 24public:
18 CKey(): _object(NULL) {} 25 CKey(): _object(NULL) {}
19 ~CKey() { Close(); } 26 ~CKey() { Close(); }
@@ -22,13 +29,14 @@ public:
22 void Attach(HKEY key) { _object = key; } 29 void Attach(HKEY key) { _object = key; }
23 HKEY Detach() 30 HKEY Detach()
24 { 31 {
25 HKEY key = _object; 32 const HKEY key = _object;
26 _object = NULL; 33 _object = NULL;
27 return key; 34 return key;
28 } 35 }
29 36
30 LONG Create(HKEY parentKey, LPCTSTR keyName, 37 LONG Create(HKEY parentKey, LPCTSTR keyName,
31 LPTSTR keyClass = REG_NONE, DWORD options = REG_OPTION_NON_VOLATILE, 38 LPTSTR keyClass = REG_NONE,
39 DWORD options = REG_OPTION_NON_VOLATILE,
32 REGSAM accessMask = KEY_ALL_ACCESS, 40 REGSAM accessMask = KEY_ALL_ACCESS,
33 LPSECURITY_ATTRIBUTES securityAttributes = NULL, 41 LPSECURITY_ATTRIBUTES securityAttributes = NULL,
34 LPDWORD disposition = NULL) throw(); 42 LPDWORD disposition = NULL) throw();
@@ -40,18 +48,18 @@ public:
40 LONG RecurseDeleteKey(LPCTSTR subKeyName) throw(); 48 LONG RecurseDeleteKey(LPCTSTR subKeyName) throw();
41 49
42 LONG DeleteValue(LPCTSTR name) throw(); 50 LONG DeleteValue(LPCTSTR name) throw();
43 #ifndef _UNICODE 51#ifndef _UNICODE
44 LONG DeleteValue(LPCWSTR name); 52 LONG DeleteValue(LPCWSTR name);
45 #endif 53#endif
46 54
47 LONG SetValue(LPCTSTR valueName, UInt32 value) throw(); 55 LONG SetValue(LPCTSTR valueName, UInt32 value) throw();
48 LONG SetValue(LPCTSTR valueName, bool value) throw(); 56 LONG SetValue(LPCTSTR valueName, bool value) throw();
49 LONG SetValue(LPCTSTR valueName, LPCTSTR value) throw(); 57 LONG SetValue(LPCTSTR valueName, LPCTSTR value) throw();
50 // LONG SetValue(LPCTSTR valueName, const CSysString &value); 58 // LONG SetValue(LPCTSTR valueName, const CSysString &value);
51 #ifndef _UNICODE 59#ifndef _UNICODE
52 LONG SetValue(LPCWSTR name, LPCWSTR value); 60 LONG SetValue(LPCWSTR name, LPCWSTR value);
53 // LONG SetValue(LPCWSTR name, const UString &value); 61 // LONG SetValue(LPCWSTR name, const UString &value);
54 #endif 62#endif
55 63
56 LONG SetValue(LPCTSTR name, const void *value, UInt32 size) throw(); 64 LONG SetValue(LPCTSTR name, const void *value, UInt32 size) throw();
57 65
@@ -60,21 +68,25 @@ public:
60 68
61 LONG SetKeyValue(LPCTSTR keyName, LPCTSTR valueName, LPCTSTR value) throw(); 69 LONG SetKeyValue(LPCTSTR keyName, LPCTSTR valueName, LPCTSTR value) throw();
62 70
63 LONG QueryValue(LPCTSTR name, UInt32 &value) throw(); 71 // GetValue_[type]_IfOk():
64 LONG QueryValue(LPCTSTR name, bool &value) throw(); 72 // if (return_result == ERROR_SUCCESS), (value) variable was read from registry
65 LONG QueryValue(LPCTSTR name, LPTSTR value, UInt32 &dataSize) throw(); 73 // if (return_result != ERROR_SUCCESS), (value) variable was not changed
66 LONG QueryValue(LPCTSTR name, CSysString &value); 74 LONG GetValue_UInt32_IfOk(LPCTSTR name, UInt32 &value) throw();
67 75 LONG GetValue_UInt64_IfOk(LPCTSTR name, UInt64 &value) throw();
68 LONG GetValue_IfOk(LPCTSTR name, UInt32 &value) throw(); 76 LONG GetValue_bool_IfOk(LPCTSTR name, bool &value) throw();
69 LONG GetValue_IfOk(LPCTSTR name, bool &value) throw();
70 77
71 #ifndef _UNICODE 78 // QueryValue():
72 LONG QueryValue(LPCWSTR name, LPWSTR value, UInt32 &dataSize); 79 // if (return_result == ERROR_SUCCESS), (value) string was read from registry
80 // if (return_result != ERROR_SUCCESS), (value) string was cleared
81 LONG QueryValue(LPCTSTR name, CSysString &value);
82#ifndef _UNICODE
73 LONG QueryValue(LPCWSTR name, UString &value); 83 LONG QueryValue(LPCWSTR name, UString &value);
74 #endif 84#endif
75 85
76 LONG QueryValue(LPCTSTR name, void *value, UInt32 &dataSize) throw(); 86 // QueryValue_Binary():
77 LONG QueryValue(LPCTSTR name, CByteBuffer &value, UInt32 &dataSize); 87 // if (return_result == ERROR_SUCCESS), (value) buffer was read from registry (BINARY data)
88 // if (return_result != ERROR_SUCCESS), (value) buffer was cleared
89 LONG QueryValue_Binary(LPCTSTR name, CByteBuffer &value);
78 90
79 LONG EnumKeys(CSysStringVector &keyNames); 91 LONG EnumKeys(CSysStringVector &keyNames);
80}; 92};
diff --git a/CPP/Windows/System.cpp b/CPP/Windows/System.cpp
index 03c8988..4745785 100644
--- a/CPP/Windows/System.cpp
+++ b/CPP/Windows/System.cpp
@@ -25,6 +25,69 @@ namespace NSystem {
25 25
26#ifdef _WIN32 26#ifdef _WIN32
27 27
28/*
29note: returned value in 32-bit version can be limited by value 32.
30 while 64-bit version returns full value.
31GetMaximumProcessorCount(groupNumber) can return higher value than
32GetActiveProcessorCount(groupNumber) in some cases, because CPUs can be added.
33*/
34// typedef DWORD (WINAPI *Func_GetMaximumProcessorCount)(WORD GroupNumber);
35typedef DWORD (WINAPI *Func_GetActiveProcessorCount)(WORD GroupNumber);
36typedef WORD (WINAPI *Func_GetActiveProcessorGroupCount)(VOID);
37/*
38#if 0 && defined(ALL_PROCESSOR_GROUPS)
39#define MY_ALL_PROCESSOR_GROUPS ALL_PROCESSOR_GROUPS
40#else
41#define MY_ALL_PROCESSOR_GROUPS 0xffff
42#endif
43*/
44
45Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
46
47bool CCpuGroups::Load()
48{
49 NumThreadsTotal = 0;
50 GroupSizes.Clear();
51 const HMODULE hmodule = ::GetModuleHandleA("kernel32.dll");
52 // Is_Win11_Groups = GetProcAddress(hmodule, "SetThreadSelectedCpuSetMasks") != NULL;
53 const
54 Func_GetActiveProcessorGroupCount
55 fn_GetActiveProcessorGroupCount = Z7_GET_PROC_ADDRESS(
56 Func_GetActiveProcessorGroupCount, hmodule,
57 "GetActiveProcessorGroupCount");
58 const
59 Func_GetActiveProcessorCount
60 fn_GetActiveProcessorCount = Z7_GET_PROC_ADDRESS(
61 Func_GetActiveProcessorCount, hmodule,
62 "GetActiveProcessorCount");
63 if (!fn_GetActiveProcessorGroupCount ||
64 !fn_GetActiveProcessorCount)
65 return false;
66
67 const unsigned numGroups = fn_GetActiveProcessorGroupCount();
68 if (numGroups == 0)
69 return false;
70 UInt32 sum = 0;
71 for (unsigned i = 0; i < numGroups; i++)
72 {
73 const UInt32 num = fn_GetActiveProcessorCount((WORD)i);
74 /*
75 if (num == 0)
76 {
77 // it means error
78 // but is it possible that some group is empty by some reason?
79 // GroupSizes.Clear();
80 // return false;
81 }
82 */
83 sum += num;
84 GroupSizes.Add(num);
85 }
86 NumThreadsTotal = sum;
87 // NumThreadsTotal = fn_GetActiveProcessorCount(MY_ALL_PROCESSOR_GROUPS);
88 return true;
89}
90
28UInt32 CountAffinity(DWORD_PTR mask) 91UInt32 CountAffinity(DWORD_PTR mask)
29{ 92{
30 UInt32 num = 0; 93 UInt32 num = 0;
@@ -38,31 +101,62 @@ UInt32 CountAffinity(DWORD_PTR mask)
38 101
39BOOL CProcessAffinity::Get() 102BOOL CProcessAffinity::Get()
40{ 103{
41 #ifndef UNDER_CE 104 IsGroupMode = false;
42 return GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask); 105 Groups.Load();
43 #else 106 // SetThreadAffinityMask(GetCurrentThread(), 1);
44 return FALSE; 107 // SetProcessAffinityMask(GetCurrentProcess(), 1);
45 #endif 108 BOOL res = GetProcessAffinityMask(GetCurrentProcess(),
109 &processAffinityMask, &systemAffinityMask);
110 /* DOCs: On a system with more than 64 processors, if the threads
111 of the calling process are in a single processor group, the
112 function sets the variables pointed to by lpProcessAffinityMask
113 and lpSystemAffinityMask to the process affinity mask and the
114 processor mask of active logical processors for that group.
115 If the calling process contains threads in multiple groups,
116 the function returns zero for both affinity masks
117
118 note: tested in Win10: GetProcessAffinityMask() doesn't return 0
119 in (processAffinityMask) and (systemAffinityMask) masks.
120 We need to test it in Win11: how to get mask==0 from GetProcessAffinityMask()?
121 */
122 if (!res)
123 {
124 processAffinityMask = 0;
125 systemAffinityMask = 0;
126 }
127 if (Groups.GroupSizes.Size() > 1 && Groups.NumThreadsTotal)
128 if (// !res ||
129 processAffinityMask == 0 || // to support case described in DOCs and for (!res) case
130 processAffinityMask == systemAffinityMask) // for default nonchanged affinity
131 {
132 // we set IsGroupMode only if processAffinity is default (not changed).
133 res = TRUE;
134 IsGroupMode = true;
135 }
136 return res;
46} 137}
47 138
48 139
140UInt32 CProcessAffinity::Load_and_GetNumberOfThreads()
141{
142 if (Get())
143 {
144 const UInt32 numProcessors = GetNumProcessThreads();
145 if (numProcessors)
146 return numProcessors;
147 }
148 SYSTEM_INFO systemInfo;
149 GetSystemInfo(&systemInfo);
150 // the number of logical processors in the current group
151 return systemInfo.dwNumberOfProcessors;
152}
153
49UInt32 GetNumberOfProcessors() 154UInt32 GetNumberOfProcessors()
50{ 155{
51 // We need to know how many threads we can use. 156 // We need to know how many threads we can use.
52 // By default the process is assigned to one group. 157 // By default the process is assigned to one group.
53 // So we get the number of logical processors (threads)
54 // assigned to current process in the current group.
55 // Group size can be smaller than total number logical processors, for exammple, 2x36
56
57 CProcessAffinity pa; 158 CProcessAffinity pa;
58 159 return pa.Load_and_GetNumberOfThreads();
59 if (pa.Get() && pa.processAffinityMask != 0)
60 return pa.GetNumProcessThreads();
61
62 SYSTEM_INFO systemInfo;
63 GetSystemInfo(&systemInfo);
64 // the number of logical processors in the current group
65 return (UInt32)systemInfo.dwNumberOfProcessors;
66} 160}
67 161
68#else 162#else
@@ -142,9 +236,9 @@ typedef BOOL (WINAPI *Func_GlobalMemoryStatusEx)(MY_LPMEMORYSTATUSEX lpBuffer);
142#endif // !UNDER_CE 236#endif // !UNDER_CE
143 237
144 238
145bool GetRamSize(UInt64 &size) 239bool GetRamSize(size_t &size)
146{ 240{
147 size = (UInt64)(sizeof(size_t)) << 29; 241 size = (size_t)sizeof(size_t) << 29;
148 242
149 #ifndef UNDER_CE 243 #ifndef UNDER_CE
150 MY_MEMORYSTATUSEX stat; 244 MY_MEMORYSTATUSEX stat;
@@ -167,11 +261,23 @@ bool GetRamSize(UInt64 &size)
167 "GlobalMemoryStatusEx"); 261 "GlobalMemoryStatusEx");
168 if (fn && fn(&stat)) 262 if (fn && fn(&stat))
169 { 263 {
170 size = MyMin(stat.ullTotalVirtual, stat.ullTotalPhys); 264 // (MY_MEMORYSTATUSEX::ullTotalVirtual) < 4 GiB in 32-bit mode
265 size_t size2 = (size_t)0 - 1;
266 if (size2 > stat.ullTotalPhys)
267 size2 = (size_t)stat.ullTotalPhys;
268 if (size2 > stat.ullTotalVirtual)
269 size2 = (size_t)stat.ullTotalVirtual;
270 size = size2;
171 return true; 271 return true;
172 } 272 }
173 #endif 273 #endif
174 274
275 // On computers with more than 4 GB of memory:
276 // new docs : GlobalMemoryStatus can report (-1) value to indicate an overflow.
277 // some old docs : GlobalMemoryStatus can report (modulo 4 GiB) value.
278 // (for example, if 5 GB total memory, it could report 1 GB).
279 // We don't want to get (modulo 4 GiB) value.
280 // So we use GlobalMemoryStatusEx() instead.
175 { 281 {
176 MEMORYSTATUS stat2; 282 MEMORYSTATUS stat2;
177 stat2.dwLength = sizeof(stat2); 283 stat2.dwLength = sizeof(stat2);
@@ -187,9 +293,11 @@ bool GetRamSize(UInt64 &size)
187// POSIX 293// POSIX
188// #include <stdio.h> 294// #include <stdio.h>
189 295
190bool GetRamSize(UInt64 &size) 296bool GetRamSize(size_t &size)
191{ 297{
192 size = (UInt64)(sizeof(size_t)) << 29; 298 UInt64 size64;
299 size = (size_t)sizeof(size_t) << 29;
300 size64 = size;
193 301
194#if defined(__APPLE__) || defined(__DragonFly__) || \ 302#if defined(__APPLE__) || defined(__DragonFly__) || \
195 defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 303 defined(BSD) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
@@ -215,7 +323,7 @@ bool GetRamSize(UInt64 &size)
215 // we use strict check (size_sys == sizeof(val)) for returned value 323 // we use strict check (size_sys == sizeof(val)) for returned value
216 // because big-endian encoding is possible: 324 // because big-endian encoding is possible:
217 if (res == 0 && size_sys == sizeof(val) && val) 325 if (res == 0 && size_sys == sizeof(val) && val)
218 size = val; 326 size64 = val;
219 else 327 else
220 { 328 {
221 uint32_t val32 = 0; 329 uint32_t val32 = 0;
@@ -223,12 +331,12 @@ bool GetRamSize(UInt64 &size)
223 res = sysctl(mib, 2, &val32, &size_sys, NULL, 0); 331 res = sysctl(mib, 2, &val32, &size_sys, NULL, 0);
224 // printf("\n sysctl res=%d val=%llx size_sys = %d, %d\n", res, (long long int)val32, (int)size_sys, errno); 332 // printf("\n sysctl res=%d val=%llx size_sys = %d, %d\n", res, (long long int)val32, (int)size_sys, errno);
225 if (res == 0 && size_sys == sizeof(val32) && val32) 333 if (res == 0 && size_sys == sizeof(val32) && val32)
226 size = val32; 334 size64 = val32;
227 } 335 }
228 336
229 #elif defined(_AIX) 337 #elif defined(_AIX)
230 #if defined(_SC_AIX_REALMEM) // AIX 338 #if defined(_SC_AIX_REALMEM) // AIX
231 size = (UInt64)sysconf(_SC_AIX_REALMEM) * 1024; 339 size64 = (UInt64)sysconf(_SC_AIX_REALMEM) * 1024;
232 #endif 340 #endif
233 #elif 0 || defined(__sun) 341 #elif 0 || defined(__sun)
234 #if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) 342 #if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
@@ -240,7 +348,7 @@ bool GetRamSize(UInt64 &size)
240 // printf("\n_SC_PHYS_PAGES (hex) = %lx", (unsigned long)phys_pages); 348 // printf("\n_SC_PHYS_PAGES (hex) = %lx", (unsigned long)phys_pages);
241 // printf("\n_SC_PAGESIZE = %lu\n", (unsigned long)page_size); 349 // printf("\n_SC_PAGESIZE = %lu\n", (unsigned long)page_size);
242 if (phys_pages != -1 && page_size != -1) 350 if (phys_pages != -1 && page_size != -1)
243 size = (UInt64)(Int64)phys_pages * (UInt64)(Int64)page_size; 351 size64 = (UInt64)(Int64)phys_pages * (UInt64)(Int64)page_size;
244 } 352 }
245 #endif 353 #endif
246 #elif defined(__gnu_hurd__) 354 #elif defined(__gnu_hurd__)
@@ -253,7 +361,7 @@ bool GetRamSize(UInt64 &size)
253 struct sysinfo info; 361 struct sysinfo info;
254 if (::sysinfo(&info) != 0) 362 if (::sysinfo(&info) != 0)
255 return false; 363 return false;
256 size = (UInt64)info.mem_unit * info.totalram; 364 size64 = (UInt64)info.mem_unit * info.totalram;
257 /* 365 /*
258 printf("\n mem_unit = %lld", (UInt64)info.mem_unit); 366 printf("\n mem_unit = %lld", (UInt64)info.mem_unit);
259 printf("\n totalram = %lld", (UInt64)info.totalram); 367 printf("\n totalram = %lld", (UInt64)info.totalram);
@@ -262,10 +370,9 @@ bool GetRamSize(UInt64 &size)
262 370
263 #endif 371 #endif
264 372
265 const UInt64 kLimit = (UInt64)1 << (sizeof(size_t) * 8 - 1); 373 size = (size_t)1 << (sizeof(size_t) * 8 - 1);
266 if (size > kLimit) 374 if (size > size64)
267 size = kLimit; 375 size = (size_t)size64;
268
269 return true; 376 return true;
270} 377}
271 378
diff --git a/CPP/Windows/System.h b/CPP/Windows/System.h
index b17111c..0c80373 100644
--- a/CPP/Windows/System.h
+++ b/CPP/Windows/System.h
@@ -9,6 +9,7 @@
9#endif 9#endif
10 10
11#include "../Common/MyTypes.h" 11#include "../Common/MyTypes.h"
12#include "../Common/MyVector.h"
12#include "../Common/MyWindows.h" 13#include "../Common/MyWindows.h"
13 14
14namespace NWindows { 15namespace NWindows {
@@ -16,6 +17,34 @@ namespace NSystem {
16 17
17#ifdef _WIN32 18#ifdef _WIN32
18 19
20struct CCpuGroups
21{
22 CRecordVector<UInt32> GroupSizes;
23 UInt32 NumThreadsTotal; // sum of threads in all groups
24 // bool Is_Win11_Groups; // useless
25
26 void Get_GroupSize_Min_Max(UInt32 &minSize, UInt32 &maxSize) const
27 {
28 unsigned num = GroupSizes.Size();
29 UInt32 minSize2 = 0, maxSize2 = 0;
30 if (num)
31 {
32 minSize2 = (UInt32)0 - 1;
33 do
34 {
35 const UInt32 v = GroupSizes[--num];
36 if (minSize2 > v) minSize2 = v;
37 if (maxSize2 < v) maxSize2 = v;
38 }
39 while (num);
40 }
41 minSize = minSize2;
42 maxSize = maxSize2;
43 }
44 bool Load();
45 CCpuGroups(): NumThreadsTotal(0) {}
46};
47
19UInt32 CountAffinity(DWORD_PTR mask); 48UInt32 CountAffinity(DWORD_PTR mask);
20 49
21struct CProcessAffinity 50struct CProcessAffinity
@@ -25,14 +54,28 @@ struct CProcessAffinity
25 DWORD_PTR processAffinityMask; 54 DWORD_PTR processAffinityMask;
26 DWORD_PTR systemAffinityMask; 55 DWORD_PTR systemAffinityMask;
27 56
57 CCpuGroups Groups;
58 bool IsGroupMode;
59 /*
60 IsGroupMode == true, if
61 Groups.GroupSizes.Size() > 1) && { dafalt affinity was not changed }
62 IsGroupMode == false, if single group or affinity was changed
63 */
64
65 UInt32 Load_and_GetNumberOfThreads();
66
28 void InitST() 67 void InitST()
29 { 68 {
30 // numProcessThreads = 1; 69 // numProcessThreads = 1;
31 // numSysThreads = 1; 70 // numSysThreads = 1;
32 processAffinityMask = 1; 71 processAffinityMask = 1;
33 systemAffinityMask = 1; 72 systemAffinityMask = 1;
73 IsGroupMode = false;
74 // Groups.NumThreadsTotal = 0;
75 // Groups.Is_Win11_Groups = false;
34 } 76 }
35 77
78/*
36 void CpuZero() 79 void CpuZero()
37 { 80 {
38 processAffinityMask = 0; 81 processAffinityMask = 0;
@@ -42,9 +85,23 @@ struct CProcessAffinity
42 { 85 {
43 processAffinityMask |= ((DWORD_PTR)1 << cpuIndex); 86 processAffinityMask |= ((DWORD_PTR)1 << cpuIndex);
44 } 87 }
88*/
45 89
46 UInt32 GetNumProcessThreads() const { return CountAffinity(processAffinityMask); } 90 UInt32 GetNumProcessThreads() const
47 UInt32 GetNumSystemThreads() const { return CountAffinity(systemAffinityMask); } 91 {
92 if (IsGroupMode)
93 return Groups.NumThreadsTotal;
94 // IsGroupMode == false
95 // so we don't want to use groups
96 // we return number of threads in default primary group:
97 return CountAffinity(processAffinityMask);
98 }
99 UInt32 GetNumSystemThreads() const
100 {
101 if (Groups.GroupSizes.Size() > 1 && Groups.NumThreadsTotal)
102 return Groups.NumThreadsTotal;
103 return CountAffinity(systemAffinityMask);
104 }
48 105
49 BOOL Get(); 106 BOOL Get();
50 107
@@ -122,7 +179,7 @@ struct CProcessAffinity
122 179
123UInt32 GetNumberOfProcessors(); 180UInt32 GetNumberOfProcessors();
124 181
125bool GetRamSize(UInt64 &size); // returns false, if unknown ram size 182bool GetRamSize(size_t &size); // returns false, if unknown ram size
126 183
127unsigned long Get_File_OPEN_MAX(); 184unsigned long Get_File_OPEN_MAX();
128unsigned Get_File_OPEN_MAX_Reduced_for_3_tasks(); 185unsigned Get_File_OPEN_MAX_Reduced_for_3_tasks();
diff --git a/CPP/Windows/SystemInfo.cpp b/CPP/Windows/SystemInfo.cpp
index cfc6a90..35846e0 100644
--- a/CPP/Windows/SystemInfo.cpp
+++ b/CPP/Windows/SystemInfo.cpp
@@ -530,6 +530,28 @@ struct CCpuName
530 AString Microcode; 530 AString Microcode;
531 AString LargePages; 531 AString LargePages;
532 532
533#ifdef _WIN32
534 UInt32 MHz;
535
536#ifdef MY_CPU_ARM64
537#define Z7_SYS_INFO_SHOW_ARM64_REGS
538#endif
539#ifdef Z7_SYS_INFO_SHOW_ARM64_REGS
540 bool Arm64_ISAR0_EL1_Defined;
541 UInt64 Arm64_ISAR0_EL1;
542#endif
543#endif
544
545#ifdef _WIN32
546 CCpuName():
547 MHz(0)
548#ifdef Z7_SYS_INFO_SHOW_ARM64_REGS
549 , Arm64_ISAR0_EL1_Defined(false)
550 , Arm64_ISAR0_EL1(0)
551#endif
552 {}
553#endif
554
533 void Fill(); 555 void Fill();
534 556
535 void Get_Revision_Microcode_LargePages(AString &s) 557 void Get_Revision_Microcode_LargePages(AString &s)
@@ -537,16 +559,46 @@ struct CCpuName
537 s.Empty(); 559 s.Empty();
538 AddBracedString(s, Revision); 560 AddBracedString(s, Revision);
539 AddBracedString(s, Microcode); 561 AddBracedString(s, Microcode);
540 s.Add_OptSpaced(LargePages); 562#ifdef _WIN32
563 if (MHz != 0)
564 {
565 s.Add_Space_if_NotEmpty();
566 s.Add_UInt32(MHz);
567 s += " MHz";
568 }
569#endif
570 if (!LargePages.IsEmpty())
571 s.Add_OptSpaced(LargePages);
572 }
573
574#ifdef Z7_SYS_INFO_SHOW_ARM64_REGS
575 void Get_Registers(AString &s)
576 {
577 if (Arm64_ISAR0_EL1_Defined)
578 {
579 // ID_AA64ISAR0_EL1
580 s.Add_OptSpaced("cp4030:");
581 PrintHex(s, Arm64_ISAR0_EL1);
582 {
583 const unsigned sha2 = ((unsigned)(Arm64_ISAR0_EL1 >> 12) & 0xf) - 1;
584 if (sha2 < 2)
585 {
586 s += ":SHA256";
587 if (sha2)
588 s += ":SHA512";
589 }
590 }
591 }
541 } 592 }
593#endif
542}; 594};
543 595
544void CCpuName::Fill() 596void CCpuName::Fill()
545{ 597{
546 CpuName.Empty(); 598 // CpuName.Empty();
547 Revision.Empty(); 599 // Revision.Empty();
548 Microcode.Empty(); 600 // Microcode.Empty();
549 LargePages.Empty(); 601 // LargePages.Empty();
550 602
551 AString &s = CpuName; 603 AString &s = CpuName;
552 604
@@ -600,21 +652,32 @@ void CCpuName::Fill()
600 Revision += GetAnsiString(name); 652 Revision += GetAnsiString(name);
601 } 653 }
602 } 654 }
655#ifdef _WIN32
656 key.GetValue_UInt32_IfOk(TEXT("~MHz"), MHz);
657#ifdef Z7_SYS_INFO_SHOW_ARM64_REGS
658/*
659mapping arm64 registers to Windows registry:
660CP 4000: MIDR_EL1
661CP 4020: ID_AA64PFR0_EL1
662CP 4021: ID_AA64PFR1_EL1
663CP 4028: ID_AA64DFR0_EL1
664CP 4029: ID_AA64DFR1_EL1
665CP 402C: ID_AA64AFR0_EL1
666CP 402D: ID_AA64AFR1_EL1
667CP 4030: ID_AA64ISAR0_EL1
668CP 4031: ID_AA64ISAR1_EL1
669CP 4038: ID_AA64MMFR0_EL1
670CP 4039: ID_AA64MMFR1_EL1
671CP 403A: ID_AA64MMFR2_EL1
672*/
673 if (key.GetValue_UInt64_IfOk(TEXT("CP 4030"), Arm64_ISAR0_EL1) == ERROR_SUCCESS)
674 Arm64_ISAR0_EL1_Defined = true;
675#endif
676#endif
603 LONG res[2]; 677 LONG res[2];
604 CByteBuffer bufs[2]; 678 CByteBuffer bufs[2];
605 { 679 res[0] = key.QueryValue_Binary(TEXT("Previous Update Revision"), bufs[0]);
606 for (unsigned i = 0; i < 2; i++) 680 res[1] = key.QueryValue_Binary(TEXT("Update Revision"), bufs[1]);
607 {
608 UInt32 size = 0;
609 res[i] = key.QueryValue(i == 0 ?
610 TEXT("Previous Update Revision") :
611 TEXT("Update Revision"),
612 bufs[i], size);
613 if (res[i] == ERROR_SUCCESS)
614 if (size != bufs[i].Size())
615 res[i] = ERROR_SUCCESS + 1;
616 }
617 }
618 if (res[0] == ERROR_SUCCESS || res[1] == ERROR_SUCCESS) 681 if (res[0] == ERROR_SUCCESS || res[1] == ERROR_SUCCESS)
619 { 682 {
620 for (unsigned i = 0; i < 2; i++) 683 for (unsigned i = 0; i < 2; i++)
@@ -747,9 +810,18 @@ void AddCpuFeatures(AString &s)
747 unsigned long h = MY_getauxval(AT_HWCAP); 810 unsigned long h = MY_getauxval(AT_HWCAP);
748 PrintHex(s, h); 811 PrintHex(s, h);
749 #ifdef MY_CPU_ARM64 812 #ifdef MY_CPU_ARM64
813#ifndef HWCAP_SHA3
814#define HWCAP_SHA3 (1 << 17)
815#endif
816#ifndef HWCAP_SHA512
817#define HWCAP_SHA512 (1 << 21)
818// #pragma message("=== HWCAP_SHA512 define === ")
819#endif
750 if (h & HWCAP_CRC32) s += ":CRC32"; 820 if (h & HWCAP_CRC32) s += ":CRC32";
751 if (h & HWCAP_SHA1) s += ":SHA1"; 821 if (h & HWCAP_SHA1) s += ":SHA1";
752 if (h & HWCAP_SHA2) s += ":SHA2"; 822 if (h & HWCAP_SHA2) s += ":SHA2";
823 if (h & HWCAP_SHA3) s += ":SHA3";
824 if (h & HWCAP_SHA512) s += ":SHA512";
753 if (h & HWCAP_AES) s += ":AES"; 825 if (h & HWCAP_AES) s += ":AES";
754 if (h & HWCAP_ASIMD) s += ":ASIMD"; 826 if (h & HWCAP_ASIMD) s += ":ASIMD";
755 #elif defined(MY_CPU_ARM) 827 #elif defined(MY_CPU_ARM)
@@ -908,13 +980,18 @@ void GetSystemInfoText(AString &sRes)
908 } 980 }
909 } 981 }
910 { 982 {
911 AString s; 983 AString s, registers;
912 GetCpuName_MultiLine(s); 984 GetCpuName_MultiLine(s, registers);
913 if (!s.IsEmpty()) 985 if (!s.IsEmpty())
914 { 986 {
915 sRes += s; 987 sRes += s;
916 sRes.Add_LF(); 988 sRes.Add_LF();
917 } 989 }
990 if (!registers.IsEmpty())
991 {
992 sRes += registers;
993 sRes.Add_LF();
994 }
918 } 995 }
919 /* 996 /*
920 #ifdef MY_CPU_X86_OR_AMD64 997 #ifdef MY_CPU_X86_OR_AMD64
@@ -932,8 +1009,8 @@ void GetSystemInfoText(AString &sRes)
932} 1009}
933 1010
934 1011
935void GetCpuName_MultiLine(AString &s); 1012void GetCpuName_MultiLine(AString &s, AString &registers);
936void GetCpuName_MultiLine(AString &s) 1013void GetCpuName_MultiLine(AString &s, AString &registers)
937{ 1014{
938 CCpuName cpuName; 1015 CCpuName cpuName;
939 cpuName.Fill(); 1016 cpuName.Fill();
@@ -945,6 +1022,10 @@ void GetCpuName_MultiLine(AString &s)
945 s.Add_LF(); 1022 s.Add_LF();
946 s += s2; 1023 s += s2;
947 } 1024 }
1025 registers.Empty();
1026#ifdef Z7_SYS_INFO_SHOW_ARM64_REGS
1027 cpuName.Get_Registers(registers);
1028#endif
948} 1029}
949 1030
950 1031
diff --git a/CPP/Windows/SystemInfo.h b/CPP/Windows/SystemInfo.h
index c2e2e3b..4601685 100644
--- a/CPP/Windows/SystemInfo.h
+++ b/CPP/Windows/SystemInfo.h
@@ -6,7 +6,7 @@
6#include "../Common/MyString.h" 6#include "../Common/MyString.h"
7 7
8 8
9void GetCpuName_MultiLine(AString &s); 9void GetCpuName_MultiLine(AString &s, AString &registers);
10 10
11void GetOsInfoText(AString &sRes); 11void GetOsInfoText(AString &sRes);
12void GetSystemInfoText(AString &s); 12void GetSystemInfoText(AString &s);
diff --git a/CPP/Windows/Thread.h b/CPP/Windows/Thread.h
index d72f64c..75c1616 100644
--- a/CPP/Windows/Thread.h
+++ b/CPP/Windows/Thread.h
@@ -26,8 +26,10 @@ public:
26 { return Thread_Create_With_Affinity(&thread, startAddress, param, affinity); } 26 { return Thread_Create_With_Affinity(&thread, startAddress, param, affinity); }
27 WRes Create_With_CpuSet(THREAD_FUNC_TYPE startAddress, LPVOID param, const CCpuSet *cpuSet) 27 WRes Create_With_CpuSet(THREAD_FUNC_TYPE startAddress, LPVOID param, const CCpuSet *cpuSet)
28 { return Thread_Create_With_CpuSet(&thread, startAddress, param, cpuSet); } 28 { return Thread_Create_With_CpuSet(&thread, startAddress, param, cpuSet); }
29 29
30 #ifdef _WIN32 30#ifdef _WIN32
31 WRes Create_With_Group(THREAD_FUNC_TYPE startAddress, LPVOID param, unsigned group, CAffinityMask affinity = 0)
32 { return Thread_Create_With_Group(&thread, startAddress, param, group, affinity); }
31 operator HANDLE() { return thread; } 33 operator HANDLE() { return thread; }
32 void Attach(HANDLE handle) { thread = handle; } 34 void Attach(HANDLE handle) { thread = handle; }
33 HANDLE Detach() { HANDLE h = thread; thread = NULL; return h; } 35 HANDLE Detach() { HANDLE h = thread; thread = NULL; return h; }
@@ -36,7 +38,7 @@ public:
36 bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread, exitCode)); } 38 bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread, exitCode)); }
37 int GetPriority() { return ::GetThreadPriority(thread); } 39 int GetPriority() { return ::GetThreadPriority(thread); }
38 bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread, priority)); } 40 bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread, priority)); }
39 #endif 41#endif
40}; 42};
41 43
42} 44}
diff --git a/CPP/Windows/TimeUtils.cpp b/CPP/Windows/TimeUtils.cpp
index bbd79ba..4e3bc59 100644
--- a/CPP/Windows/TimeUtils.cpp
+++ b/CPP/Windows/TimeUtils.cpp
@@ -258,8 +258,9 @@ bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day,
258 FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0, 258 FreeBSD 11.0, NetBSD 7.1, OpenBSD 6.0,
259 Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.3, 259 Minix 3.1.8, AIX 7.1, HP-UX 11.31, IRIX 6.5, Solaris 11.3,
260 Cygwin 2.9, mingw, MSVC 14, Android 9.0. 260 Cygwin 2.9, mingw, MSVC 14, Android 9.0.
261 Android NDK defines TIME_UTC but doesn't have the timespec_get().
261*/ 262*/
262#if defined(TIME_UTC) 263#if defined(TIME_UTC) && !defined(__ANDROID__)
263#define ZIP7_USE_timespec_get 264#define ZIP7_USE_timespec_get
264// #pragma message("ZIP7_USE_timespec_get") 265// #pragma message("ZIP7_USE_timespec_get")
265#elif defined(CLOCK_REALTIME) 266#elif defined(CLOCK_REALTIME)