mirror of
https://github.com/holub/mame
synced 2025-06-07 13:23:50 +03:00
1230 lines
26 KiB
C++
1230 lines
26 KiB
C++
// Windows/FileDir.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
|
|
#ifndef _WIN32
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <utime.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "../Common/StringConvert.h"
|
|
#include "../Common/C_FileIO.h"
|
|
#endif
|
|
|
|
#include "FileDir.h"
|
|
#include "FileFind.h"
|
|
#include "FileName.h"
|
|
|
|
#ifndef _UNICODE
|
|
extern bool g_IsNT;
|
|
#endif
|
|
|
|
using namespace NWindows;
|
|
using namespace NFile;
|
|
using namespace NName;
|
|
|
|
#ifndef _WIN32
|
|
|
|
static bool FiTime_To_timespec(const CFiTime *ft, timespec &ts)
|
|
{
|
|
if (ft)
|
|
{
|
|
ts = *ft;
|
|
return true;
|
|
}
|
|
// else
|
|
{
|
|
ts.tv_sec = 0;
|
|
ts.tv_nsec =
|
|
#ifdef UTIME_OMIT
|
|
UTIME_OMIT; // -2 keep old timesptamp
|
|
#else
|
|
// UTIME_NOW; -1 // set to the current time
|
|
0;
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
namespace NWindows {
|
|
namespace NFile {
|
|
namespace NDir {
|
|
|
|
#ifdef _WIN32
|
|
|
|
#ifndef UNDER_CE
|
|
|
|
bool GetWindowsDir(FString &path)
|
|
{
|
|
const unsigned kBufSize = MAX_PATH + 16;
|
|
UINT len;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetWindowsDirectory(s, kBufSize);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetWindowsDirectoryW(s, kBufSize);
|
|
path = us2fs(s);
|
|
}
|
|
return (len != 0 && len < kBufSize);
|
|
}
|
|
|
|
|
|
/*
|
|
new DOCs for GetSystemDirectory:
|
|
returned path does not end with a backslash unless the
|
|
system directory is the root directory.
|
|
*/
|
|
|
|
bool GetSystemDir(FString &path)
|
|
{
|
|
const unsigned kBufSize = MAX_PATH + 16;
|
|
UINT len;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetSystemDirectory(s, kBufSize);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetSystemDirectoryW(s, kBufSize);
|
|
path = us2fs(s);
|
|
}
|
|
return (len != 0 && len < kBufSize);
|
|
}
|
|
#endif // UNDER_CE
|
|
|
|
|
|
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
HANDLE hDir = INVALID_HANDLE_VALUE;
|
|
IF_USE_MAIN_PATH
|
|
hDir = ::CreateFileW(fs2us(path), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
#ifdef Z7_LONG_PATH
|
|
if (hDir == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
|
|
{
|
|
UString superPath;
|
|
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
|
hDir = ::CreateFileW(superPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
}
|
|
#endif
|
|
|
|
bool res = false;
|
|
if (hDir != INVALID_HANDLE_VALUE)
|
|
{
|
|
res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
|
|
::CloseHandle(hDir);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
bool SetFileAttrib(CFSTR path, DWORD attrib)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::SetFileAttributes(fs2fas(path), attrib))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
IF_USE_MAIN_PATH
|
|
if (::SetFileAttributesW(fs2us(path), attrib))
|
|
return true;
|
|
#ifdef Z7_LONG_PATH
|
|
if (USE_SUPER_PATH)
|
|
{
|
|
UString superPath;
|
|
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
|
return BOOLToBool(::SetFileAttributesW(superPath, attrib));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib)
|
|
{
|
|
#ifdef _WIN32
|
|
if ((attrib & 0xF0000000) != 0)
|
|
attrib &= 0x3FFF;
|
|
#endif
|
|
return SetFileAttrib(path, attrib);
|
|
}
|
|
|
|
|
|
bool RemoveDir(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::RemoveDirectory(fs2fas(path)))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
IF_USE_MAIN_PATH
|
|
if (::RemoveDirectoryW(fs2us(path)))
|
|
return true;
|
|
#ifdef Z7_LONG_PATH
|
|
if (USE_SUPER_PATH)
|
|
{
|
|
UString superPath;
|
|
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
|
return BOOLToBool(::RemoveDirectoryW(superPath));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::MoveFile(fs2fas(oldFile), fs2fas(newFile)))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
IF_USE_MAIN_PATH_2(oldFile, newFile)
|
|
{
|
|
if (::MoveFileW(fs2us(oldFile), fs2us(newFile)))
|
|
return true;
|
|
}
|
|
#ifdef Z7_LONG_PATH
|
|
if (USE_SUPER_PATH_2)
|
|
{
|
|
UString d1, d2;
|
|
if (GetSuperPaths(oldFile, newFile, d1, d2, USE_MAIN_PATH_2))
|
|
return BOOLToBool(::MoveFileW(d1, d2));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifndef UNDER_CE
|
|
EXTERN_C_BEGIN
|
|
typedef BOOL (WINAPI *Func_CreateHardLinkW)(
|
|
LPCWSTR lpFileName,
|
|
LPCWSTR lpExistingFileName,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes
|
|
);
|
|
EXTERN_C_END
|
|
#endif // UNDER_CE
|
|
|
|
bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return false;
|
|
/*
|
|
if (::CreateHardLink(fs2fas(newFileName), fs2fas(existFileName), NULL))
|
|
return true;
|
|
*/
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
const
|
|
Func_CreateHardLinkW
|
|
my_CreateHardLinkW = Z7_GET_PROC_ADDRESS(
|
|
Func_CreateHardLinkW, ::GetModuleHandleW(L"kernel32.dll"),
|
|
"CreateHardLinkW");
|
|
if (!my_CreateHardLinkW)
|
|
return false;
|
|
IF_USE_MAIN_PATH_2(newFileName, existFileName)
|
|
{
|
|
if (my_CreateHardLinkW(fs2us(newFileName), fs2us(existFileName), NULL))
|
|
return true;
|
|
}
|
|
#ifdef Z7_LONG_PATH
|
|
if (USE_SUPER_PATH_2)
|
|
{
|
|
UString d1, d2;
|
|
if (GetSuperPaths(newFileName, existFileName, d1, d2, USE_MAIN_PATH_2))
|
|
return BOOLToBool(my_CreateHardLinkW(d1, d2, NULL));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
WinXP-64 CreateDir():
|
|
"" - ERROR_PATH_NOT_FOUND
|
|
\ - ERROR_ACCESS_DENIED
|
|
C:\ - ERROR_ACCESS_DENIED, if there is such drive,
|
|
|
|
D:\folder - ERROR_PATH_NOT_FOUND, if there is no such drive,
|
|
C:\nonExistent\folder - ERROR_PATH_NOT_FOUND
|
|
|
|
C:\existFolder - ERROR_ALREADY_EXISTS
|
|
C:\existFolder\ - ERROR_ALREADY_EXISTS
|
|
|
|
C:\folder - OK
|
|
C:\folder\ - OK
|
|
|
|
\\Server\nonExistent - ERROR_BAD_NETPATH
|
|
\\Server\Share_Readonly - ERROR_ACCESS_DENIED
|
|
\\Server\Share - ERROR_ALREADY_EXISTS
|
|
|
|
\\Server\Share_NTFS_drive - ERROR_ACCESS_DENIED
|
|
\\Server\Share_FAT_drive - ERROR_ALREADY_EXISTS
|
|
*/
|
|
|
|
bool CreateDir(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::CreateDirectory(fs2fas(path), NULL))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
IF_USE_MAIN_PATH
|
|
if (::CreateDirectoryW(fs2us(path), NULL))
|
|
return true;
|
|
#ifdef Z7_LONG_PATH
|
|
if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
|
|
{
|
|
UString superPath;
|
|
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
|
return BOOLToBool(::CreateDirectoryW(superPath, NULL));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
CreateDir2 returns true, if directory can contain files after the call (two cases):
|
|
1) the directory already exists
|
|
2) the directory was created
|
|
path must be WITHOUT trailing path separator.
|
|
|
|
We need CreateDir2, since fileInfo.Find() for reserved names like "com8"
|
|
returns FILE instead of DIRECTORY. And we need to use SuperPath */
|
|
|
|
static bool CreateDir2(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::CreateDirectory(fs2fas(path), NULL))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
IF_USE_MAIN_PATH
|
|
if (::CreateDirectoryW(fs2us(path), NULL))
|
|
return true;
|
|
#ifdef Z7_LONG_PATH
|
|
if ((!USE_MAIN_PATH || ::GetLastError() != ERROR_ALREADY_EXISTS) && USE_SUPER_PATH)
|
|
{
|
|
UString superPath;
|
|
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
|
{
|
|
if (::CreateDirectoryW(superPath, NULL))
|
|
return true;
|
|
if (::GetLastError() != ERROR_ALREADY_EXISTS)
|
|
return false;
|
|
NFind::CFileInfo fi;
|
|
if (!fi.Find(us2fs(superPath)))
|
|
return false;
|
|
return fi.IsDir();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (::GetLastError() != ERROR_ALREADY_EXISTS)
|
|
return false;
|
|
NFind::CFileInfo fi;
|
|
if (!fi.Find(path))
|
|
return false;
|
|
return fi.IsDir();
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
static bool CreateDir2(CFSTR path);
|
|
|
|
bool CreateComplexDir(CFSTR _path)
|
|
{
|
|
#ifdef _WIN32
|
|
|
|
{
|
|
const DWORD attrib = NFind::GetFileAttrib(_path);
|
|
if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
|
return true;
|
|
}
|
|
|
|
#ifndef UNDER_CE
|
|
|
|
if (IsDriveRootPath_SuperAllowed(_path))
|
|
return false;
|
|
|
|
const unsigned prefixSize = GetRootPrefixSize(_path);
|
|
|
|
#endif // UNDER_CE
|
|
|
|
#else // _WIN32
|
|
|
|
// Posix
|
|
NFind::CFileInfo fi;
|
|
if (fi.Find(_path))
|
|
{
|
|
if (fi.IsDir())
|
|
return true;
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
FString path (_path);
|
|
|
|
int pos = path.ReverseFind_PathSepar();
|
|
if (pos >= 0 && (unsigned)pos == path.Len() - 1)
|
|
{
|
|
if (path.Len() == 1)
|
|
return true;
|
|
path.DeleteBack();
|
|
}
|
|
|
|
const FString path2 (path);
|
|
pos = (int)path.Len();
|
|
|
|
for (;;)
|
|
{
|
|
if (CreateDir2(path))
|
|
break;
|
|
if (::GetLastError() == ERROR_ALREADY_EXISTS)
|
|
return false;
|
|
pos = path.ReverseFind_PathSepar();
|
|
if (pos < 0 || pos == 0)
|
|
return false;
|
|
|
|
#if defined(_WIN32) && !defined(UNDER_CE)
|
|
if (pos == 1 && IS_PATH_SEPAR(path[0]))
|
|
return false;
|
|
if (prefixSize >= (unsigned)pos + 1)
|
|
return false;
|
|
#endif
|
|
|
|
path.DeleteFrom((unsigned)pos);
|
|
}
|
|
|
|
while (pos < (int)path2.Len())
|
|
{
|
|
int pos2 = NName::FindSepar(path2.Ptr((unsigned)pos + 1));
|
|
if (pos2 < 0)
|
|
pos = (int)path2.Len();
|
|
else
|
|
pos += 1 + pos2;
|
|
path.SetFrom(path2, (unsigned)pos);
|
|
if (!CreateDir(path))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
bool DeleteFileAlways(CFSTR path)
|
|
{
|
|
/* If alt stream, we also need to clear READ-ONLY attribute of main file before delete.
|
|
SetFileAttrib("name:stream", ) changes attributes of main file. */
|
|
{
|
|
DWORD attrib = NFind::GetFileAttrib(path);
|
|
if (attrib != INVALID_FILE_ATTRIBUTES
|
|
&& (attrib & FILE_ATTRIBUTE_DIRECTORY) == 0
|
|
&& (attrib & FILE_ATTRIBUTE_READONLY) != 0)
|
|
{
|
|
if (!SetFileAttrib(path, attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
if (::DeleteFile(fs2fas(path)))
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* DeleteFile("name::$DATA") deletes all alt streams (same as delete DeleteFile("name")).
|
|
Maybe it's better to open "name::$DATA" and clear data for unnamed stream? */
|
|
IF_USE_MAIN_PATH
|
|
if (::DeleteFileW(fs2us(path)))
|
|
return true;
|
|
#ifdef Z7_LONG_PATH
|
|
if (USE_SUPER_PATH)
|
|
{
|
|
UString superPath;
|
|
if (GetSuperPath(path, superPath, USE_MAIN_PATH))
|
|
return BOOLToBool(::DeleteFileW(superPath));
|
|
}
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
bool RemoveDirWithSubItems(const FString &path)
|
|
{
|
|
bool needRemoveSubItems = true;
|
|
{
|
|
NFind::CFileInfo fi;
|
|
if (!fi.Find(path))
|
|
return false;
|
|
if (!fi.IsDir())
|
|
{
|
|
::SetLastError(ERROR_DIRECTORY);
|
|
return false;
|
|
}
|
|
if (fi.HasReparsePoint())
|
|
needRemoveSubItems = false;
|
|
}
|
|
|
|
if (needRemoveSubItems)
|
|
{
|
|
FString s (path);
|
|
s.Add_PathSepar();
|
|
const unsigned prefixSize = s.Len();
|
|
NFind::CEnumerator enumerator;
|
|
enumerator.SetDirPrefix(s);
|
|
NFind::CDirEntry fi;
|
|
bool isError = false;
|
|
DWORD lastError = 0;
|
|
while (enumerator.Next(fi))
|
|
{
|
|
s.DeleteFrom(prefixSize);
|
|
s += fi.Name;
|
|
if (fi.IsDir())
|
|
{
|
|
if (!RemoveDirWithSubItems(s))
|
|
{
|
|
lastError = GetLastError();
|
|
isError = true;
|
|
}
|
|
}
|
|
else if (!DeleteFileAlways(s))
|
|
{
|
|
lastError = GetLastError();
|
|
isError = false;
|
|
}
|
|
}
|
|
if (isError)
|
|
{
|
|
SetLastError(lastError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// we clear read-only attrib to remove read-only dir
|
|
if (!SetFileAttrib(path, 0))
|
|
return false;
|
|
return RemoveDir(path);
|
|
}
|
|
|
|
#endif // _WIN32
|
|
|
|
#ifdef UNDER_CE
|
|
|
|
bool MyGetFullPathName(CFSTR path, FString &resFullPath)
|
|
{
|
|
resFullPath = path;
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
|
|
bool MyGetFullPathName(CFSTR path, FString &resFullPath)
|
|
{
|
|
return GetFullPath(path, resFullPath);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
/* Win10: SetCurrentDirectory() doesn't support long paths and
|
|
doesn't support super prefix "\\?\", if long path behavior is not
|
|
enabled in registry (LongPathsEnabled) and in manifest (longPathAware). */
|
|
|
|
bool SetCurrentDir(CFSTR path)
|
|
{
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
we use system function GetCurrentDirectory()
|
|
new GetCurrentDirectory() DOCs:
|
|
- If the function fails, the return value is zero.
|
|
- If the function succeeds, the return value specifies
|
|
the number of characters that are written to the buffer,
|
|
not including the terminating null character.
|
|
- If the buffer is not large enough, the return value specifies
|
|
the required size of the buffer, in characters,
|
|
including the null-terminating character.
|
|
|
|
GetCurrentDir() calls GetCurrentDirectory().
|
|
GetCurrentDirectory() in win10 in tests:
|
|
the returned (path) does not end with a backslash, if
|
|
current directory is not root directory of drive.
|
|
But that behavior is not guarantied in specification docs.
|
|
*/
|
|
|
|
bool GetCurrentDir(FString &path)
|
|
{
|
|
const unsigned kBufSize = MAX_PATH + 16;
|
|
path.Empty();
|
|
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
const DWORD len = ::GetCurrentDirectory(kBufSize, s);
|
|
if (len == 0 || len >= kBufSize)
|
|
return false;
|
|
s[kBufSize] = 0; // optional guard
|
|
path = fas2fs(s);
|
|
return true;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
DWORD len;
|
|
{
|
|
WCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetCurrentDirectoryW(kBufSize, s);
|
|
if (len == 0)
|
|
return false;
|
|
if (len < kBufSize)
|
|
{
|
|
s[kBufSize] = 0; // optional guard
|
|
path = us2fs(s);
|
|
return true;
|
|
}
|
|
}
|
|
UString temp;
|
|
const DWORD len2 = ::GetCurrentDirectoryW(len, temp.GetBuf(len));
|
|
if (len2 == 0)
|
|
return false;
|
|
temp.ReleaseBuf_CalcLen(len);
|
|
if (temp.Len() != len2 || len - 1 != len2)
|
|
{
|
|
/* it's unexpected case, if current dir of process
|
|
was changed between two function calls,
|
|
or some unexpected function implementation */
|
|
// SetLastError((DWORD)E_FAIL); // we can set some error code
|
|
return false;
|
|
}
|
|
path = us2fs(temp);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endif // _WIN32
|
|
#endif // UNDER_CE
|
|
|
|
|
|
bool GetFullPathAndSplit(CFSTR path, FString &resDirPrefix, FString &resFileName)
|
|
{
|
|
bool res = MyGetFullPathName(path, resDirPrefix);
|
|
if (!res)
|
|
resDirPrefix = path;
|
|
int pos = resDirPrefix.ReverseFind_PathSepar();
|
|
pos++;
|
|
resFileName = resDirPrefix.Ptr((unsigned)pos);
|
|
resDirPrefix.DeleteFrom((unsigned)pos);
|
|
return res;
|
|
}
|
|
|
|
bool GetOnlyDirPrefix(CFSTR path, FString &resDirPrefix)
|
|
{
|
|
FString resFileName;
|
|
return GetFullPathAndSplit(path, resDirPrefix, resFileName);
|
|
}
|
|
|
|
|
|
|
|
bool MyGetTempPath(FString &path)
|
|
{
|
|
#ifdef _WIN32
|
|
|
|
/*
|
|
new DOCs for GetTempPathW():
|
|
- The returned string ends with a backslash.
|
|
- The maximum possible return value is MAX_PATH+1 (261).
|
|
*/
|
|
|
|
const unsigned kBufSize = MAX_PATH + 16;
|
|
DWORD len;
|
|
#ifndef _UNICODE
|
|
if (!g_IsNT)
|
|
{
|
|
TCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetTempPath(kBufSize, s);
|
|
path = fas2fs(s);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
WCHAR s[kBufSize + 1];
|
|
s[0] = 0;
|
|
len = ::GetTempPathW(kBufSize, s);
|
|
path = us2fs(s);
|
|
}
|
|
/* win10: GetTempPathW() doesn't set backslash at the end of path,
|
|
if (buffer_size == len_of(path_with_backslash)).
|
|
So we normalize path here: */
|
|
NormalizeDirPathPrefix(path);
|
|
return (len != 0 && len < kBufSize);
|
|
|
|
#else // !_WIN32
|
|
|
|
// FIXME: improve that code
|
|
path = STRING_PATH_SEPARATOR "tmp";
|
|
const char *s;
|
|
if (NFind::DoesDirExist_FollowLink(path))
|
|
s = STRING_PATH_SEPARATOR "tmp" STRING_PATH_SEPARATOR;
|
|
else
|
|
s = "." STRING_PATH_SEPARATOR;
|
|
path = s;
|
|
return true;
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
bool CreateTempFile2(CFSTR prefix, bool addRandom, AString &postfix, NIO::COutFile *outFile)
|
|
{
|
|
UInt32 d =
|
|
#ifdef _WIN32
|
|
(GetTickCount() << 12) ^ (GetCurrentThreadId() << 14) ^ GetCurrentProcessId();
|
|
#else
|
|
(UInt32)(time(NULL) << 12) ^ ((UInt32)getppid() << 14) ^ (UInt32)(getpid());
|
|
#endif
|
|
|
|
for (unsigned i = 0; i < 100; i++)
|
|
{
|
|
postfix.Empty();
|
|
if (addRandom)
|
|
{
|
|
char s[16];
|
|
UInt32 val = d;
|
|
unsigned k;
|
|
for (k = 0; k < 8; k++)
|
|
{
|
|
const unsigned t = val & 0xF;
|
|
val >>= 4;
|
|
s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
|
|
}
|
|
s[k] = '\0';
|
|
if (outFile)
|
|
postfix.Add_Dot();
|
|
postfix += s;
|
|
UInt32 step = GetTickCount() + 2;
|
|
if (step == 0)
|
|
step = 1;
|
|
d += step;
|
|
}
|
|
addRandom = true;
|
|
if (outFile)
|
|
postfix += ".tmp";
|
|
FString path (prefix);
|
|
path += postfix;
|
|
if (NFind::DoesFileOrDirExist(path))
|
|
{
|
|
SetLastError(ERROR_ALREADY_EXISTS);
|
|
continue;
|
|
}
|
|
if (outFile)
|
|
{
|
|
if (outFile->Create(path, false))
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (CreateDir(path))
|
|
return true;
|
|
}
|
|
const DWORD error = GetLastError();
|
|
if (error != ERROR_FILE_EXISTS &&
|
|
error != ERROR_ALREADY_EXISTS)
|
|
break;
|
|
}
|
|
postfix.Empty();
|
|
return false;
|
|
}
|
|
|
|
bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
|
|
{
|
|
if (!Remove())
|
|
return false;
|
|
_path.Empty();
|
|
AString postfix;
|
|
if (!CreateTempFile2(prefix, false, postfix, outFile))
|
|
return false;
|
|
_path = prefix;
|
|
_path += postfix;
|
|
_mustBeDeleted = true;
|
|
return true;
|
|
}
|
|
|
|
bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
|
|
{
|
|
if (!Remove())
|
|
return false;
|
|
_path.Empty();
|
|
FString tempPath;
|
|
if (!MyGetTempPath(tempPath))
|
|
return false;
|
|
AString postfix;
|
|
tempPath += namePrefix;
|
|
if (!CreateTempFile2(tempPath, true, postfix, outFile))
|
|
return false;
|
|
_path = tempPath;
|
|
_path += postfix;
|
|
_mustBeDeleted = true;
|
|
return true;
|
|
}
|
|
|
|
bool CTempFile::Remove()
|
|
{
|
|
if (!_mustBeDeleted)
|
|
return true;
|
|
_mustBeDeleted = !DeleteFileAlways(_path);
|
|
return !_mustBeDeleted;
|
|
}
|
|
|
|
bool CTempFile::MoveTo(CFSTR name, bool deleteDestBefore)
|
|
{
|
|
// DWORD attrib = 0;
|
|
if (deleteDestBefore)
|
|
{
|
|
if (NFind::DoesFileExist_Raw(name))
|
|
{
|
|
// attrib = NFind::GetFileAttrib(name);
|
|
if (!DeleteFileAlways(name))
|
|
return false;
|
|
}
|
|
}
|
|
DisableDeleting();
|
|
return MyMoveFile(_path, name);
|
|
|
|
/*
|
|
if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_READONLY))
|
|
{
|
|
DWORD attrib2 = NFind::GetFileAttrib(name);
|
|
if (attrib2 != INVALID_FILE_ATTRIBUTES)
|
|
SetFileAttrib(name, attrib2 | FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
*/
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
bool CTempDir::Create(CFSTR prefix)
|
|
{
|
|
if (!Remove())
|
|
return false;
|
|
_path.Empty();
|
|
FString tempPath;
|
|
if (!MyGetTempPath(tempPath))
|
|
return false;
|
|
tempPath += prefix;
|
|
AString postfix;
|
|
if (!CreateTempFile2(tempPath, true, postfix, NULL))
|
|
return false;
|
|
_path = tempPath;
|
|
_path += postfix;
|
|
_mustBeDeleted = true;
|
|
return true;
|
|
}
|
|
|
|
bool CTempDir::Remove()
|
|
{
|
|
if (!_mustBeDeleted)
|
|
return true;
|
|
_mustBeDeleted = !RemoveDirWithSubItems(_path);
|
|
return !_mustBeDeleted;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
bool RemoveDir(CFSTR path)
|
|
{
|
|
return (rmdir(path) == 0);
|
|
}
|
|
|
|
|
|
static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile)
|
|
{
|
|
NWindows::NFile::NIO::COutFile outFile;
|
|
if (!outFile.Create(newFile, false))
|
|
return FALSE;
|
|
|
|
NWindows::NFile::NIO::CInFile inFile;
|
|
if (!inFile.Open(oldFile))
|
|
return FALSE;
|
|
|
|
char buf[1 << 14];
|
|
|
|
for (;;)
|
|
{
|
|
const ssize_t num = inFile.read_part(buf, sizeof(buf));
|
|
if (num == 0)
|
|
return TRUE;
|
|
if (num < 0)
|
|
return FALSE;
|
|
size_t processed;
|
|
const ssize_t num2 = outFile.write_full(buf, (size_t)num, processed);
|
|
if (num2 != num || processed != (size_t)num)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
bool MyMoveFile(CFSTR oldFile, CFSTR newFile)
|
|
{
|
|
int res = rename(oldFile, newFile);
|
|
if (res == 0)
|
|
return true;
|
|
if (errno != EXDEV) // (oldFile and newFile are not on the same mounted filesystem)
|
|
return false;
|
|
|
|
if (My_CopyFile(oldFile, newFile) == FALSE)
|
|
return false;
|
|
|
|
struct stat info_file;
|
|
res = stat(oldFile, &info_file);
|
|
if (res != 0)
|
|
return false;
|
|
|
|
/*
|
|
ret = chmod(dst,info_file.st_mode & g_umask.mask);
|
|
*/
|
|
return (unlink(oldFile) == 0);
|
|
}
|
|
|
|
|
|
bool CreateDir(CFSTR path)
|
|
{
|
|
return (mkdir(path, 0777) == 0); // change it
|
|
}
|
|
|
|
static bool CreateDir2(CFSTR path)
|
|
{
|
|
return (mkdir(path, 0777) == 0); // change it
|
|
}
|
|
|
|
|
|
bool DeleteFileAlways(CFSTR path)
|
|
{
|
|
return (remove(path) == 0);
|
|
}
|
|
|
|
bool SetCurrentDir(CFSTR path)
|
|
{
|
|
return (chdir(path) == 0);
|
|
}
|
|
|
|
|
|
bool GetCurrentDir(FString &path)
|
|
{
|
|
path.Empty();
|
|
|
|
#define MY_PATH_MAX PATH_MAX
|
|
// #define MY_PATH_MAX 1024
|
|
|
|
char s[MY_PATH_MAX + 1];
|
|
char *res = getcwd(s, MY_PATH_MAX);
|
|
if (res)
|
|
{
|
|
path = fas2fs(s);
|
|
return true;
|
|
}
|
|
{
|
|
// if (errno != ERANGE) return false;
|
|
#if defined(__GLIBC__) || defined(__APPLE__)
|
|
/* As an extension to the POSIX.1-2001 standard, glibc's getcwd()
|
|
allocates the buffer dynamically using malloc(3) if buf is NULL. */
|
|
res = getcwd(NULL, 0);
|
|
if (res)
|
|
{
|
|
path = fas2fs(res);
|
|
::free(res);
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// #undef UTIME_OMIT // to debug
|
|
|
|
#ifndef UTIME_OMIT
|
|
/* we can define UTIME_OMIT for debian and another systems.
|
|
Is it OK to define UTIME_OMIT to -2 here, if UTIME_OMIT is not defined? */
|
|
// #define UTIME_OMIT -2
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime)
|
|
{
|
|
// need testing
|
|
/*
|
|
struct utimbuf buf;
|
|
struct stat st;
|
|
UNUSED_VAR(cTime)
|
|
|
|
printf("\nstat = %s\n", path);
|
|
int ret = stat(path, &st);
|
|
|
|
if (ret == 0)
|
|
{
|
|
buf.actime = st.st_atime;
|
|
buf.modtime = st.st_mtime;
|
|
}
|
|
else
|
|
{
|
|
time_t cur_time = time(0);
|
|
buf.actime = cur_time;
|
|
buf.modtime = cur_time;
|
|
}
|
|
|
|
if (aTime)
|
|
{
|
|
UInt32 ut;
|
|
if (NTime::FileTimeToUnixTime(*aTime, ut))
|
|
buf.actime = ut;
|
|
}
|
|
|
|
if (mTime)
|
|
{
|
|
UInt32 ut;
|
|
if (NTime::FileTimeToUnixTime(*mTime, ut))
|
|
buf.modtime = ut;
|
|
}
|
|
|
|
return utime(path, &buf) == 0;
|
|
*/
|
|
|
|
// if (!aTime && !mTime) return true;
|
|
|
|
struct timespec times[2];
|
|
UNUSED_VAR(cTime)
|
|
|
|
bool needChange;
|
|
needChange = FiTime_To_timespec(aTime, times[0]);
|
|
needChange |= FiTime_To_timespec(mTime, times[1]);
|
|
|
|
/*
|
|
if (mTime)
|
|
{
|
|
printf("\n time = %ld.%9ld\n", mTime->tv_sec, mTime->tv_nsec);
|
|
}
|
|
*/
|
|
|
|
if (!needChange)
|
|
return true;
|
|
const int flags = 0; // follow link
|
|
// = AT_SYMLINK_NOFOLLOW; // don't follow link
|
|
return utimensat(AT_FDCWD, path, times, flags) == 0;
|
|
}
|
|
|
|
|
|
|
|
struct C_umask
|
|
{
|
|
mode_t mask;
|
|
|
|
C_umask()
|
|
{
|
|
/* by security reasons we restrict attributes according
|
|
with process's file mode creation mask (umask) */
|
|
const mode_t um = umask(0); // octal :0022 is expected
|
|
mask = 0777 & (~um); // octal: 0755 is expected
|
|
umask(um); // restore the umask
|
|
// printf("\n umask = 0%03o mask = 0%03o\n", um, mask);
|
|
|
|
// mask = 0777; // debug we can disable the restriction:
|
|
}
|
|
};
|
|
|
|
static C_umask g_umask;
|
|
|
|
// #define PRF(x) x;
|
|
#define PRF(x)
|
|
|
|
#define TRACE_SetFileAttrib(msg) \
|
|
PRF(printf("\nSetFileAttrib(%s, %x) : %s\n", (const char *)path, attrib, msg);)
|
|
|
|
#define TRACE_chmod(s, mode) \
|
|
PRF(printf("\n chmod(%s, %o)\n", (const char *)path, (unsigned)(mode));)
|
|
|
|
int my_chown(CFSTR path, uid_t owner, gid_t group)
|
|
{
|
|
return chown(path, owner, group);
|
|
}
|
|
|
|
bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib)
|
|
{
|
|
TRACE_SetFileAttrib("")
|
|
|
|
struct stat st;
|
|
|
|
bool use_lstat = true;
|
|
if (use_lstat)
|
|
{
|
|
if (lstat(path, &st) != 0)
|
|
{
|
|
TRACE_SetFileAttrib("bad lstat()")
|
|
return false;
|
|
}
|
|
// TRACE_chmod("lstat", st.st_mode);
|
|
}
|
|
else
|
|
{
|
|
if (stat(path, &st) != 0)
|
|
{
|
|
TRACE_SetFileAttrib("bad stat()")
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (attrib & FILE_ATTRIBUTE_UNIX_EXTENSION)
|
|
{
|
|
TRACE_SetFileAttrib("attrib & FILE_ATTRIBUTE_UNIX_EXTENSION")
|
|
st.st_mode = attrib >> 16;
|
|
if (S_ISDIR(st.st_mode))
|
|
{
|
|
// user/7z must be able to create files in this directory
|
|
st.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR);
|
|
}
|
|
else if (!S_ISREG(st.st_mode))
|
|
return true;
|
|
}
|
|
else if (S_ISLNK(st.st_mode))
|
|
{
|
|
/* for most systems: permissions for symlinks are fixed to rwxrwxrwx.
|
|
so we don't need chmod() for symlinks. */
|
|
return true;
|
|
// SetLastError(ENOSYS);
|
|
// return false;
|
|
}
|
|
else
|
|
{
|
|
TRACE_SetFileAttrib("Only Windows Attributes")
|
|
// Only Windows Attributes
|
|
if (S_ISDIR(st.st_mode)
|
|
|| (attrib & FILE_ATTRIBUTE_READONLY) == 0)
|
|
return true;
|
|
st.st_mode &= ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); // octal: ~0222; // disable write permissions
|
|
}
|
|
|
|
int res;
|
|
/*
|
|
if (S_ISLNK(st.st_mode))
|
|
{
|
|
printf("\nfchmodat()\n");
|
|
TRACE_chmod(path, (st.st_mode) & g_umask.mask)
|
|
// AT_SYMLINK_NOFOLLOW is not implemted still in Linux.
|
|
res = fchmodat(AT_FDCWD, path, (st.st_mode) & g_umask.mask,
|
|
S_ISLNK(st.st_mode) ? AT_SYMLINK_NOFOLLOW : 0);
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
TRACE_chmod(path, (st.st_mode) & g_umask.mask)
|
|
res = chmod(path, (st.st_mode) & g_umask.mask);
|
|
}
|
|
// TRACE_SetFileAttrib("End")
|
|
return (res == 0);
|
|
}
|
|
|
|
|
|
bool MyCreateHardLink(CFSTR newFileName, CFSTR existFileName)
|
|
{
|
|
PRF(printf("\nhard link() %s -> %s\n", newFileName, existFileName);)
|
|
return (link(existFileName, newFileName) == 0);
|
|
}
|
|
|
|
#endif // !_WIN32
|
|
|
|
// #endif
|
|
|
|
}}}
|