/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011-2016 Martin Raiber * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **************************************************************************/ #include "os_functions.h" #include "../stringtools.h" #ifdef _WIN_PRE_VISTA #define _WIN32_WINNT 0x0500 #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_NTFS_TXF #include #endif #include #include "server_compat.h" //For PathIsRelative #pragma comment(lib, "Shlwapi.lib") namespace { #define REPARSE_MOUNTPOINT_HEADER_SIZE 8 typedef struct { DWORD ReparseTag; DWORD ReparseDataLength; WORD Reserved; WORD ReparseTargetLength; WORD ReparseTargetMaximumLength; WORD Reserved1; WCHAR ReparseTarget[1]; } REPARSE_MOUNTPOINT_DATA_BUFFER, *PREPARSE_MOUNTPOINT_DATA_BUFFER; typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; USHORT ReparseDataLength; USHORT Reserved; union { struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; ULONG Flags; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct { USHORT SubstituteNameOffset; USHORT SubstituteNameLength; USHORT PrintNameOffset; USHORT PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; namespace usn { typedef struct { DWORD RecordLength; WORD MajorVersion; WORD MinorVersion; BYTE FileReferenceNumber[16]; BYTE ParentFileReferenceNumber[16]; USN Usn; LARGE_INTEGER TimeStamp; DWORD Reason; DWORD SourceInfo; DWORD SecurityId; DWORD FileAttributes; WORD FileNameLength; WORD FileNameOffset; WCHAR FileName[1]; } USN_RECORD_V3, *PUSN_RECORD_V3; } } void getMousePos(int &x, int &y) { POINT mousepos; GetCursorPos(&mousepos); x=mousepos.x; y=mousepos.y; } const int64 WINDOWS_TICK=10000000; const int64 SEC_TO_UNIX_EPOCH=11644473600LL; bool os_is_symlink_int(const std::string& path); int64 os_windows_to_unix_time(int64 windows_filetime) { return windows_filetime / WINDOWS_TICK - SEC_TO_UNIX_EPOCH; } int64 os_to_windows_filetime(int64 unix_time) { return (unix_time+SEC_TO_UNIX_EPOCH)*WINDOWS_TICK; } std::vector getFiles(const std::string &path, bool *has_error, bool ignore_other_fs) { return getFilesWin(path, has_error, true, false, ignore_other_fs); } std::wstring os_file_prefix(std::wstring path) { if(path.size()>=2 && path[0]=='\\' && path[1]=='\\' ) { if(path.size()>=3 && path[2]=='?') { return path; } else { return L"\\\\?\\UNC"+path.substr(1); } } else return L"\\\\?\\"+path; } bool is_volume(std::string target) { if (next(target, 0, "\\??\\")) { target = target.substr(4); } target = strlower(target); if (next(target, 0, "volume") && getafter("\\", target).empty()) { return true; } return false; } std::vector getFilesWin(const std::string &path, bool *has_error, bool exact_filesize, bool with_usn, bool ignore_other_fs) { if(has_error!=NULL) { *has_error=false; } std::vector usn_buffer; usn_buffer.resize(1024); std::vector tmp; HANDLE fHandle; WIN32_FIND_DATAW wfd; std::wstring tpath=ConvertToWchar(path); if(!tpath.empty() && tpath[tpath.size()-1]=='\\' ) tpath.erase(tpath.size()-1, 1); fHandle=FindFirstFileW((tpath+L"\\*").c_str(),&wfd); if(fHandle==INVALID_HANDLE_VALUE) { if(tpath.find(L"\\\\?\\UNC")==0) { tpath.erase(0, 7); tpath=L"\\"+tpath; fHandle=FindFirstFileW((tpath+L"\\*").c_str(),&wfd); } else if(tpath.find(L"\\\\?\\")==0) { tpath.erase(0, 4); fHandle=FindFirstFileW((tpath+L"\\*").c_str(),&wfd); } if(fHandle==INVALID_HANDLE_VALUE) { if(has_error!=NULL) { *has_error=true; } return tmp; } } do { SFile f; f.name=ConvertFromWchar(wfd.cFileName); if(f.name=="." || f.name==".." ) continue; f.usn=0; f.isdir=(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)>0; LARGE_INTEGER lwt; lwt.HighPart=wfd.ftLastWriteTime.dwHighDateTime; lwt.LowPart=wfd.ftLastWriteTime.dwLowDateTime; f.last_modified=os_windows_to_unix_time(lwt.QuadPart); LARGE_INTEGER size; size.HighPart=wfd.nFileSizeHigh; size.LowPart=wfd.nFileSizeLow; f.size=size.QuadPart; lwt.HighPart=wfd.ftCreationTime.dwHighDateTime; lwt.LowPart=wfd.ftCreationTime.dwLowDateTime; f.created=os_windows_to_unix_time(lwt.QuadPart); std::string reparse_target; if (ignore_other_fs && wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT && os_get_symlink_target(os_file_prefix(path + os_file_sep() + f.name), reparse_target) && is_volume(reparse_target) ) { continue; } else if(wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && (wfd.dwReserved0== IO_REPARSE_TAG_SYMLINK || wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) ) { f.issym=true; f.isspecialf=true; } else if (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && (wfd.dwReserved0 == 0x8000001b /*IO_REPARSE_TAG_APPEXECLINK*/ )) { f.isspecialf = true; } f.isencrypted = (wfd.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)>0; if( (exact_filesize && !f.isdir && !f.issym ) || with_usn ) { if(with_usn) { HANDLE hFile = CreateFileW(os_file_prefix(tpath+L"\\"+ConvertToWchar(f.name)).c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL); if(hFile!=INVALID_HANDLE_VALUE) { if (exact_filesize && !f.isdir && !f.issym) { BY_HANDLE_FILE_INFORMATION fileInformation; if (GetFileInformationByHandle(hFile, &fileInformation)!=0) { LARGE_INTEGER fsize; fsize.LowPart = fileInformation.nFileSizeLow; fsize.HighPart = fileInformation.nFileSizeHigh; f.size = fsize.QuadPart; f.nlinks = fileInformation.nNumberOfLinks; } } DWORD last_err=0; do { DWORD ret_bytes = 0; BOOL b = DeviceIoControl(hFile, FSCTL_READ_FILE_USN_DATA, NULL, 0, usn_buffer.data(), static_cast(usn_buffer.size()), &ret_bytes, NULL); if(b) { USN_RECORD* usnv2=reinterpret_cast(usn_buffer.data()); if(usnv2->MajorVersion==2) { f.usn = usnv2->Usn; } else if(usnv2->MajorVersion==3) { usn::USN_RECORD_V3* usnv3=reinterpret_cast(usn_buffer.data()); f.usn = usnv3->Usn; } else { Log("USN entry major version "+convert(usnv2->MajorVersion)+" of file \""+ConvertFromWchar(tpath)+"\\"+f.name+"\" not supported", LL_ERROR); } } else { last_err=GetLastError(); } if(last_err==ERROR_INSUFFICIENT_BUFFER) { usn_buffer.resize(usn_buffer.size()*2); } } while (last_err==ERROR_INSUFFICIENT_BUFFER); CloseHandle(hFile); } } else { WIN32_FILE_ATTRIBUTE_DATA fad; if( GetFileAttributesExW(os_file_prefix(tpath+L"\\"+ConvertToWchar(f.name)).c_str(), GetFileExInfoStandard, &fad) ) { size.HighPart = fad.nFileSizeHigh; size.LowPart = fad.nFileSizeLow; f.size = size.QuadPart; lwt.HighPart = fad.ftLastWriteTime.dwHighDateTime; lwt.LowPart = fad.ftLastWriteTime.dwLowDateTime; f.last_modified = os_windows_to_unix_time(lwt.QuadPart); lwt.HighPart=fad.ftCreationTime.dwHighDateTime; lwt.LowPart=fad.ftCreationTime.dwLowDateTime; f.created=os_windows_to_unix_time(lwt.QuadPart); } } } if(f.last_modified<0) f.last_modified*=-1; tmp.push_back(f); } while (FindNextFileW(fHandle,&wfd) ); if (GetLastError() != ERROR_NO_MORE_FILES) { DWORD err = GetLastError(); if (has_error != NULL) { *has_error = true; } FindClose(fHandle); std::sort(tmp.begin(), tmp.end()); SetLastError(err); return tmp; } FindClose(fHandle); std::sort(tmp.begin(), tmp.end()); return tmp; } SFile getFileMetadataWin( const std::string &path, bool with_usn ) { SFile ret; ret.name=path; WIN32_FILE_ATTRIBUTE_DATA fad; if( GetFileAttributesExW(ConvertToWchar(path).c_str(), GetFileExInfoStandard, &fad) ) { if (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { ret.isdir=true; } LARGE_INTEGER size; size.HighPart = fad.nFileSizeHigh; size.LowPart = fad.nFileSizeLow; ret.size = size.QuadPart; LARGE_INTEGER lwt; lwt.HighPart = fad.ftLastWriteTime.dwHighDateTime; lwt.LowPart = fad.ftLastWriteTime.dwLowDateTime; ret.last_modified = os_windows_to_unix_time(lwt.QuadPart); lwt.HighPart=fad.ftCreationTime.dwHighDateTime; lwt.LowPart=fad.ftCreationTime.dwLowDateTime; ret.created=os_windows_to_unix_time(lwt.QuadPart); lwt.HighPart = fad.ftLastAccessTime.dwHighDateTime; lwt.LowPart = fad.ftLastAccessTime.dwLowDateTime; ret.accessed = os_windows_to_unix_time(lwt.QuadPart); if (fad.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (os_is_symlink_int(path)) { ret.issym = true; } ret.isspecialf = true; } if(with_usn && !ret.isdir && !(fad.dwFileAttributes &FILE_ATTRIBUTE_REPARSE_POINT)) { HANDLE hFile = CreateFileW(ConvertToWchar(path).c_str(), GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hFile!=INVALID_HANDLE_VALUE) { std::vector buffer; buffer.resize(1024); DWORD last_err=0; do { DWORD ret_bytes = 0; BOOL b = DeviceIoControl(hFile, FSCTL_READ_FILE_USN_DATA, NULL, 0, buffer.data(), static_cast(buffer.size()), &ret_bytes, NULL); if(b) { USN_RECORD* usnv2=reinterpret_cast(buffer.data()); if(usnv2->MajorVersion==2) { ret.usn = usnv2->Usn; } else if(usnv2->MajorVersion==3) { usn::USN_RECORD_V3* usnv3=reinterpret_cast(buffer.data()); ret.usn = usnv3->Usn; } else { Log("USN entry major version "+convert(usnv2->MajorVersion)+" of file \""+path+"\" not supported", LL_ERROR); } } else { last_err=GetLastError(); } if(last_err==ERROR_INSUFFICIENT_BUFFER) { buffer.resize(buffer.size()*2); } } while (last_err==ERROR_INSUFFICIENT_BUFFER); CloseHandle(hFile); } } return ret; } else { return SFile(); } } SFile getFileMetadata( const std::string &path ) { return getFileMetadataWin(path, false); } bool removeFile(const std::string &path) { return _unlink(path.c_str())==0; } bool moveFile(const std::string &src, const std::string &dst) { return rename(src.c_str(), dst.c_str() )==0; } bool isDirectory(const std::string &path, void* transaction) { DWORD attrib; #ifdef USE_NTFS_TXF if(transaction!=NULL) { WIN32_FILE_ATTRIBUTE_DATA ad; if(!GetFileAttributesTransactedW(ConvertToWchar(path).c_str(), GetFileExInfoStandard, &ad, transaction)) { attrib=0xFFFFFFFF; } else { attrib = ad.dwFileAttributes; } } else { #endif attrib = GetFileAttributesW(ConvertToWchar(path).c_str()); #ifdef USE_NTFS_TXF } #endif if ( attrib == INVALID_FILE_ATTRIBUTES || !(attrib & FILE_ATTRIBUTE_DIRECTORY) ) { return false; } else { return true; } } bool os_is_symlink_int(const std::string& path) { WIN32_FIND_DATAW wfd; HANDLE hf = FindFirstFileW(ConvertToWchar(path).c_str(), &wfd); if (hf == INVALID_HANDLE_VALUE) { return false; } FindClose(hf); if (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT || wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { return true; } } return false; } int os_get_file_type(const std::string &path) { DWORD attrib = GetFileAttributesW(ConvertToWchar(path).c_str()); if (attrib == INVALID_FILE_ATTRIBUTES) { return 0; } int ret = 0; if (attrib & FILE_ATTRIBUTE_DIRECTORY) { ret |= EFileType_Directory; } else { ret |= EFileType_File; } if (attrib & FILE_ATTRIBUTE_REPARSE_POINT) { if (os_is_symlink_int(path)) { ret |= EFileType_Symlink; } ret |= EFileType_Special; } return ret; } int64 os_atoi64(const std::string &str) { return _atoi64(str.c_str()); } bool os_create_dir(const std::string &dir) { return CreateDirectoryW(ConvertToWchar(dir).c_str(), NULL)!=0; } namespace { namespace reflink { #pragma pack(push, 8) typedef struct _DUPLICATE_EXTENTS_DATA { HANDLE FileHandle; LARGE_INTEGER SourceFileOffset; LARGE_INTEGER TargetFileOffset; LARGE_INTEGER ByteCount; } DUPLICATE_EXTENTS_DATA, *PDUPLICATE_EXTENTS_DATA; #pragma pack(pop) DWORD LOCAL_FSCTL_DUPLICATE_EXTENTS_TO_FILE = 0x98344; } } bool os_create_reflink(const std::string &linkname, const std::string &fname) { HANDLE source_handle = CreateFileW(ConvertToWchar(fname).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (source_handle == INVALID_HANDLE_VALUE) return false; BY_HANDLE_FILE_INFORMATION source_info; if (!GetFileInformationByHandle(source_handle, &source_info)) { CloseHandle(source_handle); return false; } LARGE_INTEGER source_size; source_size.LowPart = source_info.nFileSizeLow; source_size.HighPart = source_info.nFileSizeHigh; ULONG ret_bytes; FSCTL_GET_INTEGRITY_INFORMATION_BUFFER get_integrity_info; if (!DeviceIoControl(source_handle, FSCTL_GET_INTEGRITY_INFORMATION, NULL, 0, &get_integrity_info, sizeof(get_integrity_info), &ret_bytes, NULL)) { CloseHandle(source_handle); return false; } HANDLE dest_handle = CreateFileW(ConvertToWchar(linkname).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, source_handle); if (dest_handle == INVALID_HANDLE_VALUE) { CloseHandle(source_handle); return false; } bool has_error = false; if ((source_info.dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)>0 && !DeviceIoControl(dest_handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &ret_bytes, NULL)) { has_error = true; } FSCTL_SET_INTEGRITY_INFORMATION_BUFFER set_integrity_info = { get_integrity_info.ChecksumAlgorithm, get_integrity_info.Reserved, get_integrity_info.Flags }; if (!has_error && !DeviceIoControl(dest_handle, FSCTL_SET_INTEGRITY_INFORMATION, &set_integrity_info, sizeof(set_integrity_info), NULL, 0, NULL, NULL)) { has_error = true; } FILE_END_OF_FILE_INFO eof_info = { source_size }; if(!has_error && !SetFileInformationByHandle(dest_handle, FileEndOfFileInfo, &eof_info, sizeof(eof_info) ) ) { has_error = true; } int64 cluster_size = get_integrity_info.ClusterSizeInBytes; int64 total_reflink = source_size.QuadPart; if(total_reflink%cluster_size!=0) total_reflink = ((total_reflink) / cluster_size + 1 )*cluster_size; int64 max_reflink = cluster_size == 0 ? total_reflink : (4LL * 1024 * 1024 * 1024 - cluster_size); reflink::DUPLICATE_EXTENTS_DATA reflink_data; reflink_data.FileHandle = source_handle; int64 reflinked = 0; while (!has_error && reflinked < total_reflink) { int64 curr_reflink = (std::min)(total_reflink - reflinked, max_reflink); reflink_data.ByteCount.QuadPart = curr_reflink; reflink_data.SourceFileOffset.QuadPart = reflinked; reflink_data.TargetFileOffset.QuadPart = reflinked; if (!DeviceIoControl(dest_handle, reflink::LOCAL_FSCTL_DUPLICATE_EXTENTS_TO_FILE, &reflink_data, sizeof(reflink_data), NULL, 0, &ret_bytes, NULL)) { has_error = true; } reflinked += curr_reflink; } DWORD err = GetLastError(); CloseHandle(source_handle); CloseHandle(dest_handle); if (has_error) { DeleteFileW(ConvertToWchar(linkname).c_str()); SetLastError(err); return false; } return true; } bool os_create_hardlink(const std::string &linkname, const std::string &fname, bool use_ioref, bool* too_many_links) { if (use_ioref) { return os_create_reflink(linkname, fname); } BOOL r=CreateHardLinkW(ConvertToWchar(linkname).c_str(), ConvertToWchar(fname).c_str(), NULL); if(too_many_links!=NULL) { *too_many_links=false; if(!r) { int err=GetLastError(); if(err==ERROR_TOO_MANY_LINKS) { *too_many_links=true; } } } return r!=0; } int64 os_free_space(const std::string &path) { std::string cp=path; if(path.size()==0) return -1; if(cp[cp.size()-1]=='/') cp.erase(cp.size()-1, 1); if(cp[cp.size()-1]!='\\') cp+='\\'; ULARGE_INTEGER li; BOOL r=GetDiskFreeSpaceExW(ConvertToWchar(cp).c_str(), &li, NULL, NULL); if(r!=0) return li.QuadPart; else return -1; } int64 os_total_space(const std::string &path) { std::string cp=path; if(path.size()==0) return -1; if(cp[cp.size()-1]=='/') cp.erase(cp.size()-1, 1); if(cp[cp.size()-1]!='\\') cp+='\\'; ULARGE_INTEGER li; BOOL r=GetDiskFreeSpaceExW(ConvertToWchar(path).c_str(), NULL, &li, NULL); if(r!=0) return li.QuadPart; else return -1; } bool os_directory_exists(const std::string &path) { return isDirectory(path, NULL); } bool os_remove_symlink_dir_int(const std::wstring &path) { return RemoveDirectoryW(path.c_str())!=FALSE; } bool os_remove_nonempty_dir_int(const std::wstring &root_path, os_symlink_callback_t symlink_callback, void* userdata, bool delete_root) { std::stack > paths; paths.push(std::make_pair(root_path, false)); while (!paths.empty()) { std::wstring path = paths.top().first; if (paths.top().second) { if (paths.size() == 1 && !delete_root) { return true; } if (!RemoveDirectoryW(path.c_str())) { Log("Error deleting directory \"" + ConvertFromWchar(path) + "\"", LL_ERROR); } paths.pop(); continue; } paths.top().second = true; std::wstring tpath = path; WIN32_FIND_DATAW wfd; HANDLE hf = FindFirstFileW((tpath + L"\\*").c_str(), &wfd); if (hf == INVALID_HANDLE_VALUE) { if (tpath.find(L"\\\\?\\UNC") == 0) { tpath.erase(0, 7); tpath = L"\\" + tpath; hf = FindFirstFileW((tpath + L"\\*").c_str(), &wfd); } else if (tpath.find(L"\\\\?\\") == 0) { tpath.erase(0, 4); hf = FindFirstFileW((tpath + L"\\*").c_str(), &wfd); } if (hf == INVALID_HANDLE_VALUE) { return false; } } BOOL b = true; while (b) { if ((std::wstring)wfd.cFileName != L"." && (std::wstring)wfd.cFileName != L"..") { if (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { if (symlink_callback != NULL && (wfd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT || wfd.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { bool isdir = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0; symlink_callback(ConvertFromWchar(path + L"\\" + wfd.cFileName), &isdir, userdata); } else if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { os_remove_symlink_dir_int(path + L"\\" + wfd.cFileName); } else { DeleteFileW((path + L"\\" + wfd.cFileName).c_str()); } } else if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { paths.push(std::make_pair(tpath + L"\\" + wfd.cFileName, false)); } else { DeleteFileW((path + L"\\" + wfd.cFileName).c_str()); } } b = FindNextFileW(hf, &wfd); } FindClose(hf); } return true; } bool os_remove_nonempty_dir(const std::string &path, os_symlink_callback_t symlink_callback, void* userdata, bool delete_root) { return os_remove_nonempty_dir_int(ConvertToWchar(path), symlink_callback, userdata, delete_root); } bool os_remove_symlink_dir(const std::string &path) { return RemoveDirectoryW(ConvertToWchar(path).c_str())!=FALSE; } bool os_remove_dir(const std::string &path) { return RemoveDirectoryW(ConvertToWchar(path).c_str())!=FALSE; } std::string os_file_sep(void) { return "\\"; } bool os_link_symbolic_symlink(const std::string &target, const std::string &lname, void* transaction, bool* isdir) { #if (_WIN32_WINNT >= 0x0600) DWORD flags=0; if(isdir!=NULL && *isdir) { flags|=SYMBOLIC_LINK_FLAG_DIRECTORY; } else if(isdir==NULL && isDirectory(target, transaction)) { flags|=SYMBOLIC_LINK_FLAG_DIRECTORY; } BOOL rc; if(transaction==NULL) { rc=CreateSymbolicLink(ConvertToWchar(lname).c_str(), ConvertToWchar(target).c_str(), flags); } else { rc=CreateSymbolicLinkTransactedW(ConvertToWchar(lname).c_str(), ConvertToWchar(target).c_str(), flags, transaction); } if(rc==FALSE) { DWORD lasterr = GetLastError(); Log("Creating symbolic link from \""+lname+"\" to \""+target+"\" failed with error "+convert((int)GetLastError()), LL_ERROR); SetLastError(lasterr); } return rc==TRUE; #else return false; #endif } bool os_link_symbolic_junctions_raw(const std::string &target, const std::string &lname, void* transaction) { bool ret = false; HANDLE hJunc = INVALID_HANDLE_VALUE; char* buf = NULL; std::wstring wtarget = ConvertToWchar(target); std::wstring wlname = ConvertToWchar(lname); if (transaction != NULL) { if (!CreateDirectoryTransactedW(NULL, wlname.c_str(), NULL, transaction)) { goto cleanup; } } else { if (!CreateDirectoryW(wlname.c_str(), NULL)) { goto cleanup; } } if (transaction != NULL) { USHORT mversion = 0xFFFF; hJunc = CreateFileTransactedW(wlname.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL, transaction, &mversion, NULL); } else { hJunc = CreateFileW(wlname.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); } if(hJunc==INVALID_HANDLE_VALUE) goto cleanup; size_t bsize=sizeof(REPARSE_MOUNTPOINT_DATA_BUFFER) + (wtarget.size()+1) * sizeof(wchar_t)+30; buf=new char[bsize]; memset(buf, 0, bsize); REPARSE_MOUNTPOINT_DATA_BUFFER *rb=(REPARSE_MOUNTPOINT_DATA_BUFFER*)buf; rb->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT; rb->ReparseTargetMaximumLength=(WORD)((wtarget.size()+1)*sizeof(wchar_t)); rb->ReparseTargetLength=rb->ReparseTargetMaximumLength-1*sizeof(wchar_t); rb->ReparseDataLength=rb->ReparseTargetLength+12; memcpy(rb->ReparseTarget, wtarget.c_str(), rb->ReparseTargetMaximumLength); DWORD bytes_ret; if(!DeviceIoControl(hJunc, FSCTL_SET_REPARSE_POINT, rb, rb->ReparseDataLength+REPARSE_MOUNTPOINT_HEADER_SIZE, NULL, 0, &bytes_ret, NULL) ) { goto cleanup; } ret=true; cleanup: DWORD lasterr; if(!ret) { lasterr = GetLastError(); Log("Creating junction failed. Last error="+convert((int)GetLastError()), LL_ERROR); } delete []buf; if(hJunc!=INVALID_HANDLE_VALUE) CloseHandle(hJunc); if(!ret) { if (transaction != NULL) RemoveDirectoryTransactedW(ConvertToWchar(lname).c_str(), transaction); else RemoveDirectoryW(ConvertToWchar(lname).c_str()); SetLastError(lasterr); } return ret; } bool os_link_symbolic_junctions(const std::string &target, const std::string &lname, void* transaction) { std::string wtarget = target; if (wtarget.find("\\\\?\\UNC") == 0) { wtarget.erase(0, 7); wtarget = "\\" + wtarget; } else if (wtarget.find(os_file_prefix("")) == 0) { wtarget.erase(0, os_file_prefix("").size()); } if (!wtarget.empty() && wtarget[0] != '\\') wtarget = "\\??\\" + wtarget; if (!wtarget.empty() && wtarget[wtarget.size() - 1] == '\\') wtarget.erase(wtarget.size() - 1, 1); return os_link_symbolic_junctions_raw(wtarget, lname, transaction); } #ifndef OS_FUNC_NO_NET bool os_lookuphostname(std::string pServer, unsigned int *dest) { const char* host=pServer.c_str(); unsigned int addr = inet_addr(host); if (addr != INADDR_NONE) { *dest = addr; } else { addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; addrinfo* h; if(getaddrinfo(pServer.c_str(), NULL, &hints, &h)==0) { if(h!=NULL) { if(h->ai_addrlen>=sizeof(sockaddr_in)) { *dest=reinterpret_cast(h->ai_addr)->sin_addr.s_addr; freeaddrinfo(h); return true; } else { freeaddrinfo(h); return false; } } else { return false; } freeaddrinfo(h); } else { return false; } } return true; } bool os_lookuphostname(std::string pServer, SLookupResult & dest) { const char* host = pServer.c_str(); unsigned int addr = inet_addr(host); if (addr != INADDR_NONE) { dest.is_ipv6 = false; dest.addr_v4 = addr; return true; } int rc = inet_pton(AF_INET6, host, dest.addr_v6); if (rc == 1) { dest.is_ipv6 = true; return true; } addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; addrinfo* h; if (getaddrinfo(pServer.c_str(), NULL, &hints, &h) == 0) { addrinfo* orig_h = h; while (h != NULL) { if (h->ai_family != AF_INET6 && h->ai_family != AF_INET) { h = h->ai_next; continue; } if (h->ai_family == AF_INET6) { if (h->ai_addrlen >= sizeof(sockaddr_in6)) { dest.is_ipv6 = true; memcpy(dest.addr_v6, &reinterpret_cast(h->ai_addr)->sin6_addr, 16); freeaddrinfo(orig_h); return true; } else { freeaddrinfo(orig_h); return false; } } else { if (h->ai_addrlen >= sizeof(sockaddr_in)) { dest.is_ipv6 = false; dest.addr_v4 = reinterpret_cast(h->ai_addr)->sin_addr.s_addr; freeaddrinfo(orig_h); return true; } else { freeaddrinfo(orig_h); return false; } } h = h->ai_next; } freeaddrinfo(orig_h); return false; } else { return false; } } #endif bool os_link_symbolic(const std::string &target, const std::string &lname, void* transaction, bool* isdir) { bool l_isdir; if(isdir!=NULL) { l_isdir=*isdir; } else { l_isdir=isDirectory(target, NULL); } if(!l_isdir || !os_path_absolute(target) ||!os_link_symbolic_junctions(target, lname, transaction) ) return os_link_symbolic_symlink(target, lname, transaction, isdir); return true; } bool os_get_symlink_target(const std::string &lname, std::string &target) { HANDLE hJunc=CreateFileW(ConvertToWchar(lname).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_ATTRIBUTE_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hJunc==INVALID_HANDLE_VALUE) return false; DWORD needed_buffer_size = 0; DWORD bytes_returned; std::string buffer; buffer.resize((std::max)((size_t)REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, (size_t)512)); BOOL b = DeviceIoControl(hJunc, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer[0], static_cast(buffer.size()), &bytes_returned, NULL); if(!b) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER && GetLastError() != ERROR_MORE_DATA) { CloseHandle(hJunc); return false; } } const REPARSE_DATA_BUFFER* reparse_buffer = reinterpret_cast(buffer.data()); if(!IsReparseTagMicrosoft(reparse_buffer->ReparseTag)) { CloseHandle(hJunc); return false; } if(!b) { buffer.resize(reparse_buffer->ReparseDataLength+ REPARSE_GUID_DATA_BUFFER_HEADER_SIZE+10); reparse_buffer = reinterpret_cast(buffer.data()); b = DeviceIoControl(hJunc, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer[0], static_cast(buffer.size()), &bytes_returned, NULL); if(!b) { CloseHandle(hJunc); return false; } } CloseHandle(hJunc); bool ret=true; std::wstring wtarget; if(reparse_buffer->ReparseTag==IO_REPARSE_TAG_SYMLINK) { wtarget.resize(reparse_buffer->SymbolicLinkReparseBuffer.SubstituteNameLength/sizeof(wchar_t)); memcpy(&wtarget[0], &reparse_buffer->SymbolicLinkReparseBuffer.PathBuffer[reparse_buffer->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(wchar_t)], reparse_buffer->SymbolicLinkReparseBuffer.SubstituteNameLength); } else if(reparse_buffer->ReparseTag==IO_REPARSE_TAG_MOUNT_POINT) { wtarget.resize(reparse_buffer->MountPointReparseBuffer.SubstituteNameLength/sizeof(wchar_t)); memcpy(&wtarget[0], &reparse_buffer->MountPointReparseBuffer.PathBuffer[reparse_buffer->MountPointReparseBuffer.SubstituteNameOffset/sizeof(wchar_t)], reparse_buffer->MountPointReparseBuffer.SubstituteNameLength); } else { ret=false; } target = ConvertFromWchar(wtarget); if(next(target, 0, "\\??\\")) target.erase(0,4); return ret; } bool os_is_symlink(const std::string &lname) { return (os_get_file_type(lname) & EFileType_Symlink)>0; } std::string os_file_prefix(std::string path) { if(path.size()>=2 && path[0]=='\\' && path[1]=='\\' ) { if(path.size()>=3 && path[2]=='?') { return path; } else { return "\\\\?\\UNC"+path.substr(1); } } else return "\\\\?\\"+path; } bool os_file_truncate(const std::string &fn, int64 fsize) { int fh; if( _wsopen_s ( &fh, ConvertToWchar(fn).c_str(), _O_RDWR | _O_CREAT, _SH_DENYNO, _S_IREAD | _S_IWRITE ) == 0 ) { if( _chsize_s( fh, fsize ) != 0 ) { _close( fh ); return false; } _close( fh ); return true; } else { return false; } } std::string os_strftime(std::string fs) { time_t rawtime; char buffer [100]; time ( &rawtime ); struct tm timeinfo; localtime_s(&timeinfo, &rawtime); strftime (buffer,100,fs.c_str(),&timeinfo); std::string r(buffer); return r; } bool os_create_dir_recursive(std::string fn) { if(fn.empty()) return false; bool b=os_create_dir(fn); if(!b) { b=os_create_dir_recursive(ExtractFilePath(fn)); if(!b) return false; return os_create_dir(fn); } else { return true; } } std::string os_get_final_path(std::string path) { #if (_WIN32_WINNT >= 0x0600) std::wstring wret; if(path.size()<3 && path.find(":")==std::string::npos) { path+=":"; } if(path.find("\\")==std::string::npos) { path+="\\"; } HANDLE hFile = CreateFileW(ConvertToWchar(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); if( hFile==INVALID_HANDLE_VALUE ) { Log("Could not open path in os_get_final_path for \""+path+"\"", LL_ERROR); return path; } DWORD dwBufsize = GetFinalPathNameByHandleW( hFile, NULL, 0, VOLUME_NAME_DOS ); if(dwBufsize==0) { Log("Error getting path size in in os_get_final_path error="+convert((int)GetLastError())+" for \""+path+"\"", LL_ERROR); CloseHandle(hFile); return path; } wret.resize(dwBufsize+1); DWORD dwRet = GetFinalPathNameByHandleW( hFile, (LPWSTR)wret.c_str(), dwBufsize, VOLUME_NAME_DOS ); CloseHandle(hFile); if(dwRet==0) { Log("Error getting path in in os_get_final_path error="+convert((int)GetLastError()), LL_ERROR); } else if(dwRet=2 && wret[wret.size()-2]=='.' && ret[wret.size()-1]=='.' ) { wret.resize(ret.size()-2); }*/ return ConvertFromWchar(wret); } else { Log("Error getting path (buffer too small) in in os_get_final_path error="+convert((int)GetLastError()), LL_ERROR); } return path; #else return path; #endif } bool os_rename_file(std::string src, std::string dst, void* transaction) { BOOL rc; #ifdef USE_NTFS_TXF if(transaction==NULL) { #endif rc = MoveFileExW(ConvertToWchar(src).c_str(), ConvertToWchar(dst).c_str(), MOVEFILE_REPLACE_EXISTING); #ifdef USE_NTFS_TXF } else { rc=MoveFileTransactedW(ConvertToWchar(src).c_str(), ConvertToWchar(dst).c_str(), NULL, NULL, MOVEFILE_REPLACE_EXISTING, transaction); } #endif #ifdef _DEBUG if(rc==0) { Log("MoveFileW error: "+convert((int)GetLastError()), LL_ERROR); } #endif return rc!=0; } void* os_start_transaction() { #ifdef USE_NTFS_TXF HANDLE htrans = CreateTransaction(NULL, NULL, 0, 0, 0, 0, NULL); if(htrans==INVALID_HANDLE_VALUE) { Log("Creating transaction failed. ec="+convert((int)GetLastError()), LL_WARNING); return NULL; } return htrans; #else return NULL; #endif } bool os_finish_transaction(void* transaction) { #ifdef USE_NTFS_TXF if(transaction==NULL) { return false; } BOOL b = CommitTransaction(transaction); if(!b) { Log("Commiting transaction failed. ec="+convert((int)GetLastError()), LL_ERROR); CloseHandle(transaction); return false; } CloseHandle(transaction); return true; #else return true; #endif } int64 os_last_error() { return GetLastError(); } bool os_set_file_time(const std::string& fn, int64 created, int64 last_modified, int64 accessed) { HANDLE hFile = CreateFileW(ConvertToWchar(fn).c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if(hFile!=INVALID_HANDLE_VALUE) { FILETIME* pcreation_time = NULL; FILETIME creation_time; if (created != 0) { LARGE_INTEGER li; li.QuadPart=os_to_windows_filetime(created); creation_time.dwHighDateTime=li.HighPart; creation_time.dwLowDateTime=li.LowPart; pcreation_time = &creation_time; } LARGE_INTEGER li; li.QuadPart = os_to_windows_filetime(last_modified); FILETIME modified_time; modified_time.dwHighDateTime=li.HighPart; modified_time.dwLowDateTime=li.LowPart; li.QuadPart=os_to_windows_filetime(accessed); FILETIME accessed_time; accessed_time.dwHighDateTime = li.HighPart; accessed_time.dwLowDateTime = li.LowPart; if(SetFileTime(hFile, pcreation_time, &accessed_time, &modified_time)) { CloseHandle(hFile); return true; } else { CloseHandle(hFile); return false; } } else { return false; } } #ifndef OS_FUNC_NO_SERVER bool copy_file(const std::string &src, const std::string &dst, bool flush, std::string* error_str) { IFile *fsrc=Server->openFile(src, MODE_READ); if (fsrc == NULL) { if (error_str != NULL) { *error_str = os_last_error_str(); } return false; } IFile *fdst=Server->openFile(dst, MODE_WRITE); if(fdst==NULL) { if (error_str != NULL) { *error_str = os_last_error_str(); } Server->destroy(fsrc); return false; } bool copy_ok = copy_file(fsrc, fdst, error_str); if (copy_ok && flush) { copy_ok = fdst->Sync(); if (!copy_ok && error_str!=NULL) { *error_str = os_last_error_str(); } } Server->destroy(fsrc); Server->destroy(fdst); return copy_ok; } bool copy_file(IFile *fsrc, IFile *fdst, std::string* error_str) { if(fsrc==NULL || fdst==NULL) { return false; } if(!fsrc->Seek(0)) { return false; } if(!fdst->Seek(0)) { return false; } char buf[4096]; size_t rc; bool has_error=false; while( (rc=(_u32)fsrc->Read(buf, 4096, &has_error))>0) { if(has_error) { if (error_str != NULL) { *error_str = os_last_error_str(); } break; } if(rc>0) { fdst->Write(buf, (_u32)rc, &has_error); if(has_error) { if (error_str != NULL) { *error_str = os_last_error_str(); } break; } } } if(has_error) { return false; } else { return true; } } #endif bool os_path_absolute(const std::string& path) { return PathIsRelativeW(ConvertToWchar(path).c_str())==FALSE; } bool os_sync(const std::string & path) { std::string prefixedbpath = os_file_prefix(path); if (next(prefixedbpath, 0, "\\\\?\\UNC")) { HANDLE hVol = CreateFileW(ConvertToWchar(prefixedbpath).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hVol == INVALID_HANDLE_VALUE) { return false; } BOOL b = FlushFileBuffers(hVol); CloseHandle(hVol); return b == TRUE; } std::wstring tvolume; tvolume.resize(prefixedbpath.size() + 100); DWORD cchBufferLength = static_cast(tvolume.size()); BOOL b = GetVolumePathNameW(ConvertToWchar(prefixedbpath).c_str(), &tvolume[0], cchBufferLength); if (!b) { return false; } std::string volume = ConvertFromWchar(tvolume.c_str()); if (volume.find("\\\\?\\") == 0 && volume.find("\\\\?\\GLOBALROOT") != 0) { volume.erase(0, 4); } if (!volume.empty() && volume[volume.size() - 1] == os_file_sep()[0]) { volume = volume.substr(0, volume.size() - 1); } HANDLE hVol = CreateFileW(ConvertToWchar("\\\\.\\"+volume).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hVol == INVALID_HANDLE_VALUE) { return false; } b = FlushFileBuffers(hVol); CloseHandle(hVol); return b == TRUE; } std::string os_last_error_str() { std::string msg; int64 code = os_last_error(msg); if (code == 0) { return "Code 0"; } return msg + " (code: " + convert(code) + ")"; } int os_popen(const std::string& cmd, std::string& ret) { ret.clear(); FILE* in = NULL; #ifndef _WIN32 #define _popen popen #define _pclose pclose #endif in = _popen(cmd.c_str(), "r"); if(in==NULL) { return -1; } char buf[4096]; size_t read; do { read=fread(buf, 1, sizeof(buf), in); if(read>0) { ret.append(buf, buf+read); } } while(read==sizeof(buf)); return _pclose(in); } int64 os_last_error(std::string& message) { DWORD last_error = GetLastError(); message = os_format_errcode(last_error); return last_error; } std::string os_format_errcode(int64 errcode) { DWORD last_error = static_cast(errcode); wchar_t* output = NULL; DWORD r = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, HRESULT_FROM_WIN32(last_error), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&output), 0, NULL); std::string message; if (r>0 && output != NULL) { std::wstring wmessage; wmessage.resize(r); memcpy(&wmessage[0], output, r*sizeof(wchar_t)); message = trim(ConvertFromWchar(wmessage)); } if (output != NULL) { LocalFree(output); } return message; } bool os_enable_background_priority(SPrioInfo& prio_info) { #ifdef THREAD_MODE_BACKGROUND_BEGIN return SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN) == TRUE; #else return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST)==TRUE; #endif } bool os_disable_background_priority(SPrioInfo& prio_info) { #ifdef THREAD_MODE_BACKGROUND_BEGIN return SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END) == TRUE; #else return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL)==TRUE; #endif } SPrioInfo::SPrioInfo() { } SPrioInfo::~SPrioInfo() { } bool os_enable_prioritize(SPrioInfo & prio_info, EPrio prio) { switch (prio) { case Prio_Prioritize: return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) == TRUE; case Prio_SlightBackground: return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL) == TRUE; case Prio_SlightPrioritize: return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) == TRUE; default: assert(false); return false; } } bool os_disable_prioritize(SPrioInfo & prio_info) { return SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL) == TRUE; } size_t os_get_num_cpus() { SYSTEM_INFO system_info; GetSystemInfo(&system_info); return system_info.dwNumberOfProcessors; } int os_system(const std::string & cmd) { return system(cmd.c_str()); }