int GetNumberOfLinks(const string& Name, bool negative_if_error) { const auto DefaultNumberOfLinks = negative_if_error ? -1 : +1; const os::fs::file File(Name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT); if (!File) return DefaultNumberOfLinks; BY_HANDLE_FILE_INFORMATION bhfi; if (!File.GetInformation(bhfi)) return DefaultNumberOfLinks; return bhfi.nNumberOfLinks; }
static bool GetREPARSE_DATA_BUFFER(const string& Object, REPARSE_DATA_BUFFER* rdb) { const auto FileAttr = os::fs::get_file_attributes(Object); if (FileAttr == INVALID_FILE_ATTRIBUTES || !(FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)) return false; const os::fs::file fObject(Object, 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT); if (!fObject) return false; DWORD dwBytesReturned; return fObject.IoControl(FSCTL_GET_REPARSE_POINT, nullptr, 0, rdb, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &dwBytesReturned) && IsReparseTagValid(rdb->ReparseTag); }
bool DeleteReparsePoint(const string& Object) { block_ptr<REPARSE_DATA_BUFFER> rdb(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); if (!GetREPARSE_DATA_BUFFER(Object, rdb.get())) return false; const os::fs::file fObject(Object, GetDesiredAccessForReparsePointChange(), 0, nullptr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT); if (!fObject) return false; DWORD dwBytes; REPARSE_GUID_DATA_BUFFER rgdb{rdb->ReparseTag}; return fObject.IoControl(FSCTL_DELETE_REPARSE_POINT,&rgdb,REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,nullptr,0,&dwBytes); }
// If the file contains a BOM this function will advance the file pointer by the BOM size (either 2 or 3) static bool GetFileCodepage(const os::fs::file& File, uintptr_t DefaultCodepage, uintptr_t& Codepage, bool& SignatureFound, bool& NotUTF8, bool& NotUTF16, bool UseHeuristics) { if (GetUnicodeCpUsingBOM(File, Codepage)) { SignatureFound = true; return true; } if (!UseHeuristics) return false; // TODO: configurable const size_t Size = 32768; char_ptr Buffer(Size); size_t ReadSize = 0; const auto ReadResult = File.Read(Buffer.get(), Size, ReadSize); File.SetPointer(0, nullptr, FILE_BEGIN); if (!ReadResult || !ReadSize) return false; if (GetUnicodeCpUsingWindows(Buffer.get(), ReadSize, Codepage)) return true; NotUTF16 = true; unsigned long long FileSize = 0; const auto WholeFileRead = File.GetSize(FileSize) && ReadSize == FileSize; bool PureAscii = false; if (encoding::is_valid_utf8({ Buffer.get(), ReadSize }, !WholeFileRead, PureAscii)) { if (!PureAscii) Codepage = CP_UTF8; else if (DefaultCodepage == CP_UTF8 || DefaultCodepage == encoding::codepage::ansi() || DefaultCodepage == encoding::codepage::oem()) Codepage = DefaultCodepage; else Codepage = encoding::codepage::ansi(); return true; } NotUTF8 = true; return GetCpUsingUniversalDetectorWithExceptions({ Buffer.get(), ReadSize }, Codepage); }
bool GetLangParam(const os::fs::file& LangFile, string_view const ParamName, string& strParam1, string* strParam2, uintptr_t CodePage) { const auto strFullParamName = concat(L'.', ParamName); const auto CurFilePos = LangFile.GetPointer(); SCOPE_EXIT{ LangFile.SetPointer(CurFilePos, nullptr, FILE_BEGIN); }; for (const auto& i: enum_file_lines(LangFile, CodePage)) { if (starts_with_icase(i.Str, strFullParamName)) { const auto EqPos = i.Str.find(L'='); if (EqPos != string::npos) { assign(strParam1, i.Str.substr(EqPos + 1)); if (strParam2) strParam2->clear(); const auto pos = strParam1.find(L','); if (pos != string::npos) { if (strParam2) { *strParam2 = trim_right(strParam1.substr(pos + 1)); } strParam1.resize(pos); } inplace::trim_right(strParam1); return true; } } else if (starts_with(i.Str, L'"')) { // '"' indicates some meaningful string. // Parameters can be only in the header, no point to go deeper return false; } } return false; }
enum_file_lines::enum_file_lines(const os::fs::file& SrcFile, uintptr_t CodePage) : SrcFile(SrcFile), BeginPos(SrcFile.GetPointer()), m_CodePage(CodePage), m_Eol(m_CodePage) { if (m_CodePage == CP_UNICODE || m_CodePage == CP_REVERSEBOM) m_wReadBuf.resize(ReadBufCount); else m_ReadBuf.resize(ReadBufCount); m_wStr.reserve(DELTA); }
bool GetVHDInfo(const string& DeviceName, string &strVolumePath, VIRTUAL_STORAGE_TYPE* StorageType) { const os::fs::file Device(DeviceName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING); if (!Device) return false; block_ptr<STORAGE_DEPENDENCY_INFO> StorageDependencyInfo; const auto& InitStorage = [&](size_t Size) { StorageDependencyInfo.reset(Size); StorageDependencyInfo->Version = STORAGE_DEPENDENCY_INFO_VERSION_2; }; DWORD Size = 4096; for (;;) { InitStorage(Size); if (Device.GetStorageDependencyInformation(GET_STORAGE_DEPENDENCY_FLAG_HOST_VOLUMES, Size, StorageDependencyInfo.get(), &Size)) break; if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return false; } if (!StorageDependencyInfo->NumberEntries) return false; if(StorageType) *StorageType = StorageDependencyInfo->Version2Entries[0].VirtualStorageType; strVolumePath = StorageDependencyInfo->Version2Entries[0].HostVolumeName; strVolumePath += StorageDependencyInfo->Version2Entries[0].DependentVolumeRelativePath; // trick: ConvertNameToReal also converts \\?\{GUID} to drive letter, if possible. strVolumePath = ConvertNameToReal(strVolumePath); return true; }
static bool SetREPARSE_DATA_BUFFER(const string& Object, REPARSE_DATA_BUFFER* rdb) { if (!IsReparseTagValid(rdb->ReparseTag)) return false; SCOPED_ACTION(os::security::privilege){ SE_CREATE_SYMBOLIC_LINK_NAME }; const auto Attributes = os::fs::get_file_attributes(Object); if(Attributes&FILE_ATTRIBUTE_READONLY) { os::fs::set_file_attributes(Object, Attributes&~FILE_ATTRIBUTE_READONLY); } SCOPE_EXIT { if (Attributes&FILE_ATTRIBUTE_READONLY) os::fs::set_file_attributes(Object, Attributes); }; const auto& SetBuffer = [&](bool ForceElevation) { const os::fs::file fObject(Object, GetDesiredAccessForReparsePointChange(), 0, nullptr, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, nullptr, ForceElevation); DWORD dwBytesReturned; return fObject && fObject.IoControl(FSCTL_SET_REPARSE_POINT, rdb, rdb->ReparseDataLength + REPARSE_DATA_BUFFER_HEADER_SIZE, nullptr, 0, &dwBytesReturned); }; if (SetBuffer(false)) return true; if (ElevationRequired(ELEVATION_MODIFY_REQUEST)) { // Open() succeeded, but IoControl() failed. We can't handle this automatically :( return SetBuffer(true); } return false; }
// If the file contains a BOM this function will advance the file pointer by the BOM size (either 2 or 3) static bool GetUnicodeCpUsingBOM(const os::fs::file& File, uintptr_t& Codepage) { char Buffer[3]{}; size_t BytesRead = 0; if (!File.Read(Buffer, std::size(Buffer), BytesRead)) return false; std::string_view Signature(Buffer, std::size(Buffer)); if (BytesRead >= 2) { if (Signature.substr(0, 2) == encoding::get_signature_bytes(CP_UNICODE)) { Codepage = CP_UNICODE; File.SetPointer(2, nullptr, FILE_BEGIN); return true; } if (Signature.substr(0, 2) == encoding::get_signature_bytes(CP_REVERSEBOM)) { Codepage = CP_REVERSEBOM; File.SetPointer(2, nullptr, FILE_BEGIN); return true; } } if (BytesRead >= 3 && Signature == encoding::get_signature_bytes(CP_UTF8)) { Codepage = CP_UTF8; File.SetPointer(3, nullptr, FILE_BEGIN); return true; } File.SetPointer(0, nullptr, FILE_BEGIN); return false; }