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 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 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 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 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 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 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 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 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 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 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 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 375 bool MyGetFullPathName(CFSTR path, FString &resFullPath) 376 { 377 resFullPath = path; 378 return true; 379 } 380 381 #else 382 383 bool MyGetFullPathName(CFSTR path, FString &resFullPath) 384 { 385 return GetFullPath(path, resFullPath); 386 } 387 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 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 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 439 bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix) 440 { 441 FString resFileName; 442 return GetFullPathAndSplit(path, resDirPrefix, resFileName); 443 } 444 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 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 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 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 544 bool CTempFile::Remove() 545 { 546 if (!_mustBeDeleted) 547 return true; 548 _mustBeDeleted = !DeleteFileAlways(_path); 549 return !_mustBeDeleted; 550 } 551 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 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 575 bool CTempDir::Remove() 576 { 577 if (!_mustBeDeleted) 578 return true; 579 _mustBeDeleted = !RemoveDirWithSubItems(_path); 580 return !_mustBeDeleted; 581 } 582 583 }}} 584