From 0766b733fe3e06dd2a7f9a3cfbf2108ac73abd17 Mon Sep 17 00:00:00 2001 From: Igor Pavlov <87184205+ip7z@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:00:00 +0000 Subject: 26.03 --- CPP/Windows/FileDir.cpp | 102 ++++++++++++-------- CPP/Windows/FileDir.h | 1 + CPP/Windows/FileIO.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++++++-- CPP/Windows/FileIO.h | 45 ++++++++- 4 files changed, 349 insertions(+), 47 deletions(-) (limited to 'CPP/Windows') diff --git a/CPP/Windows/FileDir.cpp b/CPP/Windows/FileDir.cpp index ad0d8c9..bf77c15 100644 --- a/CPP/Windows/FileDir.cpp +++ b/CPP/Windows/FileDir.cpp @@ -34,7 +34,9 @@ using namespace NName; #ifndef _WIN32 -static bool FiTime_To_timespec(const CFiTime *ft, timespec &ts) +extern +bool FiTime_To_timespec(const CFiTime *ft, timespec &ts); +bool FiTime_To_timespec(const CFiTime *ft, timespec &ts) { if (ft) { @@ -51,7 +53,7 @@ static bool FiTime_To_timespec(const CFiTime *ft, timespec &ts) ts.tv_sec = 0; ts.tv_nsec = #ifdef UTIME_OMIT - UTIME_OMIT; // -2 keep old timesptamp + UTIME_OMIT; // -2 keep old timestamp #else // UTIME_NOW; -1 // set to the current time 0; @@ -170,6 +172,10 @@ bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, con bool SetFileAttrib(CFSTR path, DWORD attrib) { + /* win10: + if (attrib == 0), it sets (FILE_ATTRIBUTE_NORMAL) attribute + (FILE_ATTRIBUTE_DIRECTORY and some another attributes are ignored for files). + */ #ifndef _UNICODE if (!g_IsNT) { @@ -1078,7 +1084,7 @@ static BOOL My_CopyFile(CFSTR oldFile, CFSTR newFile, ICopyFileProgress *progres } // There is file IO error or process was interrupted by user. // We close output file and delete it. - // DeleteFileAlways doesn't change errno (if successed), but we restore errno. + // DeleteFileAlways doesn't change errno (if succeed), but we restore errno. const int errno_save = errno; DeleteFileAlways(newFile); errno = errno_save; @@ -1240,26 +1246,34 @@ bool SetLinkFileTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, con } -struct C_umask +C_umask::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: - } -}; +/* + For security purposes, we restrict the file (mode) attributes + using the process's file mode creation mask (umask). + System's umask is used by open(), mkdir(), and other system calls + that create files to modify the permissions placed on newly + created files or directories. + We use (g_umask.mask) for any function that changes the file's access + mode but is not affected by the system's umask. + We use additional mask restiction 0777 for security purposes. + So we don't create the following mode bits for files and directories: + S_ISUID 04000 set-user-ID bit + S_ISGID 02000 set-group-ID bit + S_ISVTX 01000 sticky bit + system's open(), mkdir() also can have similar 0777 restiction for some cases. +*/ + const mode_t um = umask(0); // um = 0022 (octal) is expected + mask = ~um + & 0777; // 0777 is our additional mode restruction : is secure + // & 07777; // for debug : 07777 to support all mode bits : is not secure + // mask = 07777; // for debug : to restore all mode bits + umask(um); // restore original umask that was changed by umask(0) in code above +} -static C_umask g_umask; +C_umask g_umask; -// #define PRF(x) x; +// #define PRF(x) x #define PRF(x) #define TRACE_SetFileAttrib(msg) \ @@ -1273,12 +1287,17 @@ int my_chown(CFSTR path, uid_t owner, gid_t group) return chown(path, owner, group); } +int my_chown_Link(CFSTR path, uid_t owner, gid_t group) +{ + return lchown(path, owner, group); + // return fchownat(AT_FDCWD, path, owner, group, AT_SYMLINK_NOFOLLOW); +} + bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib) { TRACE_SetFileAttrib("") - + mode_t mode; struct stat st; - bool use_lstat = true; if (use_lstat) { @@ -1297,20 +1316,26 @@ bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib) return false; } } - + mode = st.st_mode; + if (attrib & FILE_ATTRIBUTE_UNIX_EXTENSION) { TRACE_SetFileAttrib("attrib & FILE_ATTRIBUTE_UNIX_EXTENSION") - st.st_mode = attrib >> 16; - if (S_ISDIR(st.st_mode)) + mode = attrib >> 16; + if (S_ISDIR(mode)) { + if (!S_ISDIR(st.st_mode)) + return true; // user/7z must be able to create files in this directory - st.st_mode |= (S_IRUSR | S_IWUSR | S_IXUSR); + mode |= (S_IRUSR | S_IWUSR | S_IXUSR); + } + else + { + if (!S_ISREG(mode) || !S_ISREG(st.st_mode)) + return true; } - else if (!S_ISREG(st.st_mode)) - return true; } - else if (S_ISLNK(st.st_mode)) + else if (S_ISLNK(mode)) { /* for most systems: permissions for symlinks are fixed to rwxrwxrwx. so we don't need chmod() for symlinks. */ @@ -1322,27 +1347,28 @@ bool SetFileAttrib_PosixHighDetect(CFSTR path, DWORD attrib) { TRACE_SetFileAttrib("Only Windows Attributes") // Only Windows Attributes - if (S_ISDIR(st.st_mode) + if (S_ISDIR(mode) || (attrib & FILE_ATTRIBUTE_READONLY) == 0) return true; - st.st_mode &= ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); // octal: ~0222; // disable write permissions + mode &= ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); // octal: ~0222; // disable write permissions } int res; + mode &= g_umask.mask; /* - if (S_ISLNK(st.st_mode)) + if (S_ISLNK(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); + TRACE_chmod(path, (mode)) + // AT_SYMLINK_NOFOLLOW is not implemented still in Linux. + res = fchmodat(AT_FDCWD, path, (mode), + S_ISLNK(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_chmod(path, mode) + res = chmod(path, mode); } // TRACE_SetFileAttrib("End") return (res == 0); diff --git a/CPP/Windows/FileDir.h b/CPP/Windows/FileDir.h index 9ba98fc..e8d9d31 100644 --- a/CPP/Windows/FileDir.h +++ b/CPP/Windows/FileDir.h @@ -46,6 +46,7 @@ bool SetFileAttrib(CFSTR path, DWORD attrib); #else int my_chown(CFSTR path, uid_t owner, gid_t group); +int my_chown_Link(CFSTR path, uid_t owner, gid_t group); #endif diff --git a/CPP/Windows/FileIO.cpp b/CPP/Windows/FileIO.cpp index dc4de14..8be06fc 100644 --- a/CPP/Windows/FileIO.cpp +++ b/CPP/Windows/FileIO.cpp @@ -6,6 +6,29 @@ #include "../../C/Alloc.h" #endif +#ifdef _WIN32 +#ifdef __MINGW32_VERSION +// #if !defined(_MSC_VER) && (__GNUC__) && (__GNUC__ < 10) +// for old mingw +#include +#else +#ifndef Z7_OLD_WIN_SDK + #if !defined(_M_IA64) + #include + #endif +#else +typedef LONG NTSTATUS; +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; +#endif +#endif +#endif // _WIN32 + // #include /* @@ -33,6 +56,48 @@ HRESULT GetLastError_noZero_HRESULT() extern bool g_IsNT; #endif +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500) && !defined(_M_IA64) +#define Z7_WIN_NTSTATUS NTSTATUS +#define Z7_WIN_IO_STATUS_BLOCK IO_STATUS_BLOCK +#else +typedef LONG Z7_WIN_NTSTATUS; +typedef struct +{ + union + { + Z7_WIN_NTSTATUS Status; + PVOID Pointer; + } DUMMYUNIONNAME; + ULONG_PTR Information; +} Z7_WIN_IO_STATUS_BLOCK; +#endif + +typedef Z7_WIN_NTSTATUS (WINAPI *Func_NtSetInformationFile)( + HANDLE FileHandle, + Z7_WIN_IO_STATUS_BLOCK *IoStatusBlock, + PVOID FileInformation, + ULONG Length, + Z7_WIN_FILE_INFORMATION_CLASS FileInformationClass); +// NTAPI +typedef ULONG (WINAPI *Func_RtlNtStatusToDosError)(Z7_WIN_NTSTATUS Status); + +Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION +static Func_NtSetInformationFile g_NtSetInformationFile; +static Func_RtlNtStatusToDosError g_RtlNtStatusToDosError; +static struct C_Init_NtSetInformationFile +{ + C_Init_NtSetInformationFile() + { + const HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll"); + g_NtSetInformationFile = Z7_GET_PROC_ADDRESS( + Func_NtSetInformationFile, ntdll, + "NtSetInformationFile"); + g_RtlNtStatusToDosError = Z7_GET_PROC_ADDRESS( + Func_RtlNtStatusToDosError, ntdll, + "RtlNtStatusToDosError"); + } +} g_C_Init_NtSetInformationFile; + using namespace NWindows; using namespace NFile; using namespace NName; @@ -353,7 +418,7 @@ void CInFile::CalcDeviceSize(CFSTR s) WinXP 64-bit: HDD \\.\PhysicalDrive0 (MBR): - GetPartitionInfo == GeometryEx : corrrect size? (includes tail) + GetPartitionInfo == GeometryEx : correct size? (includes tail) Geometry : smaller than GeometryEx (no tail, maybe correct too?) MyGetDiskFreeSpace : FAIL Size correction is slow and block size (kClusterSize) must be small? @@ -365,8 +430,8 @@ void CInFile::CalcDeviceSize(CFSTR s) CD-ROM drive (ISO): MyGetDiskFreeSpace : correct size. Same size can be calculated after correction - Geometry == CdRomGeometry : smaller than corrrect size - GetPartitionInfo == GeometryEx : larger than corrrect size + Geometry == CdRomGeometry : smaller than correct size + GetPartitionInfo == GeometryEx : larger than correct size Floppy \\.\a: (FAT): Geometry : correct size. @@ -539,6 +604,70 @@ bool COutFile::Open_Disposition(CFSTR fileName, DWORD creationDisposition) bool COutFile::Create_ALWAYS_with_Attribs(CFSTR fileName, DWORD flagsAndAttributes) { return Open(fileName, FILE_SHARE_READ, CREATE_ALWAYS, flagsAndAttributes); } +DWORD CFileBase::Call_NtSetInformationFile_return_WinError( + void *data, ULONG len, Z7_WIN_FILE_INFORMATION_CLASS fileInformationClass) const +{ + if (!g_NtSetInformationFile) + return ERROR_PROC_NOT_FOUND; + Z7_WIN_IO_STATUS_BLOCK ioStatus; + Z7_memset_0_VAR(ioStatus); // optional + const Z7_WIN_NTSTATUS status = g_NtSetInformationFile(_handle, + &ioStatus, data, len, fileInformationClass); + if (status == 0) // MY_STATUS_SUCCESS + return 0; + if (g_RtlNtStatusToDosError) + { + const ULONG res = g_RtlNtStatusToDosError(status); + if (res != ERROR_MR_MID_NOT_FOUND) + return res; + } + return 1; +} + + +#define SET_TIME_FIELD_IF_DEFINED(dest, src) \ +{ if (src) { \ + dest.LowPart = (src)->dwLowDateTime; \ + dest.HighPart = (LONG)(src)->dwHighDateTime; \ + } \ +} + +bool COutFile::Set_Time_and_WinAttrib(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime, DWORD attrib) throw() +{ +#ifdef _WIN32 + // similar to GetAttrib_PosixHighDetect(attrib) + if (attrib & 0xF0000000) + attrib &= 0x3FFF; +#endif + + Z7_WIN_FILE_BASIC_INFORMATION fbi; + Z7_memset_0_VAR(fbi); + SET_TIME_FIELD_IF_DEFINED (fbi.CreationTime, cTime) + SET_TIME_FIELD_IF_DEFINED (fbi.LastAccessTime, aTime) + SET_TIME_FIELD_IF_DEFINED (fbi.LastWriteTime, mTime) + // fbi.ChangeTime.QuadPart = 0; + /* + if (attrib == 0) + { win10: NtSetInformationFile() doesn't change file attribute + if (attrib & FILE_ATTRIBUTE_DIRECTORY) + { win10: NtSetInformationFile() returns (STATUS_INVALID_PARAMETER) } + we provide similar attribute processing as + SetFileAttributes(): + - we ignore (FILE_ATTRIBUTE_DIRECTORY) + - we write (FILE_ATTRIBUTE_NORMAL) instead of (0) + */ + attrib &= ~(DWORD)FILE_ATTRIBUTE_DIRECTORY; + if (attrib == 0) + attrib = FILE_ATTRIBUTE_NORMAL; + fbi.FileAttributes = attrib; + const DWORD wres = Call_NtSetInformationFile_return_WinError( + &fbi, sizeof(fbi), Z7_WIN_FileBasicInformation); + if (wres == 0) + return true; + SetLastError(wres); + return false; +} + bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime) throw() { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); } @@ -628,10 +757,16 @@ bool COutFile::SetLength_KeepPosition(UInt64 length) throw() #include #include +extern +bool FiTime_To_timespec(const CFiTime *ft, timespec &ts); + namespace NWindows { namespace NFile { namespace NDir { + +extern C_umask g_umask; + bool SetDirTime(CFSTR path, const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime); } @@ -644,6 +779,34 @@ bool CFileBase::OpenBinary(const char *name, int flags, mode_t mode) #endif Close(); + /* + The mode argument specifies the file mode bits to be + applied when a new file is created. If neither O_CREAT nor + O_TMPFILE is specified in flags, then mode is ignored (and + can thus be specified as 0, or simply omitted). + + The effective mode is modified by the process's umask in + the usual way: in the absence of a default ACL, the mode of + the created file is (mode & ~umask). + + Note that mode applies only to future accesses of the newly + created file; the open() call that creates a read-only file + may well return a read/write file descriptor. + + POSIX: if other bits (~0777) are set in mode : the effect is unspecified + Linux: + S_ISUID 04000 set-user-ID bit + S_ISGID 02000 set-group-ID bit + S_ISVTX 01000 sticky bit + + S_ISVTX can be set, but it's useless for file in modern linux. + by security reasons: + open() ignores S_ISUID + open() ignores S_ISGID, if S_IXGRP is set. + */ + // mode |= S_ISUID | S_ISGID | S_ISVTX; // for debug + // printf("\n open() mode=%o\n", (unsigned)(mode)); + _handle = ::open(name, flags, mode); return _handle != -1; @@ -740,7 +903,7 @@ bool CFileBase::SeekToBegin() const throw() bool CInFile::Open(const char *name) { - return CFileBase::OpenBinary(name, O_RDONLY); + return CFileBase::OpenBinary(name, O_RDONLY, k_OutFile_mode_default); } bool CInFile::OpenShared(const char *name, bool) @@ -903,11 +1066,64 @@ bool COutFile::SetLength(UInt64 length) throw() return (iret == 0); } +// #define PRF(x) x +#define PRF(x) + bool COutFile::Close() { - const bool res = CFileBase::Close(); - if (!res) - return res; + bool res = true; + if (_handle == -1) + return true; + { + // bool res2 = true; + if (mode_for_Close_defined) + { + PRF(printf("\n COutFile::Close() mode=%o\n", (unsigned)(mode_for_Close));) + // fchmod() function ignores the file creation mask set by umask() + // so we can restore any of (07777) mode bits + // mode_for_Close |= (0xffff << 12); // for debug + if (fchmod(_handle, mode_for_Close) == 0) + { + mode_for_Close_defined = false; + PRF(printf("\n COutFile::Close() fchmod() == 0\n");) + } + // else res2 = false; + } + { + if (MTime_defined || ATime_defined) + { + struct timespec times[2]; + FiTime_To_timespec(ATime_defined ? &ATime : NULL, times[0]); + FiTime_To_timespec(MTime_defined ? &MTime : NULL, times[1]); + // futimens : POSIX.1-2008 + if (futimens(_handle, times) == 0) + { + PRF(printf("\n futimens() OK \n");) + ATime_defined = false; + MTime_defined = false; + } + else + { + PRF(printf("\n futimens() ERROR: %d=%s\n", errno, strerror(errno));) + } + } + } + res = CFileBase::Close(); + if (!res) + return res; + // res = res2; + } + if (mode_for_Close_defined) + { + // chmod() ignores the file creation mask set by umask() + // so we can restore any of (07777) mode bits + // const int res_chmod = + chmod(Path, mode_for_Close); + mode_for_Close_defined = false; + // if (res_chmod != 0 && res == true) res = false; + // PRF(printf("\n COutFile::Close() chmod res=%d\n", (int)res_chmod);) + } + if (CTime_defined || ATime_defined || MTime_defined) { /* bool res2 = */ NWindows::NFile::NDir::SetDirTime(Path, @@ -942,6 +1158,24 @@ bool COutFile::SetTime(const CFiTime *cTime, const CFiTime *aTime, const CFiTime */ } +bool COutFile::Set_Time_and_WinAttrib( + const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD attrib) throw() +{ + PRF(printf("\n Set_Time_and_WinAttrib() mode=%o\n", (unsigned)(attrib >> 16));) + SetTime(cTime, aTime, mTime); + // we will use fchmod() or chmod() later in Close() fucntion. + // fchmod() or chmod() are not limited by system umask. + // but for security reasons we use the system mask (g_umask.mask) derived from umask(). + mode_t mode = k_OutFile_mode_default; // 0666 + if (attrib & FILE_ATTRIBUTE_UNIX_EXTENSION) + mode = (attrib >> 16); + else if (attrib & FILE_ATTRIBUTE_READONLY) + mode &= ~(mode_t)(S_IWUSR | S_IWGRP | S_IWOTH); // octal: ~0222; // disable write permissions + mode_for_Close = mode & NDir::g_umask.mask; + mode_for_Close_defined = true; + return true; +} + bool COutFile::SetMTime(const CFiTime *mTime) throw() { if (mTime) { MTime = *mTime; MTime_defined = true; } else MTime_defined = false; diff --git a/CPP/Windows/FileIO.h b/CPP/Windows/FileIO.h index a202df5..9c32137 100644 --- a/CPP/Windows/FileIO.h +++ b/CPP/Windows/FileIO.h @@ -19,6 +19,27 @@ #include #endif +typedef enum +{ + Z7_WIN_FileDirectoryInformation = 1, + Z7_WIN_FileFullDirectoryInformation, + Z7_WIN_FileBothDirectoryInformation, + Z7_WIN_FileBasicInformation, + Z7_WIN_FileRenameInformation = 10 +} Z7_WIN_FILE_INFORMATION_CLASS; + +// FILE_BASIC_INFORMATION / FILE_BASIC_INFO +typedef struct +{ + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + ULONG FileAttributes; + ULONG Reserved; // it's expected dummy variable for alignment : optional +} +Z7_WIN_FILE_BASIC_INFORMATION; + #else #include @@ -205,6 +226,9 @@ public: return false; return file.GetFileInformation(info); } + + DWORD Call_NtSetInformationFile_return_WinError(void *data, ULONG len, + Z7_WIN_FILE_INFORMATION_CLASS fileInformationClass) const; }; #ifndef UNDER_CE @@ -323,6 +347,8 @@ public: bool Create_ALWAYS_with_Attribs(CFSTR fileName, DWORD flagsAndAttributes); + bool Set_Time_and_WinAttrib(const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD attrib) throw(); + bool SetTime(const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) throw(); bool SetMTime(const CFiTime *mTime) throw(); bool WritePart(const void *data, UInt32 size, UInt32 &processedSize) throw(); @@ -338,6 +364,15 @@ public: #else // _WIN32 +namespace NDir { +struct C_umask +{ + mode_t mask; + C_umask(); +}; +extern C_umask g_umask; +} + namespace NIO { bool GetReparseData(CFSTR path, CByteBuffer &reparseData); @@ -347,6 +382,7 @@ bool GetReparseData(CFSTR path, CByteBuffer &reparseData); bool SetSymLink(CFSTR from, CFSTR to); bool SetSymLink_UString(CFSTR from, const UString &to); +const mode_t k_OutFile_mode_default = 0666; class CFileBase { @@ -359,7 +395,7 @@ protected: UInt64 Size; // it can be larger than real available size */ - bool OpenBinary(const char *name, int flags, mode_t mode = 0666); + bool OpenBinary(const char *name, int flags, mode_t mode /* = k_OutFile_mode_default */); public: bool PreserveATime; #if 0 @@ -411,9 +447,11 @@ class COutFile: public CFileBase bool CTime_defined; bool ATime_defined; bool MTime_defined; + bool mode_for_Close_defined; CFiTime CTime; CFiTime ATime; CFiTime MTime; + mode_t mode_for_Close; AString Path; ssize_t write_part(const void *data, size_t size) throw(); @@ -425,9 +463,11 @@ public: CTime_defined(false), ATime_defined(false), MTime_defined(false), - mode_for_Create(0666) + mode_for_Close_defined(false), + mode_for_Create(k_OutFile_mode_default) // 0666 {} + ~COutFile() { Close(); } bool Close(); bool Open_EXISTING(CFSTR fileName); @@ -454,6 +494,7 @@ public: return SetLength(length); } bool SetTime(const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime) throw(); + bool Set_Time_and_WinAttrib(const CFiTime *cTime, const CFiTime *aTime, const CFiTime *mTime, DWORD attrib) throw(); bool SetMTime(const CFiTime *mTime) throw(); }; -- cgit v1.2.3-55-g6feb