1 // Windows/FileDir.cpp
2
3 #include "StdAfx.h"
4
5 #ifndef _UNICODE
6 #include "../Common/StringConvert.h"
7 #endif
8
9 #include "FileDir.h"
10 #include "FileFind.h"
11 #include "FileName.h"
12
13 #ifndef _UNICODE
14 extern bool g_IsNT;
15 #endif
16
17 using namespace NWindows;
18 using namespace NFile;
19 using namespace NName;
20
21 namespace NWindows {
22 namespace NFile {
23 namespace NDir {
24
25 #ifndef UNDER_CE
26
GetWindowsDir(FString & path)27 bool GetWindowsDir(FString &path)
28 {
29 UINT needLength;
30 #ifndef _UNICODE
31 if (!g_IsNT)
32 {
33 TCHAR s[MAX_PATH + 2];
34 s[0] = 0;
35 needLength = ::GetWindowsDirectory(s, MAX_PATH + 1);
36 path = fas2fs(s);
37 }
38 else
39 #endif
40 {
41 WCHAR s[MAX_PATH + 2];
42 s[0] = 0;
43 needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1);
44 path = us2fs(s);
45 }
46 return (needLength > 0 && needLength <= MAX_PATH);
47 }
48
GetSystemDir(FString & path)49 bool GetSystemDir(FString &path)
50 {
51 UINT needLength;
52 #ifndef _UNICODE
53 if (!g_IsNT)
54 {
55 TCHAR s[MAX_PATH + 2];
56 s[0] = 0;
57 needLength = ::GetSystemDirectory(s, MAX_PATH + 1);
58 path = fas2fs(s);
59 }
60 else
61 #endif
62 {
63 WCHAR s[MAX_PATH + 2];
64 s[0] = 0;
65 needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1);
66 path = us2fs(s);
67 }
68 return (needLength > 0 && needLength <= MAX_PATH);
69 }
70 #endif
71
SetDirTime(CFSTR path,const FILETIME * cTime,const FILETIME * aTime,const FILETIME * mTime)72 bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
73 {
74 #ifndef _UNICODE
75 if (!g_IsNT)
76 {
77 ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
78 return false;
79 }
80 #endif
81
82 HANDLE hDir = INVALID_HANDLE_VALUE;
83 IF_USE_MAIN_PATH
84 hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
85 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
86 #ifdef WIN_LONG_PATH
87 if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
88 {
89 UString longPath;
90 if (GetSuperPath(path, longPath, USE_MAIN_PATH))
91 hDir = ::CreateFileW(longPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
92 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
93 }
94 #endif
95
96 bool res = false;
97 if (hDir != INVALID_HANDLE_VALUE)
98 {
99 res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
100 ::CloseHandle(hDir);
101 }
102 return res;
103 }
104
SetFileAttrib(CFSTR path,DWORD attrib)105 bool SetFileAttrib(CFSTR path, DWORD attrib)
106 {
107 #ifndef _UNICODE
108 if (!g_IsNT)
109 {
110 if (::SetFileAttributes(fs2fas(path), attrib))
111 return true;
112 }
113 else
114 #endif
115 {
116 IF_USE_MAIN_PATH
117 if (::SetFileAttributesW(fs2us(path), attrib))
118 return true;
119 #ifdef WIN_LONG_PATH
120 if (USE_SUPER_PATH)
121 {
122 UString longPath;
123 if (GetSuperPath(path, longPath, USE_MAIN_PATH))
124 return BOOLToBool(::SetFileAttributesW(longPath, attrib));
125 }
126 #endif
127 }
128 return false;
129 }
130
RemoveDir(CFSTR path)131 bool RemoveDir(CFSTR path)
132 {
133 #ifndef _UNICODE
134 if (!g_IsNT)
135 {
136 if (::RemoveDirectory(fs2fas(path)))
137 return true;
138 }
139 else
140 #endif
141 {
142 IF_USE_MAIN_PATH
143 if (::RemoveDirectoryW(fs2us(path)))
144 return true;
145 #ifdef WIN_LONG_PATH
146 if (USE_SUPER_PATH)
147 {
148 UString longPath;
149 if (GetSuperPath(path, longPath, USE_MAIN_PATH))
150 return BOOLToBool(::RemoveDirectoryW(longPath));
151 }
152 #endif
153 }
154 return false;
155 }
156
MyMoveFile(CFSTR oldFile,CFSTR newFile)157 bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
158 {
159 #ifndef _UNICODE
160 if (!g_IsNT)
161 {
162 if (::MoveFile(fs2fas(oldFile), fs2fas(newFile)))
163 return true;
164 }
165 else
166 #endif
167 {
168 IF_USE_MAIN_PATH_2(oldFile, newFile)
169 if (::MoveFileW(fs2us(oldFile), fs2us(newFile)))
170 return true;
171 #ifdef WIN_LONG_PATH
172 if (USE_SUPER_PATH_2)
173 {
174 UString d1, d2;
175 if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
176 return BOOLToBool(::MoveFileW(d1, d2));
177 }
178 #endif
179 }
180 return false;
181 }
182
183 #ifndef UNDER_CE
184
185 EXTERN_C_BEGIN
186 typedef BOOL (WINAPI *Func_CreateHardLinkW)(
187 LPCWSTR lpFileName,
188 LPCWSTR lpExistingFileName,
189 LPSECURITY_ATTRIBUTES lpSecurityAttributes
190 );
191 EXTERN_C_END
192
MyCreateHardLink(CFSTR newFileName,CFSTR existFileName)193 bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
194 {
195 #ifndef _UNICODE
196 if (!g_IsNT)
197 {
198 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
199 return false;
200 /*
201 if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL))
202 return true;
203 */
204 }
205 else
206 #endif
207 {
208 Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW)
209 ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW");
210 if (!my_CreateHardLinkW)
211 return false;
212 IF_USE_MAIN_PATH_2(newFileName, existFileName)
213 if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL))
214 return true;
215 #ifdef WIN_LONG_PATH
216 if (USE_SUPER_PATH_2)
217 {
218 UString d1, d2;
219 if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2))
220 return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL));
221 }
222 #endif
223 }
224 return false;
225 }
226
227 #endif
228
CreateDir(CFSTR path)229 bool CreateDir(CFSTR path)
230 {
231 #ifndef _UNICODE
232 if (!g_IsNT)
233 {
234 if (::CreateDirectory(fs2fas(path), NULL))
235 return true;
236 }
237 else
238 #endif
239 {
240 IF_USE_MAIN_PATH
241 if (::CreateDirectoryW(fs2us(path), NULL))
242 return true;
243 #ifdef WIN_LONG_PATH
244 if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
245 {
246 UString longPath;
247 if (GetSuperPath(path, longPath, USE_MAIN_PATH))
248 return BOOLToBool(::CreateDirectoryW(longPath, NULL));
249 }
250 #endif
251 }
252 return false;
253 }
254
CreateComplexDir(CFSTR _aPathName)255 bool CreateComplexDir(CFSTR _aPathName)
256 {
257 FString pathName = _aPathName;
258 int pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
259 if (pos > 0 && (unsigned)pos == pathName.Len() - 1)
260 {
261 if (pathName.Len() == 3 && pathName[1] == L':')
262 return true; // Disk folder;
263 pathName.Delete(pos);
264 }
265 const FString pathName2 = pathName;
266 pos = pathName.Len();
267
268 for (;;)
269 {
270 if (CreateDir(pathName))
271 break;
272 if (::GetLastError() == ERROR_ALREADY_EXISTS)
273 {
274 NFind::CFileInfo fileInfo;
275 if (!fileInfo.Find(pathName)) // For network folders
276 return true;
277 if (!fileInfo.IsDir())
278 return false;
279 break;
280 }
281 pos = pathName.ReverseFind(FCHAR_PATH_SEPARATOR);
282 if (pos < 0 || pos == 0)
283 return false;
284 if (pathName[pos - 1] == L':')
285 return false;
286 pathName.DeleteFrom(pos);
287 }
288
289 while (pos < (int)pathName2.Len())
290 {
291 pos = pathName2.Find(FCHAR_PATH_SEPARATOR, pos + 1);
292 if (pos < 0)
293 pos = pathName2.Len();
294 pathName.SetFrom(pathName2, pos);
295 if (!CreateDir(pathName))
296 return false;
297 }
298
299 return true;
300 }
301
DeleteFileAlways(CFSTR path)302 bool DeleteFileAlways(CFSTR path)
303 {
304 if (!SetFileAttrib(path, 0))
305 return false;
306 #ifndef _UNICODE
307 if (!g_IsNT)
308 {
309 if (::DeleteFile(fs2fas(path)))
310 return true;
311 }
312 else
313 #endif
314 {
315 IF_USE_MAIN_PATH
316 if (::DeleteFileW(fs2us(path)))
317 return true;
318 #ifdef WIN_LONG_PATH
319 if (USE_SUPER_PATH)
320 {
321 UString longPath;
322 if (GetSuperPath(path, longPath, USE_MAIN_PATH))
323 return BOOLToBool(::DeleteFileW(longPath));
324 }
325 #endif
326 }
327 return false;
328 }
329
RemoveDirWithSubItems(const FString & path)330 bool RemoveDirWithSubItems(const FString &path)
331 {
332 bool needRemoveSubItems = true;
333 {
334 NFind::CFileInfo fi;
335 if (!fi.Find(path))
336 return false;
337 if (!fi.IsDir())
338 {
339 ::SetLastError(ERROR_DIRECTORY);
340 return false;
341 }
342 if (fi.HasReparsePoint())
343 needRemoveSubItems = false;
344 }
345
346 if (needRemoveSubItems)
347 {
348 FString s = path;
349 s += FCHAR_PATH_SEPARATOR;
350 unsigned prefixSize = s.Len();
351 s += FCHAR_ANY_MASK;
352 NFind::CEnumerator enumerator(s);
353 NFind::CFileInfo fi;
354 while (enumerator.Next(fi))
355 {
356 s.DeleteFrom(prefixSize);
357 s += fi.Name;
358 if (fi.IsDir())
359 {
360 if (!RemoveDirWithSubItems(s))
361 return false;
362 }
363 else if (!DeleteFileAlways(s))
364 return false;
365 }
366 }
367
368 if (!SetFileAttrib(path, 0))
369 return false;
370 return RemoveDir(path);
371 }
372
373 #ifdef UNDER_CE
374
MyGetFullPathName(CFSTR path,FString & resFullPath)375 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
376 {
377 resFullPath = path;
378 return true;
379 }
380
381 #else
382
MyGetFullPathName(CFSTR path,FString & resFullPath)383 bool MyGetFullPathName(CFSTR path, FString &resFullPath)
384 {
385 return GetFullPath(path, resFullPath);
386 }
387
SetCurrentDir(CFSTR path)388 bool SetCurrentDir(CFSTR path)
389 {
390 // SetCurrentDirectory doesn't support \\?\ prefix
391 #ifndef _UNICODE
392 if (!g_IsNT)
393 {
394 return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
395 }
396 else
397 #endif
398 {
399 return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
400 }
401 }
402
GetCurrentDir(FString & path)403 bool GetCurrentDir(FString &path)
404 {
405 path.Empty();
406 DWORD needLength;
407 #ifndef _UNICODE
408 if (!g_IsNT)
409 {
410 TCHAR s[MAX_PATH + 2];
411 s[0] = 0;
412 needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
413 path = fas2fs(s);
414 }
415 else
416 #endif
417 {
418 WCHAR s[MAX_PATH + 2];
419 s[0] = 0;
420 needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
421 path = us2fs(s);
422 }
423 return (needLength > 0 && needLength <= MAX_PATH);
424 }
425
426 #endif
427
GetFullPathAndSplit(CFSTR path,FString & resDirPrefix,FString & resFileName)428 bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
429 {
430 bool res = MyGetFullPathName(path, resDirPrefix);
431 if (!res)
432 resDirPrefix = path;
433 int pos = resDirPrefix.ReverseFind(FCHAR_PATH_SEPARATOR);
434 resFileName = resDirPrefix.Ptr(pos + 1);
435 resDirPrefix.DeleteFrom(pos + 1);
436 return res;
437 }
438
GetOnlyDirPrefix(CFSTR path,FString & resDirPrefix)439 bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
440 {
441 FString resFileName;
442 return GetFullPathAndSplit(path, resDirPrefix, resFileName);
443 }
444
MyGetTempPath(FString & path)445 bool MyGetTempPath(FString &path)
446 {
447 path.Empty();
448 DWORD needLength;
449 #ifndef _UNICODE
450 if (!g_IsNT)
451 {
452 TCHAR s[MAX_PATH + 2];
453 s[0] = 0;
454 needLength = ::GetTempPath(MAX_PATH + 1, s);
455 path = fas2fs(s);
456 }
457 else
458 #endif
459 {
460 WCHAR s[MAX_PATH + 2];
461 s[0] = 0;
462 needLength = ::GetTempPathW(MAX_PATH + 1, s);;
463 path = us2fs(s);
464 }
465 return (needLength > 0 && needLength <= MAX_PATH);
466 }
467
CreateTempFile(CFSTR prefix,bool addRandom,FString & path,NIO::COutFile * outFile)468 static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, NIO::COutFile *outFile)
469 {
470 UInt32 d = (GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
471 for (unsigned i = 0; i < 100; i++)
472 {
473 path = prefix;
474 if (addRandom)
475 {
476 FChar s[16];
477 UInt32 value = d;
478 unsigned k;
479 for (k = 0; k < 8; k++)
480 {
481 unsigned t = value & 0xF;
482 value >>= 4;
483 s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
484 }
485 s[k] = '\0';
486 if (outFile)
487 path += FChar('.');
488 path += s;
489 UInt32 step = GetTickCount() + 2;
490 if (step == 0)
491 step = 1;
492 d += step;
493 }
494 addRandom = true;
495 if (outFile)
496 path += FTEXT(".tmp");
497 if (NFind::DoesFileOrDirExist(path))
498 {
499 SetLastError(ERROR_ALREADY_EXISTS);
500 continue;
501 }
502 if (outFile)
503 {
504 if (outFile->Create(path, false))
505 return true;
506 }
507 else
508 {
509 if (CreateDir(path))
510 return true;
511 }
512 DWORD error = GetLastError();
513 if (error != ERROR_FILE_EXISTS &&
514 error != ERROR_ALREADY_EXISTS)
515 break;
516 }
517 path.Empty();
518 return false;
519 }
520
Create(CFSTR prefix,NIO::COutFile * outFile)521 bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
522 {
523 if (!Remove())
524 return false;
525 if (!CreateTempFile(prefix, false, _path, outFile))
526 return false;
527 _mustBeDeleted = true;
528 return true;
529 }
530
CreateRandomInTempFolder(CFSTR namePrefix,NIO::COutFile * outFile)531 bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
532 {
533 if (!Remove())
534 return false;
535 FString tempPath;
536 if (!MyGetTempPath(tempPath))
537 return false;
538 if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
539 return false;
540 _mustBeDeleted = true;
541 return true;
542 }
543
Remove()544 bool CTempFile::Remove()
545 {
546 if (!_mustBeDeleted)
547 return true;
548 _mustBeDeleted = !DeleteFileAlways(_path);
549 return !_mustBeDeleted;
550 }
551
MoveTo(CFSTR name,bool deleteDestBefore)552 bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
553 {
554 if (deleteDestBefore)
555 if (NFind::DoesFileExist(name))
556 if (!DeleteFileAlways(name))
557 return false;
558 DisableDeleting();
559 return MyMoveFile(_path, name);
560 }
561
Create(CFSTR prefix)562 bool CTempDir::Create(CFSTR prefix)
563 {
564 if (!Remove())
565 return false;
566 FString tempPath;
567 if (!MyGetTempPath(tempPath))
568 return false;
569 if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
570 return false;
571 _mustBeDeleted = true;
572 return true;
573 }
574
Remove()575 bool CTempDir::Remove()
576 {
577 if (!_mustBeDeleted)
578 return true;
579 _mustBeDeleted = !RemoveDirWithSubItems(_path);
580 return !_mustBeDeleted;
581 }
582
583 }}}
584