From f19f813537c7aea1c20749c914e756b54a9c3cf5 Mon Sep 17 00:00:00 2001
From: Igor Pavlov <87184205+ip7z@users.noreply.github.com>
Date: Mon, 27 Dec 2021 00:00:00 +0000
Subject: '21.07'

---
 CPP/Windows/FileDir.cpp | 1129 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1129 insertions(+)
 create mode 100644 CPP/Windows/FileDir.cpp

(limited to 'CPP/Windows/FileDir.cpp')

diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp
new file mode 100644
index 0000000..8a33fc8
--- /dev/null
+++ b/CPP/Windows/FileDir.cpp
@@ -0,0 +1,1129 @@
+// 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"
+#include "TimeUtils.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;
+
+namespace NWindows {
+namespace NFile {
+namespace NDir {
+
+#ifdef _WIN32
+
+#ifndef UNDER_CE
+
+bool GetWindowsDir(FString &path)
+{
+  UINT needLength;
+  #ifndef _UNICODE
+  if (!g_IsNT)
+  {
+    TCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetWindowsDirectory(s, MAX_PATH + 1);
+    path = fas2fs(s);
+  }
+  else
+  #endif
+  {
+    WCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetWindowsDirectoryW(s, MAX_PATH + 1);
+    path = us2fs(s);
+  }
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+
+bool GetSystemDir(FString &path)
+{
+  UINT needLength;
+  #ifndef _UNICODE
+  if (!g_IsNT)
+  {
+    TCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetSystemDirectory(s, MAX_PATH + 1);
+    path = fas2fs(s);
+  }
+  else
+  #endif
+  {
+    WCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetSystemDirectoryW(s, MAX_PATH + 1);
+    path = us2fs(s);
+  }
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+#endif // UNDER_CE
+
+
+bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *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 WIN_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 WIN_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 WIN_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 WIN_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
+  {
+    Func_CreateHardLinkW my_CreateHardLinkW = (Func_CreateHardLinkW)
+        (void *)::GetProcAddress(::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 WIN_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 WIN_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 WIN_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
+  
+  {
+    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 WIN_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
+
+bool SetCurrentDir(CFSTR path)
+{
+  // SetCurrentDirectory doesn't support \\?\ prefix
+  #ifndef _UNICODE
+  if (!g_IsNT)
+  {
+    return BOOLToBool(::SetCurrentDirectory(fs2fas(path)));
+  }
+  else
+  #endif
+  {
+    return BOOLToBool(::SetCurrentDirectoryW(fs2us(path)));
+  }
+}
+
+
+bool GetCurrentDir(FString &path)
+{
+  path.Empty();
+
+  DWORD needLength;
+  #ifndef _UNICODE
+  if (!g_IsNT)
+  {
+    TCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetCurrentDirectory(MAX_PATH + 1, s);
+    path = fas2fs(s);
+  }
+  else
+  #endif
+  {
+    WCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, s);
+    path = us2fs(s);
+  }
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+
+#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
+  path.Empty();
+  DWORD needLength;
+  #ifndef _UNICODE
+  if (!g_IsNT)
+  {
+    TCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetTempPath(MAX_PATH + 1, s);
+    path = fas2fs(s);
+  }
+  else
+  #endif
+  {
+    WCHAR s[MAX_PATH + 2];
+    s[0] = 0;
+    needLength = ::GetTempPathW(MAX_PATH + 1, s);;
+    path = us2fs(s);
+  }
+  return (needLength > 0 && needLength <= MAX_PATH);
+
+  #else
+  
+  // FIXME: improve that code
+  path = "/tmp/";
+  if (!NFind::DoesDirExist_FollowLink(path))
+    path = "./";
+  return true;
+  #endif
+}
+
+
+static bool CreateTempFile(CFSTR prefix, bool addRandom, FString &path, 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++)
+  {
+    path = prefix;
+    if (addRandom)
+    {
+      char s[16];
+      UInt32 val = d;
+      unsigned k;
+      for (k = 0; k < 8; k++)
+      {
+        unsigned t = val & 0xF;
+        val >>= 4;
+        s[k] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));
+      }
+      s[k] = '\0';
+      if (outFile)
+        path += '.';
+      path += s;
+      UInt32 step = GetTickCount() + 2;
+      if (step == 0)
+        step = 1;
+      d += step;
+    }
+    addRandom = true;
+    if (outFile)
+      path += ".tmp";
+    if (NFind::DoesFileOrDirExist(path))
+    {
+      SetLastError(ERROR_ALREADY_EXISTS);
+      continue;
+    }
+    if (outFile)
+    {
+      if (outFile->Create(path, false))
+        return true;
+    }
+    else
+    {
+      if (CreateDir(path))
+        return true;
+    }
+    DWORD error = GetLastError();
+    if (error != ERROR_FILE_EXISTS &&
+        error != ERROR_ALREADY_EXISTS)
+      break;
+  }
+  path.Empty();
+  return false;
+}
+
+bool CTempFile::Create(CFSTR prefix, NIO::COutFile *outFile)
+{
+  if (!Remove())
+    return false;
+  if (!CreateTempFile(prefix, false, _path, outFile))
+    return false;
+  _mustBeDeleted = true;
+  return true;
+}
+
+bool CTempFile::CreateRandomInTempFolder(CFSTR namePrefix, NIO::COutFile *outFile)
+{
+  if (!Remove())
+    return false;
+  FString tempPath;
+  if (!MyGetTempPath(tempPath))
+    return false;
+  if (!CreateTempFile(tempPath + namePrefix, true, _path, outFile))
+    return false;
+  _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;
+  FString tempPath;
+  if (!MyGetTempPath(tempPath))
+    return false;
+  if (!CreateTempFile(tempPath + prefix, true, _path, NULL))
+    return false;
+  _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
+
+static bool FILETME_To_timespec(const FILETIME *ft, timespec &ts)
+{
+  if (ft)
+  {
+    const Int64 sec = NTime::FileTimeToUnixTime64(*ft);
+    // time_t is long
+    const time_t sec2 = (time_t)sec;
+    if (sec2 == sec)
+    {
+      ts.tv_sec = sec2;
+      const UInt64 winTime = (((UInt64)ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
+      ts.tv_nsec = (long)((winTime % 10000000) * 100);
+      return true;
+    }
+  }
+  // else
+  {
+    ts.tv_sec = 0;
+    ts.tv_nsec =
+    #ifdef UTIME_OMIT
+      UTIME_OMIT; // keep old timesptamp
+    #else
+      // UTIME_NOW; // set to the current time
+      0;
+    #endif
+    return false;
+  }
+}
+
+
+
+
+bool SetDirTime(CFSTR path, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *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  = FILETME_To_timespec(aTime, times[0]);
+  needChange |= FILETME_To_timespec(mTime, times[1]);
+
+  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)));
+
+
+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
+
+}}}
-- 
cgit v1.2.3-55-g6feb