diff options
Diffstat (limited to 'CPP/Windows')
-rw-r--r-- | CPP/Windows/FileDir.cpp | 211 | ||||
-rw-r--r-- | CPP/Windows/FileDir.h | 41 | ||||
-rw-r--r-- | CPP/Windows/FileFind.cpp | 9 | ||||
-rw-r--r-- | CPP/Windows/FileIO.h | 43 | ||||
-rw-r--r-- | CPP/Windows/FileLink.cpp | 244 | ||||
-rw-r--r-- | CPP/Windows/FileName.cpp | 79 | ||||
-rw-r--r-- | CPP/Windows/FileName.h | 13 | ||||
-rw-r--r-- | CPP/Windows/Registry.cpp | 295 | ||||
-rw-r--r-- | CPP/Windows/Registry.h | 48 | ||||
-rw-r--r-- | CPP/Windows/System.cpp | 169 | ||||
-rw-r--r-- | CPP/Windows/System.h | 63 | ||||
-rw-r--r-- | CPP/Windows/SystemInfo.cpp | 125 | ||||
-rw-r--r-- | CPP/Windows/SystemInfo.h | 2 | ||||
-rw-r--r-- | CPP/Windows/Thread.h | 8 | ||||
-rw-r--r-- | CPP/Windows/TimeUtils.cpp | 3 |
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 | ||
126 | bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) | 127 | static 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 | ||
160 | bool 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 | |||
165 | bool 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 | ||
161 | bool SetFileAttrib(CFSTR path, DWORD attrib) | 171 | bool 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 | |||
225 | bool MyMoveFile(CFSTR oldFile, CFSTR newFile) | 237 | bool 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 | ||
266 | static 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 | |||
284 | bool 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 | ||
663 | bool 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 | ||
881 | bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore) | 975 | bool 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 | ||
944 | static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile) | 1038 | static 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 | ||
971 | bool MyMoveFile(CFSTR oldFile, CFSTR newFile) | 1089 | bool 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 | ||
1112 | bool MyMoveFile(CFSTR oldFile, CFSTR newFile) | ||
1113 | { | ||
1114 | return MyMoveFile_with_Progress(oldFile, newFile, NULL); | ||
1115 | } | ||
1116 | |||
993 | 1117 | ||
994 | bool CreateDir(CFSTR path) | 1118 | bool CreateDir(CFSTR path) |
995 | { | 1119 | { |
@@ -1058,17 +1182,15 @@ bool GetCurrentDir(FString &path) | |||
1058 | 1182 | ||
1059 | 1183 | ||
1060 | 1184 | ||
1061 | bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) | 1185 | static 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 | ||
1232 | bool 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 | |||
1237 | bool 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 | ||
1126 | struct C_umask | 1243 | struct 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); | |||
18 | WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file | 18 | WIN32 API : SetFileTime() doesn't allow to set zero timestamps in file |
19 | but linux : allows unix time = 0 in filesystem | 19 | but linux : allows unix time = 0 in filesystem |
20 | */ | 20 | */ |
21 | 21 | /* | |
22 | SetDirTime() can be used to set time for file or for dir. | ||
23 | If path is symbolic link, SetDirTime() will follow symbolic link, | ||
24 | and it will set timestamps of symbolic link's target file or dir. | ||
25 | */ | ||
22 | bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); | 26 | bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); |
23 | 27 | ||
28 | /* | ||
29 | SetLinkFileTime() doesn't follow symbolic link, | ||
30 | and it sets timestamps for symbolic link file itself. | ||
31 | If (path) is not symbolic link, it still can work (at least in some new OS versions). | ||
32 | */ | ||
33 | bool 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); | |||
41 | bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib); | 52 | bool 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 | ||
61 | Z7_PURE_INTERFACES_BEGIN | ||
62 | DECLARE_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 | }; | ||
68 | Z7_PURE_INTERFACES_END | ||
69 | |||
44 | bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); | 70 | bool MyMoveFile(CFSTR existFileName, CFSTR newFileName); |
71 | // (progress == NULL) is allowed | ||
72 | bool MyMoveFile_with_Progress(CFSTR oldFile, CFSTR newFile, | ||
73 | ICopyFileProgress *progress); | ||
74 | |||
45 | 75 | ||
46 | #ifndef UNDER_CE | 76 | #ifndef UNDER_CE |
47 | bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName); | 77 | bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName); |
@@ -59,6 +89,11 @@ bool CreateComplexDir(CFSTR path); | |||
59 | 89 | ||
60 | bool DeleteFileAlways(CFSTR name); | 90 | bool DeleteFileAlways(CFSTR name); |
61 | bool RemoveDirWithSubItems(const FString &path); | 91 | bool RemoveDirWithSubItems(const FString &path); |
92 | #ifdef _WIN32 | ||
93 | bool RemoveDirAlways_if_Empty(const FString &path); | ||
94 | #else | ||
95 | #define RemoveDirAlways_if_Empty RemoveDir | ||
96 | #endif | ||
62 | 97 | ||
63 | bool MyGetFullPathName(CFSTR path, FString &resFullPath); | 98 | bool MyGetFullPathName(CFSTR path, FString &resFullPath); |
64 | bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName); | 99 | bool 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 | ||
774 | bool CFileInfoBase::Fill_From_ByHandleFileInfo(CFSTR path) | 781 | bool 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 { | |||
44 | namespace NFile { | 43 | namespace NFile { |
45 | 44 | ||
46 | #if defined(_WIN32) && !defined(UNDER_CE) | 45 | #if defined(_WIN32) && !defined(UNDER_CE) |
47 | bool 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 | */ | ||
52 | void Convert_WinPath_to_WslLinuxPath(FString &path, bool convertDrivePath); | ||
53 | // (path) must use Linux path separator (/). | ||
54 | void 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 | */ | ||
64 | void FillLinkData_WinLink(CByteBuffer &dest, const wchar_t *path, bool isSymLink); | ||
65 | // in: (CByteBuffer &dest) is empty | ||
66 | inline 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 | ||
50 | struct CReparseShortInfo | 75 | struct 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 { | |||
39 | using namespace NName; | 39 | using namespace NName; |
40 | 40 | ||
41 | /* | 41 | /* |
42 | Win10 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 | |||
84 | DOCS: | ||
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 | |||
89 | reparse tags, with the exception of IO_REPARSE_TAG_SYMLINK, | ||
90 | are processed on the server and are not processed by a client | ||
91 | after transmission over the wire. | ||
92 | Clients 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 | ||
96 | static const wchar_t * const k_LinkPrefix = L"\\??\\"; | 118 | static const char * const k_LinkPrefix = "\\??\\"; |
119 | static const char * const k_LinkPrefix_UNC = "\\??\\UNC\\"; | ||
97 | static const unsigned k_LinkPrefix_Size = 4; | 120 | static const unsigned k_LinkPrefix_Size = 4; |
98 | 121 | ||
99 | static bool IsLinkPrefix(const wchar_t *s) | 122 | static bool IsLinkPrefix(const wchar_t *s) |
@@ -102,7 +125,7 @@ static bool IsLinkPrefix(const wchar_t *s) | |||
102 | } | 125 | } |
103 | 126 | ||
104 | /* | 127 | /* |
105 | static const wchar_t * const k_VolumePrefix = L"Volume{"; | 128 | static const char * const k_VolumePrefix = "Volume{"; |
106 | static const bool IsVolumeName(const wchar_t *s) | 129 | static 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 | ||
129 | bool FillLinkData(CByteBuffer &dest, const wchar_t *path, bool isSymLink, bool isWSL) | 152 | #ifdef _WIN32 |
153 | void 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; | 168 | static const unsigned k_Link_Size_Limit = 1u << 16; // 16-bit field is used for size. |
169 | |||
170 | void 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)) | 190 | void 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 | |||
242 | bool CReparseAttr::Parse(const Byte *p, size_t size) | 303 | bool 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 | ||
336 | bool CReparseShortInfo::Parse(const Byte *p, size_t size) | 400 | bool 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 | ||
416 | UString CReparseAttr::GetPath() const | 484 | UString 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 | |||
468 | static bool CreatePrefixDirOfFile(CFSTR path) | 541 | static 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 |
498 | bool SetReparseData(CFSTR path, bool isDir, const void *data, DWORD size) | 573 | bool 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'))) | ||
70 | bool 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 | ||
75 | bool 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 |
72 | static void NormalizeDirSeparators(UString &s) | 79 | static 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 | |||
95 | bool IsDrivePath(const wchar_t *s) throw() { return IS_LETTER_CHAR(s[0]) && s[1] == ':' && IS_SEPAR(s[2]); } | ||
96 | |||
97 | bool IsAltPathPrefix(CFSTR s) throw() | 97 | bool 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 | ||
122 | const char * const kSuperPathPrefix = "\\\\?\\"; | 123 | const 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 |
124 | static const char * const kSuperUncPrefix = "\\\\?\\UNC\\"; | 128 | static 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 | ||
144 | static const unsigned kDrivePrefixSize = 3; /* c:\ */ | ||
145 | |||
146 | bool IsSuperPath(const wchar_t *s) throw(); | ||
147 | bool 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])) | ||
153 | bool IsSuperOrDevicePath(const wchar_t *s) throw() { return IS_SUPER_OR_DEVICE_PATH(s); } | ||
137 | bool IsDevicePath(CFSTR s) throw() | 154 | bool 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 | ||
212 | static const unsigned kDrivePrefixSize = 3; /* c:\ */ | ||
213 | |||
214 | bool 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; } | ||
216 | bool IsSuperPath(const wchar_t *s) throw() { return IS_SUPER_PREFIX(s); } | ||
217 | bool 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 | |||
220 | bool IsAltStreamPrefixWithColon(const UString &s) throw() | 229 | bool 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() | |||
278 | int FindAltStreamColon(CFSTR path) throw() | 287 | int 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 | ||
351 | static unsigned GetRootPrefixSize_Of_NetworkPath(const wchar_t *s) throw() | 364 | static 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 | ||
389 | unsigned GetRootPrefixSize(const wchar_t *s) throw() | 403 | unsigned GetRootPrefixSize(const wchar_t *s) throw() |
404 | #else | ||
405 | unsigned 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 | ||
400 | bool IsAbsolutePath(const wchar_t *s) throw() { return IS_SEPAR(s[0]); } | 417 | bool 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 | ||
26 | bool IsAltPathPrefix(CFSTR s) throw(); /* name: */ | 26 | bool IsAltPathPrefix(CFSTR s) throw(); /* name: */ |
27 | 27 | ||
28 | #if defined(_WIN32) && !defined(UNDER_CE) | ||
29 | |||
30 | extern const char * const kSuperPathPrefix; /* \\?\ */ | 28 | extern const char * const kSuperPathPrefix; /* \\?\ */ |
31 | const unsigned kDevicePathPrefixSize = 4; | 29 | const unsigned kDevicePathPrefixSize = 4; |
32 | const unsigned kSuperPathPrefixSize = 4; | 30 | const unsigned kSuperPathPrefixSize = 4; |
33 | const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4; | 31 | const unsigned kSuperUncPathPrefixSize = kSuperPathPrefixSize + 4; |
34 | 32 | ||
33 | #if defined(_WIN32) && !defined(UNDER_CE) | ||
34 | |||
35 | bool IsDevicePath(CFSTR s) throw(); /* \\.\ */ | 35 | bool IsDevicePath(CFSTR s) throw(); /* \\.\ */ |
36 | bool IsSuperUncPath(CFSTR s) throw(); /* \\?\UNC\ */ | 36 | bool IsSuperUncPath(CFSTR s) throw(); /* \\?\UNC\ */ |
37 | bool IsNetworkPath(CFSTR s) throw(); /* \\?\UNC\ or \\SERVER */ | 37 | bool IsNetworkPath(CFSTR s) throw(); /* \\?\UNC\ or \\SERVER */ |
@@ -86,6 +86,15 @@ int FindAltStreamColon(CFSTR path) throw(); | |||
86 | bool IsAbsolutePath(const wchar_t *s) throw(); | 86 | bool IsAbsolutePath(const wchar_t *s) throw(); |
87 | unsigned GetRootPrefixSize(const wchar_t *s) throw(); | 87 | unsigned 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 | */ | ||
95 | unsigned GetRootPrefixSize_WINDOWS(const wchar_t *s) throw(); | ||
96 | #endif | ||
97 | |||
89 | #ifdef Z7_LONG_PATH | 98 | #ifdef Z7_LONG_PATH |
90 | 99 | ||
91 | const int kSuperPathType_UseOnlyMain = 0; | 100 | const 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 |
83 | LONG CKey::DeleteSubKey(LPCTSTR subKeyName) throw() | 83 | LONG CKey::DeleteSubKey(LPCTSTR subKeyName) throw() |
84 | { | 84 | { |
@@ -88,22 +88,36 @@ LONG CKey::DeleteSubKey(LPCTSTR subKeyName) throw() | |||
88 | 88 | ||
89 | LONG CKey::RecurseDeleteKey(LPCTSTR subKeyName) throw() | 89 | LONG 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 | ||
146 | LONG CKey::SetValue(LPCTSTR name, LPCTSTR value) throw() | 162 | LONG 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 | ||
208 | LONG 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 | ||
219 | LONG CKey::QueryValue(LPCTSTR name, bool &value) throw() | 227 | LONG 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 | ||
227 | LONG CKey::GetValue_IfOk(LPCTSTR name, UInt32 &value) throw() | 243 | LONG 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 | ||
236 | LONG CKey::GetValue_IfOk(LPCTSTR name, bool &value) throw() | 258 | LONG 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 | ||
245 | LONG 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 | ||
253 | LONG CKey::QueryValue(LPCTSTR name, CSysString &value) | 269 | LONG 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 | ||
272 | LONG 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 | |||
280 | LONG CKey::QueryValue(LPCWSTR name, UString &value) | 316 | LONG 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 | ||
309 | LONG CKey::QueryValue(LPCTSTR name, void *value, UInt32 &count) throw() | 365 | LONG 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 | ||
318 | LONG 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 | |||
329 | LONG CKey::EnumKeys(CSysStringVector &keyNames) | 402 | LONG 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 | |||
350 | LONG CKey::SetValue_Strings(LPCTSTR valueName, const UStringVector &strings) | 424 | LONG 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) | |||
14 | class CKey | 14 | class 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 | |||
17 | public: | 24 | public: |
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 | /* | ||
29 | note: returned value in 32-bit version can be limited by value 32. | ||
30 | while 64-bit version returns full value. | ||
31 | GetMaximumProcessorCount(groupNumber) can return higher value than | ||
32 | GetActiveProcessorCount(groupNumber) in some cases, because CPUs can be added. | ||
33 | */ | ||
34 | // typedef DWORD (WINAPI *Func_GetMaximumProcessorCount)(WORD GroupNumber); | ||
35 | typedef DWORD (WINAPI *Func_GetActiveProcessorCount)(WORD GroupNumber); | ||
36 | typedef 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 | |||
45 | Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION | ||
46 | |||
47 | bool 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 | |||
28 | UInt32 CountAffinity(DWORD_PTR mask) | 91 | UInt32 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 | ||
39 | BOOL CProcessAffinity::Get() | 102 | BOOL 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 | ||
140 | UInt32 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 | |||
49 | UInt32 GetNumberOfProcessors() | 154 | UInt32 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 | ||
145 | bool GetRamSize(UInt64 &size) | 239 | bool 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 | ||
190 | bool GetRamSize(UInt64 &size) | 296 | bool 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 | ||
14 | namespace NWindows { | 15 | namespace NWindows { |
@@ -16,6 +17,34 @@ namespace NSystem { | |||
16 | 17 | ||
17 | #ifdef _WIN32 | 18 | #ifdef _WIN32 |
18 | 19 | ||
20 | struct 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 | |||
19 | UInt32 CountAffinity(DWORD_PTR mask); | 48 | UInt32 CountAffinity(DWORD_PTR mask); |
20 | 49 | ||
21 | struct CProcessAffinity | 50 | struct 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 | ||
123 | UInt32 GetNumberOfProcessors(); | 180 | UInt32 GetNumberOfProcessors(); |
124 | 181 | ||
125 | bool GetRamSize(UInt64 &size); // returns false, if unknown ram size | 182 | bool GetRamSize(size_t &size); // returns false, if unknown ram size |
126 | 183 | ||
127 | unsigned long Get_File_OPEN_MAX(); | 184 | unsigned long Get_File_OPEN_MAX(); |
128 | unsigned Get_File_OPEN_MAX_Reduced_for_3_tasks(); | 185 | unsigned 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 | ||
544 | void CCpuName::Fill() | 596 | void 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 | /* | ||
659 | mapping arm64 registers to Windows registry: | ||
660 | CP 4000: MIDR_EL1 | ||
661 | CP 4020: ID_AA64PFR0_EL1 | ||
662 | CP 4021: ID_AA64PFR1_EL1 | ||
663 | CP 4028: ID_AA64DFR0_EL1 | ||
664 | CP 4029: ID_AA64DFR1_EL1 | ||
665 | CP 402C: ID_AA64AFR0_EL1 | ||
666 | CP 402D: ID_AA64AFR1_EL1 | ||
667 | CP 4030: ID_AA64ISAR0_EL1 | ||
668 | CP 4031: ID_AA64ISAR1_EL1 | ||
669 | CP 4038: ID_AA64MMFR0_EL1 | ||
670 | CP 4039: ID_AA64MMFR1_EL1 | ||
671 | CP 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 | ||
935 | void GetCpuName_MultiLine(AString &s); | 1012 | void GetCpuName_MultiLine(AString &s, AString ®isters); |
936 | void GetCpuName_MultiLine(AString &s) | 1013 | void GetCpuName_MultiLine(AString &s, AString ®isters) |
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 | ||
9 | void GetCpuName_MultiLine(AString &s); | 9 | void GetCpuName_MultiLine(AString &s, AString ®isters); |
10 | 10 | ||
11 | void GetOsInfoText(AString &sRes); | 11 | void GetOsInfoText(AString &sRes); |
12 | void GetSystemInfoText(AString &s); | 12 | void 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) |