fs_win.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. #include "fs_win.h"
  2. #include "defaults.h"
  3. #include "maxlen.h"
  4. #include <util/folder/dirut.h>
  5. #include <util/charset/wide.h>
  6. #include "file.h"
  7. #include <winioctl.h>
  8. namespace NFsPrivate {
  9. static LPCWSTR UTF8ToWCHAR(const TStringBuf str, TUtf16String& wstr) {
  10. wstr.resize(str.size());
  11. size_t written = 0;
  12. if (!UTF8ToWide(str.data(), str.size(), wstr.begin(), written)) {
  13. return nullptr;
  14. }
  15. wstr.erase(written);
  16. static_assert(sizeof(WCHAR) == sizeof(wchar16), "expect sizeof(WCHAR) == sizeof(wchar16)");
  17. return (const WCHAR*)wstr.data();
  18. }
  19. static TString WCHARToUTF8(const LPWSTR wstr, size_t len) {
  20. static_assert(sizeof(WCHAR) == sizeof(wchar16), "expect sizeof(WCHAR) == sizeof(wchar16)");
  21. return WideToUTF8((wchar16*)wstr, len);
  22. }
  23. HANDLE CreateFileWithUtf8Name(const TStringBuf fName, ui32 accessMode, ui32 shareMode, ui32 createMode, ui32 attributes, bool inheritHandle) {
  24. TUtf16String wstr;
  25. LPCWSTR wname = UTF8ToWCHAR(fName, wstr);
  26. if (!wname) {
  27. ::SetLastError(ERROR_INVALID_NAME);
  28. return INVALID_HANDLE_VALUE;
  29. }
  30. SECURITY_ATTRIBUTES secAttrs;
  31. secAttrs.bInheritHandle = inheritHandle ? TRUE : FALSE;
  32. secAttrs.lpSecurityDescriptor = nullptr;
  33. secAttrs.nLength = sizeof(secAttrs);
  34. return ::CreateFileW(wname, accessMode, shareMode, &secAttrs, createMode, attributes, nullptr);
  35. }
  36. bool WinRename(const TString& oldPath, const TString& newPath) {
  37. TUtf16String op, np;
  38. LPCWSTR opPtr = UTF8ToWCHAR(oldPath, op);
  39. LPCWSTR npPtr = UTF8ToWCHAR(newPath, np);
  40. if (!opPtr || !npPtr) {
  41. ::SetLastError(ERROR_INVALID_NAME);
  42. return false;
  43. }
  44. return MoveFileExW(opPtr, npPtr, MOVEFILE_REPLACE_EXISTING) != 0;
  45. }
  46. bool WinRemove(const TString& path) {
  47. TUtf16String wstr;
  48. LPCWSTR wname = UTF8ToWCHAR(path, wstr);
  49. if (!wname) {
  50. ::SetLastError(ERROR_INVALID_NAME);
  51. return false;
  52. }
  53. WIN32_FILE_ATTRIBUTE_DATA fad;
  54. if (::GetFileAttributesExW(wname, GetFileExInfoStandard, &fad)) {
  55. if (fad.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
  56. fad.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
  57. ::SetFileAttributesW(wname, fad.dwFileAttributes);
  58. }
  59. if (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  60. return ::RemoveDirectoryW(wname) != 0;
  61. }
  62. return ::DeleteFileW(wname) != 0;
  63. }
  64. return false;
  65. }
  66. bool WinSymLink(const TString& targetName, const TString& linkName) {
  67. TString tName(targetName);
  68. {
  69. size_t pos;
  70. while ((pos = tName.find('/')) != TString::npos) {
  71. tName.replace(pos, 1, LOCSLASH_S);
  72. }
  73. }
  74. TUtf16String tstr;
  75. LPCWSTR wname = UTF8ToWCHAR(tName, tstr);
  76. TUtf16String lstr;
  77. LPCWSTR lname = UTF8ToWCHAR(linkName, lstr);
  78. // we can't create a dangling link to a dir in this way
  79. ui32 attr = ::GetFileAttributesW(wname);
  80. if (attr == INVALID_FILE_ATTRIBUTES) {
  81. TTempBuf result;
  82. if (GetFullPathNameW(lname, result.Size(), (LPWSTR)result.Data(), 0) != 0) {
  83. TString fullPath = WideToUTF8(TWtringBuf((const wchar16*)result.Data()));
  84. TStringBuf linkDir(fullPath);
  85. linkDir.RNextTok('\\');
  86. if (linkDir) {
  87. TString fullTarget(tName);
  88. resolvepath(fullTarget, TString{linkDir});
  89. TUtf16String fullTargetW;
  90. LPCWSTR ptrFullTarget = UTF8ToWCHAR(fullTarget, fullTargetW);
  91. attr = ::GetFileAttributesW(ptrFullTarget);
  92. }
  93. }
  94. }
  95. return 0 != CreateSymbolicLinkW(lname, wname, attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0);
  96. }
  97. bool WinHardLink(const TString& existingPath, const TString& newPath) {
  98. TUtf16String ep, np;
  99. LPCWSTR epPtr = UTF8ToWCHAR(existingPath, ep);
  100. LPCWSTR npPtr = UTF8ToWCHAR(newPath, np);
  101. if (!epPtr || !npPtr) {
  102. ::SetLastError(ERROR_INVALID_NAME);
  103. return false;
  104. }
  105. return (CreateHardLinkW(npPtr, epPtr, nullptr) != 0);
  106. }
  107. bool WinExists(const TString& path) {
  108. TUtf16String buf;
  109. LPCWSTR ptr = UTF8ToWCHAR(path, buf);
  110. return ::GetFileAttributesW(ptr) != INVALID_FILE_ATTRIBUTES;
  111. }
  112. TString WinCurrentWorkingDirectory() {
  113. TTempBuf result;
  114. LPWSTR buf = reinterpret_cast<LPWSTR>(result.Data());
  115. int r = GetCurrentDirectoryW(result.Size() / sizeof(WCHAR), buf);
  116. if (r == 0) {
  117. throw TIoSystemError() << "failed to GetCurrentDirectory";
  118. }
  119. return WCHARToUTF8(buf, r);
  120. }
  121. bool WinSetCurrentWorkingDirectory(const TString& path) {
  122. TUtf16String wstr;
  123. LPCWSTR wname = UTF8ToWCHAR(path, wstr);
  124. if (!wname) {
  125. ::SetLastError(ERROR_INVALID_NAME);
  126. return false;
  127. }
  128. return SetCurrentDirectoryW(wname);
  129. }
  130. bool WinMakeDirectory(const TString& path) {
  131. TUtf16String buf;
  132. LPCWSTR ptr = UTF8ToWCHAR(path, buf);
  133. return CreateDirectoryW(ptr, (LPSECURITY_ATTRIBUTES) nullptr);
  134. }
  135. // edited part of <Ntifs.h> from Windows DDK
  136. #define SYMLINK_FLAG_RELATIVE 1
  137. struct TReparseBufferHeader {
  138. USHORT SubstituteNameOffset;
  139. USHORT SubstituteNameLength;
  140. USHORT PrintNameOffset;
  141. USHORT PrintNameLength;
  142. };
  143. struct TSymbolicLinkReparseBuffer: public TReparseBufferHeader {
  144. ULONG Flags; // 0 or SYMLINK_FLAG_RELATIVE
  145. wchar16 PathBuffer[1];
  146. };
  147. struct TMountPointReparseBuffer: public TReparseBufferHeader {
  148. wchar16 PathBuffer[1];
  149. };
  150. struct TGenericReparseBuffer {
  151. wchar16 DataBuffer[1];
  152. };
  153. struct REPARSE_DATA_BUFFER {
  154. ULONG ReparseTag;
  155. USHORT ReparseDataLength;
  156. USHORT Reserved;
  157. union {
  158. TSymbolicLinkReparseBuffer SymbolicLinkReparseBuffer;
  159. TMountPointReparseBuffer MountPointReparseBuffer;
  160. TGenericReparseBuffer GenericReparseBuffer;
  161. };
  162. };
  163. // the end of edited part of <Ntifs.h>
  164. // For more info see:
  165. // * https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points
  166. // * https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/fsctl-get-reparse-point
  167. // * https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer
  168. REPARSE_DATA_BUFFER* ReadReparsePoint(HANDLE h, TTempBuf& buf) {
  169. while (true) {
  170. DWORD bytesReturned = 0;
  171. BOOL res = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, nullptr, 0, buf.Data(), buf.Size(), &bytesReturned, nullptr);
  172. if (res) {
  173. REPARSE_DATA_BUFFER* rdb = (REPARSE_DATA_BUFFER*)buf.Data();
  174. return rdb;
  175. } else {
  176. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  177. buf = TTempBuf(buf.Size() * 2);
  178. } else {
  179. return nullptr;
  180. }
  181. }
  182. }
  183. }
  184. TString WinReadLink(const TString& name) {
  185. TFileHandle h = CreateFileWithUtf8Name(name, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING,
  186. FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, true);
  187. if (h == INVALID_HANDLE_VALUE) {
  188. ythrow TIoSystemError() << "can't open file " << name;
  189. }
  190. TTempBuf buf;
  191. REPARSE_DATA_BUFFER* rdb = ReadReparsePoint(h, buf);
  192. if (rdb == nullptr) {
  193. ythrow TIoSystemError() << "can't read reparse point " << name;
  194. }
  195. if (rdb->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
  196. wchar16* str = (wchar16*)&rdb->SymbolicLinkReparseBuffer.PathBuffer[rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(wchar16)];
  197. size_t len = rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(wchar16);
  198. return WideToUTF8(str, len);
  199. } else if (rdb->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
  200. wchar16* str = (wchar16*)&rdb->MountPointReparseBuffer.PathBuffer[rdb->MountPointReparseBuffer.SubstituteNameOffset / sizeof(wchar16)];
  201. size_t len = rdb->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar16);
  202. return WideToUTF8(str, len);
  203. }
  204. // this reparse point is unsupported in arcadia
  205. return TString();
  206. }
  207. ULONG WinReadReparseTag(HANDLE h) {
  208. TTempBuf buf;
  209. REPARSE_DATA_BUFFER* rdb = ReadReparsePoint(h, buf);
  210. return rdb ? rdb->ReparseTag : 0;
  211. }
  212. // we can't use this function to get an analog of unix inode due to a lot of NTFS folders do not have this GUID
  213. // (it will be 'create' case really)
  214. /*
  215. bool GetObjectId(const char* path, GUID* id) {
  216. TFileHandle h = CreateFileWithUtf8Name(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  217. OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, true);
  218. if (h.IsOpen()) {
  219. FILE_OBJECTID_BUFFER fob;
  220. DWORD resSize = 0;
  221. if (DeviceIoControl(h, FSCTL_CREATE_OR_GET_OBJECT_ID, nullptr, 0, &fob, sizeof(fob), &resSize, nullptr)) {
  222. Y_ASSERT(resSize == sizeof(fob));
  223. memcpy(id, &fob.ObjectId, sizeof(GUID));
  224. return true;
  225. }
  226. }
  227. memset(id, 0, sizeof(GUID));
  228. return false;
  229. }
  230. */
  231. } // namespace NFsPrivate