win_file.c 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497
  1. /*
  2. * Copyright (C) 2023 Intel Corporation. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "platform_api_extension.h"
  6. #include "libc_errno.h"
  7. #include "win_util.h"
  8. #include "PathCch.h"
  9. #pragma comment(lib, "Pathcch.lib")
  10. #define CHECK_VALID_HANDLE_WITH_RETURN_VALUE(win_handle, ret) \
  11. do { \
  12. if ((win_handle) == NULL \
  13. || ((win_handle)->type == windows_handle_type_socket \
  14. && (win_handle)->raw.socket == INVALID_SOCKET) \
  15. || ((win_handle)->type == windows_handle_type_file \
  16. && (win_handle)->raw.handle == INVALID_HANDLE_VALUE)) \
  17. return (ret); \
  18. \
  19. } while (0)
  20. #define CHECK_VALID_HANDLE(win_handle) \
  21. CHECK_VALID_HANDLE_WITH_RETURN_VALUE(win_handle, __WASI_EBADF)
  22. #define CHECK_VALID_FILE_HANDLE(win_handle) \
  23. do { \
  24. if ((win_handle) == NULL) \
  25. return __WASI_EBADF; \
  26. \
  27. if ((win_handle)->type == windows_handle_type_socket) \
  28. return __WASI_EINVAL; \
  29. \
  30. if (((win_handle)->type == windows_handle_type_file \
  31. && (win_handle)->raw.handle == INVALID_HANDLE_VALUE)) \
  32. return __WASI_EBADF; \
  33. \
  34. } while (0)
  35. #define CHECK_VALID_WIN_DIR_STREAM(win_dir_stream) \
  36. do { \
  37. if ((win_dir_stream) == NULL) \
  38. return __WASI_EINVAL; \
  39. CHECK_VALID_FILE_HANDLE((win_dir_stream)->handle); \
  40. } while (0)
  41. static __wasi_errno_t
  42. convert_winsock_error_code(int error_code)
  43. {
  44. switch (error_code) {
  45. case WSASYSNOTREADY:
  46. case WSAEWOULDBLOCK:
  47. return __WASI_EAGAIN;
  48. case WSAVERNOTSUPPORTED:
  49. return __WASI_ENOTSUP;
  50. case WSAEINPROGRESS:
  51. return __WASI_EINPROGRESS;
  52. case WSAEPROCLIM:
  53. return __WASI_EBUSY;
  54. case WSAEFAULT:
  55. return __WASI_EFAULT;
  56. case WSAENETDOWN:
  57. return __WASI_ENETDOWN;
  58. case WSAENOTSOCK:
  59. return __WASI_ENOTSOCK;
  60. case WSAEINTR:
  61. return __WASI_EINTR;
  62. case WSAEAFNOSUPPORT:
  63. return __WASI_EAFNOSUPPORT;
  64. case WSAEMFILE:
  65. return __WASI_ENFILE;
  66. case WSAEINVAL:
  67. return __WASI_EINVAL;
  68. case WSAENOBUFS:
  69. return __WASI_ENOBUFS;
  70. case WSAEPROTONOSUPPORT:
  71. return __WASI_EPROTONOSUPPORT;
  72. case WSAEPROTOTYPE:
  73. return __WASI_EPROTOTYPE;
  74. case WSAESOCKTNOSUPPORT:
  75. return __WASI_ENOTSUP;
  76. case WSAEINVALIDPROCTABLE:
  77. case WSAEINVALIDPROVIDER:
  78. case WSAEPROVIDERFAILEDINIT:
  79. case WSANOTINITIALISED:
  80. default:
  81. return __WASI_EINVAL;
  82. }
  83. }
  84. static __wasi_filetype_t
  85. get_disk_filetype(DWORD attribute)
  86. {
  87. if (attribute == INVALID_FILE_ATTRIBUTES)
  88. return __WASI_FILETYPE_UNKNOWN;
  89. if (attribute & FILE_ATTRIBUTE_REPARSE_POINT)
  90. return __WASI_FILETYPE_SYMBOLIC_LINK;
  91. if (attribute & FILE_ATTRIBUTE_DIRECTORY)
  92. return __WASI_FILETYPE_DIRECTORY;
  93. return __WASI_FILETYPE_REGULAR_FILE;
  94. }
  95. static __wasi_filetype_t
  96. get_socket_filetype(SOCKET socket)
  97. {
  98. char socket_type = 0;
  99. int size = sizeof(socket_type);
  100. if (getsockopt(socket, SOL_SOCKET, SO_TYPE, &socket_type, &size) == 0) {
  101. switch (socket_type) {
  102. case SOCK_STREAM:
  103. return __WASI_FILETYPE_SOCKET_STREAM;
  104. case SOCK_DGRAM:
  105. return __WASI_FILETYPE_SOCKET_DGRAM;
  106. }
  107. }
  108. return __WASI_FILETYPE_UNKNOWN;
  109. }
  110. static __wasi_errno_t
  111. convert_windows_filetype(os_file_handle handle, DWORD filetype,
  112. __wasi_filetype_t *out_filetype)
  113. {
  114. __wasi_errno_t error = __WASI_ESUCCESS;
  115. switch (filetype) {
  116. case FILE_TYPE_DISK:
  117. FILE_ATTRIBUTE_TAG_INFO file_info;
  118. bool success = GetFileInformationByHandleEx(
  119. handle->raw.handle, FileAttributeTagInfo, &file_info,
  120. sizeof(file_info));
  121. if (!success
  122. || file_info.FileAttributes == INVALID_FILE_ATTRIBUTES) {
  123. error = convert_windows_error_code(GetLastError());
  124. break;
  125. }
  126. *out_filetype = get_disk_filetype(file_info.FileAttributes);
  127. break;
  128. case FILE_TYPE_CHAR:
  129. *out_filetype = __WASI_FILETYPE_CHARACTER_DEVICE;
  130. break;
  131. case FILE_TYPE_PIPE:
  132. if (handle->type == windows_handle_type_socket)
  133. *out_filetype = get_socket_filetype(handle->raw.socket);
  134. else
  135. *out_filetype = __WASI_FILETYPE_BLOCK_DEVICE;
  136. break;
  137. case FILE_TYPE_REMOTE:
  138. case FILE_TYPE_UNKNOWN:
  139. default:
  140. *out_filetype = __WASI_FILETYPE_UNKNOWN;
  141. }
  142. return error;
  143. }
  144. // Converts the input string to a wchar string.
  145. static __wasi_errno_t
  146. convert_to_wchar(const char *str, wchar_t *buf, size_t buf_size)
  147. {
  148. int converted_chars =
  149. MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, (int)buf_size);
  150. if (converted_chars == 0)
  151. return convert_windows_error_code(GetLastError());
  152. return __WASI_ESUCCESS;
  153. }
  154. // Get the filepath for a handle. The size of the buffer should be specified in
  155. // terms of wchar.
  156. static __wasi_errno_t
  157. get_handle_filepath(HANDLE handle, wchar_t *buf, DWORD buf_size)
  158. {
  159. DWORD bufsize_in_chars = buf_size * (sizeof(wchar_t) / sizeof(char));
  160. DWORD size = GetFinalPathNameByHandleW(
  161. handle, buf, bufsize_in_chars, FILE_NAME_NORMALIZED | VOLUME_NAME_NONE);
  162. if (size > bufsize_in_chars)
  163. return __WASI_ENAMETOOLONG;
  164. if (size == 0)
  165. return convert_windows_error_code(GetLastError());
  166. return __WASI_ESUCCESS;
  167. }
  168. static __wasi_errno_t
  169. convert_hresult_error_code(HRESULT error_code)
  170. {
  171. switch (error_code) {
  172. case E_OUTOFMEMORY:
  173. return __WASI_ENOMEM;
  174. case E_INVALIDARG:
  175. default:
  176. return __WASI_EINVAL;
  177. }
  178. }
  179. // Returns the absolute filepath from the relative path to the directory
  180. // associated with the provided handle.
  181. static __wasi_errno_t
  182. get_absolute_filepath(HANDLE handle, const char *relative_path,
  183. wchar_t *absolute_path, size_t buf_len)
  184. {
  185. wchar_t handle_path[PATH_MAX];
  186. __wasi_errno_t error = get_handle_filepath(handle, handle_path, PATH_MAX);
  187. if (error != __WASI_ESUCCESS)
  188. return error;
  189. wchar_t relative_wpath[PATH_MAX];
  190. error = convert_to_wchar(relative_path, relative_wpath, PATH_MAX);
  191. if (error != __WASI_ESUCCESS)
  192. return error;
  193. HRESULT ret =
  194. PathCchCombine(absolute_path, buf_len, handle_path, relative_wpath);
  195. if (ret != S_OK)
  196. error = convert_hresult_error_code(ret);
  197. return error;
  198. }
  199. static bool
  200. has_directory_attribute(DWORD attributes)
  201. {
  202. if (attributes == INVALID_FILE_ATTRIBUTES)
  203. return false;
  204. return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
  205. }
  206. static bool
  207. is_directory(const wchar_t *path)
  208. {
  209. DWORD attributes = GetFileAttributesW(path);
  210. return has_directory_attribute(attributes);
  211. }
  212. static bool
  213. has_symlink_attribute(DWORD attributes)
  214. {
  215. if (attributes == INVALID_FILE_ATTRIBUTES)
  216. return false;
  217. return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
  218. }
  219. static bool
  220. is_symlink(const wchar_t *path)
  221. {
  222. DWORD attributes = GetFileAttributesW(path);
  223. return has_symlink_attribute(attributes);
  224. }
  225. static void
  226. init_dir_stream(os_dir_stream dir_stream, os_file_handle handle)
  227. {
  228. dir_stream->cursor = 0;
  229. dir_stream->handle = handle;
  230. dir_stream->cookie = 0;
  231. }
  232. // Advances to the next directory entry and optionally reads into to the
  233. // provided buffer if not NULL.
  234. static __wasi_errno_t
  235. read_next_dir_entry(os_dir_stream dir_stream, FILE_ID_BOTH_DIR_INFO **out_entry)
  236. {
  237. FILE_INFO_BY_HANDLE_CLASS file_info_class;
  238. if (dir_stream->cookie == 0)
  239. file_info_class = FileIdBothDirectoryRestartInfo;
  240. else
  241. file_info_class = FileIdBothDirectoryInfo;
  242. if (dir_stream->cursor == 0
  243. && !GetFileInformationByHandleEx(dir_stream->handle->raw.handle,
  244. file_info_class, dir_stream->info_buf,
  245. sizeof(dir_stream->info_buf))) {
  246. if (out_entry != NULL)
  247. *out_entry = NULL;
  248. DWORD win_error = GetLastError();
  249. // We've reached the end of the directory - return success
  250. if (win_error == ERROR_NO_MORE_FILES) {
  251. dir_stream->cookie = 0;
  252. dir_stream->cursor = 0;
  253. return __WASI_ESUCCESS;
  254. }
  255. return convert_windows_error_code(win_error);
  256. }
  257. FILE_ID_BOTH_DIR_INFO *current_info =
  258. (FILE_ID_BOTH_DIR_INFO *)(dir_stream->info_buf + dir_stream->cursor);
  259. if (current_info->NextEntryOffset == 0)
  260. dir_stream->cursor = 0;
  261. else
  262. dir_stream->cursor += current_info->NextEntryOffset;
  263. ++dir_stream->cookie;
  264. if (out_entry != NULL)
  265. *out_entry = current_info;
  266. else
  267. return __WASI_ESUCCESS;
  268. // Convert and copy over the wchar filename into the entry_name buf
  269. int ret = WideCharToMultiByte(
  270. CP_UTF8, 0, current_info->FileName,
  271. current_info->FileNameLength / (sizeof(wchar_t) / sizeof(char)),
  272. dir_stream->current_entry_name, sizeof(dir_stream->current_entry_name),
  273. NULL, NULL);
  274. if (ret == 0)
  275. return convert_windows_error_code(GetLastError());
  276. return __WASI_ESUCCESS;
  277. }
  278. static HANDLE
  279. create_handle(wchar_t *path, bool is_dir, bool follow_symlink, bool readonly)
  280. {
  281. CREATEFILE2_EXTENDED_PARAMETERS create_params;
  282. create_params.dwSize = sizeof(create_params);
  283. create_params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
  284. create_params.dwSecurityQosFlags = 0;
  285. create_params.dwFileFlags = 0;
  286. create_params.lpSecurityAttributes = NULL;
  287. create_params.hTemplateFile = NULL;
  288. if (is_dir) {
  289. create_params.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  290. create_params.dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS;
  291. }
  292. if (!follow_symlink)
  293. create_params.dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT;
  294. DWORD desired_access = GENERIC_READ;
  295. if (!readonly)
  296. desired_access |= GENERIC_WRITE;
  297. else
  298. create_params.dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
  299. return CreateFile2(path, desired_access,
  300. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  301. OPEN_EXISTING, &create_params);
  302. }
  303. #if WINAPI_PARTITION_DESKTOP == 0
  304. // Modifies the given path in place and replaces it with the filename component
  305. // (including the extension) of the path.
  306. static __wasi_errno_t
  307. extract_filename_from_path(wchar_t *path, size_t buf_size)
  308. {
  309. wchar_t extension[256];
  310. wchar_t filename[256];
  311. __wasi_errno_t error = __WASI_ESUCCESS;
  312. // Get the filename from the fullpath.
  313. errno_t ret =
  314. _wsplitpath_s(path, NULL, 0, NULL, 0, filename, 256, extension, 256);
  315. if (ret != 0) {
  316. error = convert_errno(ret);
  317. return error;
  318. }
  319. ret = wcscat_s(filename, 256, extension);
  320. if (ret != 0) {
  321. error = convert_errno(ret);
  322. return error;
  323. }
  324. ret = wcscpy_s(path, buf_size, filename);
  325. if (ret != 0)
  326. error = convert_errno(ret);
  327. return error;
  328. }
  329. static __wasi_errno_t
  330. get_handle_to_parent_directory(HANDLE handle, HANDLE *out_dir_handle)
  331. {
  332. wchar_t path[PATH_MAX];
  333. __wasi_errno_t error = get_handle_filepath(handle, path, PATH_MAX);
  334. if (error != __WASI_ESUCCESS)
  335. return error;
  336. wchar_t parent_dir_path[PATH_MAX];
  337. errno_t ret = wcscpy_s(parent_dir_path, PATH_MAX, path);
  338. if (ret != 0) {
  339. error = convert_errno(ret);
  340. return error;
  341. }
  342. ret = wcscat_s(parent_dir_path, PATH_MAX, L"/..");
  343. if (ret != 0) {
  344. error = convert_errno(ret);
  345. return error;
  346. }
  347. HANDLE dir_handle = create_handle(parent_dir_path, true, true, true);
  348. if (dir_handle == INVALID_HANDLE_VALUE) {
  349. error = convert_windows_error_code(GetLastError());
  350. return error;
  351. }
  352. *out_dir_handle = dir_handle;
  353. return error;
  354. }
  355. // The easiest way to get all the necessary file information for files is to
  356. // open a handle to the parent directory and iterate through the entries via
  357. // FileIdBothDirectoryInfo. Other file information classes are only
  358. // available on desktop.
  359. static __wasi_errno_t
  360. get_disk_file_information(HANDLE handle, __wasi_filestat_t *buf)
  361. {
  362. __wasi_errno_t error = __WASI_ESUCCESS;
  363. HANDLE raw_dir_handle = INVALID_HANDLE_VALUE;
  364. wchar_t path[PATH_MAX] = L".";
  365. if (buf->st_filetype != __WASI_FILETYPE_DIRECTORY) {
  366. error = get_handle_filepath(handle, path, PATH_MAX);
  367. if (error != __WASI_ESUCCESS)
  368. goto fail;
  369. error = get_handle_to_parent_directory(handle, &raw_dir_handle);
  370. if (error != __WASI_ESUCCESS)
  371. goto fail;
  372. error = extract_filename_from_path(path, PATH_MAX);
  373. if (error != __WASI_ESUCCESS)
  374. goto fail;
  375. }
  376. else {
  377. raw_dir_handle = handle;
  378. }
  379. windows_handle dir_handle = { .access_mode = windows_access_mode_read,
  380. .raw = { .handle = raw_dir_handle },
  381. .fdflags = 0,
  382. .type = windows_handle_type_file };
  383. windows_dir_stream dir_stream;
  384. init_dir_stream(&dir_stream, &dir_handle);
  385. do {
  386. FILE_ID_BOTH_DIR_INFO *file_id_both_dir_info = NULL;
  387. __wasi_errno_t error =
  388. read_next_dir_entry(&dir_stream, &file_id_both_dir_info);
  389. if (error != __WASI_ESUCCESS || file_id_both_dir_info == NULL)
  390. goto fail;
  391. const DWORD filename_length = file_id_both_dir_info->FileNameLength
  392. / (sizeof(wchar_t) / sizeof(char));
  393. if (wcsncmp(file_id_both_dir_info->FileName, path, filename_length)
  394. == 0) {
  395. buf->st_ino =
  396. (__wasi_inode_t)(file_id_both_dir_info->FileId.QuadPart);
  397. buf->st_atim = convert_filetime_to_wasi_timestamp(
  398. (LPFILETIME)&file_id_both_dir_info->LastAccessTime.QuadPart);
  399. buf->st_mtim = convert_filetime_to_wasi_timestamp(
  400. (LPFILETIME)&file_id_both_dir_info->LastWriteTime.QuadPart);
  401. buf->st_ctim = convert_filetime_to_wasi_timestamp(
  402. (LPFILETIME)&file_id_both_dir_info->ChangeTime.QuadPart);
  403. buf->st_size =
  404. (__wasi_filesize_t)(file_id_both_dir_info->EndOfFile.QuadPart);
  405. break;
  406. }
  407. } while (dir_stream.cookie != 0);
  408. FILE_STANDARD_INFO file_standard_info;
  409. bool success = GetFileInformationByHandleEx(handle, FileStandardInfo,
  410. &file_standard_info,
  411. sizeof(file_standard_info));
  412. if (!success) {
  413. error = convert_windows_error_code(GetLastError());
  414. goto fail;
  415. }
  416. buf->st_nlink = (__wasi_linkcount_t)file_standard_info.NumberOfLinks;
  417. fail:
  418. if (buf->st_filetype != __WASI_FILETYPE_DIRECTORY
  419. && raw_dir_handle != INVALID_HANDLE_VALUE)
  420. CloseHandle(raw_dir_handle);
  421. return error;
  422. }
  423. #else
  424. static __wasi_errno_t
  425. get_disk_file_information(HANDLE handle, __wasi_filestat_t *buf)
  426. {
  427. __wasi_errno_t error = __WASI_ESUCCESS;
  428. FILE_BASIC_INFO file_basic_info;
  429. int ret = GetFileInformationByHandleEx(
  430. handle, FileBasicInfo, &file_basic_info, sizeof(file_basic_info));
  431. if (ret == 0) {
  432. error = convert_windows_error_code(GetLastError());
  433. return error;
  434. }
  435. buf->st_atim = convert_filetime_to_wasi_timestamp(
  436. (LPFILETIME)&file_basic_info.LastAccessTime.QuadPart);
  437. buf->st_mtim = convert_filetime_to_wasi_timestamp(
  438. (LPFILETIME)&file_basic_info.LastWriteTime.QuadPart);
  439. buf->st_ctim = convert_filetime_to_wasi_timestamp(
  440. (LPFILETIME)&file_basic_info.ChangeTime.QuadPart);
  441. BY_HANDLE_FILE_INFORMATION file_info;
  442. ret = GetFileInformationByHandle(handle, &file_info);
  443. if (ret == 0) {
  444. error = convert_windows_error_code(GetLastError());
  445. return error;
  446. }
  447. ULARGE_INTEGER file_size = { .LowPart = file_info.nFileSizeLow,
  448. .HighPart = file_info.nFileSizeHigh };
  449. buf->st_size = (__wasi_filesize_t)(file_size.QuadPart);
  450. ULARGE_INTEGER file_id = { .LowPart = file_info.nFileIndexLow,
  451. .HighPart = file_info.nFileIndexHigh };
  452. buf->st_ino = (__wasi_inode_t)(file_id.QuadPart);
  453. buf->st_dev = (__wasi_device_t)file_info.dwVolumeSerialNumber;
  454. buf->st_nlink = (__wasi_linkcount_t)file_info.nNumberOfLinks;
  455. return error;
  456. }
  457. #endif /* end of WINAPI_PARTITION_DESKTOP == 0 */
  458. static __wasi_errno_t
  459. get_file_information(os_file_handle handle, __wasi_filestat_t *buf)
  460. {
  461. __wasi_errno_t error = __WASI_ESUCCESS;
  462. DWORD windows_filetype = GetFileType(handle->raw.handle);
  463. error =
  464. convert_windows_filetype(handle, windows_filetype, &buf->st_filetype);
  465. if (error != __WASI_ESUCCESS)
  466. return error;
  467. buf->st_dev = 0;
  468. if (windows_filetype != FILE_TYPE_DISK) {
  469. buf->st_atim = 0;
  470. buf->st_ctim = 0;
  471. buf->st_mtim = 0;
  472. buf->st_nlink = 0;
  473. buf->st_size = 0;
  474. buf->st_ino = 0;
  475. return error;
  476. }
  477. return get_disk_file_information(handle->raw.handle, buf);
  478. }
  479. __wasi_errno_t
  480. os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf)
  481. {
  482. CHECK_VALID_HANDLE(handle);
  483. return get_file_information(handle, buf);
  484. }
  485. __wasi_errno_t
  486. os_fstatat(os_file_handle handle, const char *path,
  487. struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags)
  488. {
  489. CHECK_VALID_FILE_HANDLE(handle);
  490. return __WASI_ENOSYS;
  491. }
  492. __wasi_errno_t
  493. os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
  494. {
  495. CHECK_VALID_HANDLE(handle);
  496. *flags = handle->fdflags;
  497. return __WASI_ESUCCESS;
  498. }
  499. __wasi_errno_t
  500. os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
  501. {
  502. CHECK_VALID_HANDLE(handle);
  503. return __WASI_ENOSYS;
  504. }
  505. __wasi_errno_t
  506. os_file_get_access_mode(os_file_handle handle,
  507. wasi_libc_file_access_mode *access_mode)
  508. {
  509. CHECK_VALID_HANDLE(handle);
  510. if ((handle->access_mode & windows_access_mode_read) != 0
  511. && (handle->access_mode & windows_access_mode_write) != 0)
  512. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  513. else if ((handle->access_mode & windows_access_mode_write) != 0)
  514. *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
  515. else
  516. *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
  517. return __WASI_ESUCCESS;
  518. }
  519. __wasi_errno_t
  520. os_fdatasync(os_file_handle handle)
  521. {
  522. CHECK_VALID_FILE_HANDLE(handle);
  523. return __WASI_ENOSYS;
  524. }
  525. __wasi_errno_t
  526. os_fsync(os_file_handle handle)
  527. {
  528. CHECK_VALID_FILE_HANDLE(handle);
  529. return __WASI_ENOSYS;
  530. }
  531. __wasi_errno_t
  532. os_open_preopendir(const char *path, os_file_handle *out)
  533. {
  534. *out = NULL;
  535. wchar_t wpath[PATH_MAX];
  536. __wasi_errno_t error = convert_to_wchar(path, wpath, PATH_MAX);
  537. if (error != __WASI_ESUCCESS)
  538. return error;
  539. HANDLE dir_handle = create_handle(wpath, true, true, true);
  540. if (dir_handle == INVALID_HANDLE_VALUE)
  541. return convert_windows_error_code(GetLastError());
  542. *out = BH_MALLOC(sizeof(windows_handle));
  543. if (*out == NULL) {
  544. CloseHandle(dir_handle);
  545. return __WASI_ENOMEM;
  546. }
  547. (*out)->type = windows_handle_type_file;
  548. (*out)->raw.handle = dir_handle;
  549. (*out)->fdflags = 0;
  550. (*out)->access_mode = windows_access_mode_read;
  551. return error;
  552. }
  553. __wasi_errno_t
  554. os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags,
  555. __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags,
  556. wasi_libc_file_access_mode access_mode, os_file_handle *out)
  557. {
  558. CHECK_VALID_FILE_HANDLE(handle);
  559. *out = BH_MALLOC(sizeof(windows_handle));
  560. if (*out == NULL)
  561. return __WASI_ENOMEM;
  562. (*out)->type = windows_handle_type_file;
  563. (*out)->fdflags = fs_flags;
  564. (*out)->raw.handle = INVALID_HANDLE_VALUE;
  565. DWORD attributes = FILE_FLAG_BACKUP_SEMANTICS;
  566. if ((fs_flags & (__WASI_FDFLAG_SYNC | __WASI_FDFLAG_RSYNC)) != 0)
  567. attributes |= (FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING);
  568. if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0)
  569. attributes |= FILE_FLAG_WRITE_THROUGH;
  570. if ((oflags & __WASI_O_DIRECTORY) != 0) {
  571. attributes |= FILE_ATTRIBUTE_DIRECTORY;
  572. oflags &= ~(__WASI_O_DIRECTORY);
  573. }
  574. // Use async operations on the handle if it's not a directory
  575. else {
  576. attributes |= FILE_FLAG_OVERLAPPED;
  577. }
  578. __wasi_errno_t error = __WASI_ESUCCESS;
  579. DWORD access_flags = 0;
  580. if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) {
  581. if ((attributes & (FILE_FLAG_NO_BUFFERING)) != 0) {
  582. // FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually
  583. // exclusive - CreateFile2 returns 87 (invalid parameter) when they
  584. // are combined.
  585. error = __WASI_ENOTSUP;
  586. goto fail;
  587. }
  588. access_flags |= FILE_APPEND_DATA;
  589. }
  590. switch (access_mode) {
  591. case WASI_LIBC_ACCESS_MODE_READ_ONLY:
  592. access_flags |= GENERIC_READ;
  593. (*out)->access_mode = windows_access_mode_read;
  594. break;
  595. case WASI_LIBC_ACCESS_MODE_WRITE_ONLY:
  596. access_flags |= GENERIC_WRITE;
  597. (*out)->access_mode = windows_access_mode_write;
  598. break;
  599. case WASI_LIBC_ACCESS_MODE_READ_WRITE:
  600. access_flags |= GENERIC_WRITE | GENERIC_READ;
  601. (*out)->access_mode =
  602. windows_access_mode_read | windows_access_mode_write;
  603. break;
  604. }
  605. DWORD creation_disposition = 0;
  606. switch (oflags) {
  607. case __WASI_O_CREAT | __WASI_O_EXCL:
  608. case __WASI_O_CREAT | __WASI_O_EXCL | __WASI_O_TRUNC:
  609. creation_disposition = CREATE_NEW;
  610. break;
  611. case __WASI_O_CREAT | __WASI_O_TRUNC:
  612. creation_disposition = CREATE_ALWAYS;
  613. break;
  614. case __WASI_O_CREAT:
  615. creation_disposition = OPEN_ALWAYS;
  616. break;
  617. case 0:
  618. case __WASI_O_EXCL:
  619. creation_disposition = OPEN_EXISTING;
  620. break;
  621. case __WASI_O_TRUNC:
  622. case __WASI_O_EXCL | __WASI_O_TRUNC:
  623. creation_disposition = TRUNCATE_EXISTING;
  624. // CreateFile2 requires write access if we truncate the file upon
  625. // opening
  626. access_flags |= GENERIC_WRITE;
  627. break;
  628. }
  629. wchar_t absolute_path[PATH_MAX];
  630. error = get_absolute_filepath(handle->raw.handle, path, absolute_path,
  631. PATH_MAX);
  632. if (error != __WASI_ESUCCESS)
  633. goto fail;
  634. if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0)
  635. attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
  636. // Check that we're not trying to open an existing file as a directory.
  637. // Windows doesn't seem to throw an error in this case so add an
  638. // explicit check.
  639. if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
  640. && creation_disposition == OPEN_EXISTING
  641. && !is_directory(absolute_path)) {
  642. error = __WASI_ENOTDIR;
  643. goto fail;
  644. }
  645. CREATEFILE2_EXTENDED_PARAMETERS create_params;
  646. create_params.dwSize = sizeof(create_params);
  647. create_params.dwFileAttributes = attributes & 0xFFF;
  648. create_params.dwFileFlags = attributes & 0xFFF00000;
  649. create_params.dwSecurityQosFlags = 0;
  650. create_params.lpSecurityAttributes = NULL;
  651. create_params.hTemplateFile = NULL;
  652. (*out)->raw.handle =
  653. CreateFile2(absolute_path, access_flags,
  654. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  655. creation_disposition, &create_params);
  656. if ((*out)->raw.handle == INVALID_HANDLE_VALUE) {
  657. error = convert_windows_error_code(GetLastError());
  658. goto fail;
  659. }
  660. return error;
  661. fail:
  662. if (*out != NULL) {
  663. if ((*out)->raw.handle != INVALID_HANDLE_VALUE)
  664. CloseHandle((*out)->raw.handle);
  665. BH_FREE(*out);
  666. }
  667. return error;
  668. }
  669. __wasi_errno_t
  670. os_close(os_file_handle handle, bool is_stdio)
  671. {
  672. CHECK_VALID_HANDLE(handle);
  673. // We don't own the underlying raw handle so just free the handle and return
  674. // success.
  675. if (is_stdio) {
  676. BH_FREE(handle);
  677. return __WASI_ESUCCESS;
  678. }
  679. switch (handle->type) {
  680. case windows_handle_type_file:
  681. bool success = CloseHandle(handle->raw.handle);
  682. if (!success)
  683. return convert_windows_error_code(GetLastError());
  684. break;
  685. case windows_handle_type_socket:
  686. int ret = closesocket(handle->raw.socket);
  687. if (ret != 0)
  688. return convert_winsock_error_code(WSAGetLastError());
  689. break;
  690. default:
  691. assert(false && "unreachable");
  692. }
  693. BH_FREE(handle);
  694. return __WASI_ESUCCESS;
  695. }
  696. static __wasi_errno_t
  697. read_data_at_offset(HANDLE handle, const struct __wasi_iovec_t *iov, int iovcnt,
  698. __wasi_filesize_t offset, size_t *nwritten)
  699. {
  700. OVERLAPPED *read_operations =
  701. BH_MALLOC((uint32_t)(sizeof(OVERLAPPED) * (uint32_t)iovcnt));
  702. if (read_operations == NULL)
  703. return __WASI_ENOMEM;
  704. ULARGE_INTEGER query_offset = { .QuadPart = offset };
  705. __wasi_errno_t error = __WASI_ESUCCESS;
  706. size_t total_bytes_read = 0;
  707. const __wasi_iovec_t *current = iov;
  708. int successful_read_count = 0;
  709. for (int i = 0; i < iovcnt; ++i, ++current) {
  710. read_operations[i].Internal = 0;
  711. read_operations[i].InternalHigh = 0;
  712. read_operations[i].Offset = query_offset.LowPart;
  713. read_operations[i].OffsetHigh = query_offset.HighPart;
  714. read_operations[i].hEvent = NULL;
  715. if (!ReadFileEx(handle, current->buf, (DWORD)current->buf_len,
  716. &read_operations[i], NULL)) {
  717. DWORD win_error = GetLastError();
  718. if (win_error != ERROR_IO_PENDING) {
  719. error = convert_windows_error_code(win_error);
  720. break;
  721. }
  722. }
  723. ++successful_read_count;
  724. query_offset.QuadPart += (DWORD)current->buf_len;
  725. }
  726. // Get the result of all the asynchronous read operations
  727. for (int i = 0; i < successful_read_count; ++i) {
  728. DWORD bytes_transferred = 0;
  729. if (!GetOverlappedResult(handle, &read_operations[i],
  730. &bytes_transferred, true)) {
  731. DWORD win_error = GetLastError();
  732. if (win_error != ERROR_HANDLE_EOF)
  733. error = convert_windows_error_code(win_error);
  734. else
  735. total_bytes_read += (size_t)bytes_transferred;
  736. CancelIo(handle);
  737. for (int j = i + 1; j < iovcnt; ++j) {
  738. GetOverlappedResult(handle, &read_operations[j],
  739. &bytes_transferred, true);
  740. }
  741. break;
  742. }
  743. total_bytes_read += (size_t)bytes_transferred;
  744. }
  745. *nwritten = total_bytes_read;
  746. BH_FREE(read_operations);
  747. return error;
  748. }
  749. __wasi_errno_t
  750. os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  751. __wasi_filesize_t offset, size_t *nread)
  752. {
  753. CHECK_VALID_FILE_HANDLE(handle);
  754. return read_data_at_offset(handle->raw.handle, iov, iovcnt, offset, nread);
  755. }
  756. __wasi_errno_t
  757. os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  758. size_t *nread)
  759. {
  760. CHECK_VALID_HANDLE(handle);
  761. LARGE_INTEGER current_offset = { .QuadPart = 0 };
  762. // Seek to the current offset before reading
  763. int ret = SetFilePointerEx(handle->raw.handle, current_offset,
  764. &current_offset, FILE_CURRENT);
  765. if (ret == 0)
  766. return convert_windows_error_code(GetLastError());
  767. __wasi_errno_t error =
  768. read_data_at_offset(handle->raw.handle, iov, iovcnt,
  769. (__wasi_filesize_t)current_offset.QuadPart, nread);
  770. if (error != __WASI_ESUCCESS)
  771. return error;
  772. current_offset.QuadPart += (LONGLONG)(*nread);
  773. // Update the current offset to match how many bytes we've read
  774. ret =
  775. SetFilePointerEx(handle->raw.handle, current_offset, NULL, FILE_BEGIN);
  776. if (ret == 0)
  777. error = convert_windows_error_code(GetLastError());
  778. return error;
  779. }
  780. static __wasi_errno_t
  781. write_data_at_offset(HANDLE handle, const struct __wasi_ciovec_t *iov,
  782. int iovcnt, __wasi_filesize_t offset, size_t *nwritten)
  783. {
  784. OVERLAPPED *write_operations =
  785. BH_MALLOC((uint32_t)(sizeof(OVERLAPPED) * (uint32_t)iovcnt));
  786. if (write_operations == NULL)
  787. return __WASI_ENOMEM;
  788. ULARGE_INTEGER query_offset = { .QuadPart = offset };
  789. __wasi_errno_t error = __WASI_ESUCCESS;
  790. size_t total_bytes_written = 0;
  791. const __wasi_ciovec_t *current = iov;
  792. int successful_write_count = 0;
  793. for (int i = 0; i < iovcnt; ++i, ++current) {
  794. write_operations[i].Internal = 0;
  795. write_operations[i].InternalHigh = 0;
  796. write_operations[i].Offset = query_offset.LowPart;
  797. write_operations[i].OffsetHigh = query_offset.HighPart;
  798. write_operations[i].hEvent = NULL;
  799. if (!WriteFileEx(handle, current->buf, (DWORD)current->buf_len,
  800. &write_operations[i], NULL)) {
  801. DWORD win_error = GetLastError();
  802. if (win_error != ERROR_IO_PENDING) {
  803. error = convert_windows_error_code(win_error);
  804. break;
  805. }
  806. }
  807. ++successful_write_count;
  808. query_offset.QuadPart += (DWORD)current->buf_len;
  809. }
  810. // Get the result of all the asynchronous writes
  811. for (int i = 0; i < successful_write_count; ++i) {
  812. DWORD bytes_transferred = 0;
  813. if (!GetOverlappedResult(handle, &write_operations[i],
  814. &bytes_transferred, true)) {
  815. error = convert_windows_error_code(GetLastError());
  816. CancelIo(handle);
  817. for (int j = i + 1; j < iovcnt; ++j) {
  818. GetOverlappedResult(handle, &write_operations[j],
  819. &bytes_transferred, true);
  820. }
  821. break;
  822. }
  823. total_bytes_written += (size_t)bytes_transferred;
  824. }
  825. *nwritten = total_bytes_written;
  826. BH_FREE(write_operations);
  827. return error;
  828. }
  829. __wasi_errno_t
  830. os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  831. __wasi_filesize_t offset, size_t *nwritten)
  832. {
  833. CHECK_VALID_FILE_HANDLE(handle);
  834. return write_data_at_offset(handle->raw.handle, iov, iovcnt, offset,
  835. nwritten);
  836. }
  837. __wasi_errno_t
  838. os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  839. size_t *nwritten)
  840. {
  841. CHECK_VALID_HANDLE(handle);
  842. bool append = (handle->fdflags & __WASI_FDFLAG_APPEND) != 0;
  843. LARGE_INTEGER write_offset = { .QuadPart = 0 };
  844. DWORD move_method = append ? FILE_END : FILE_CURRENT;
  845. int ret = SetFilePointerEx(handle->raw.handle, write_offset, &write_offset,
  846. move_method);
  847. if (ret == 0)
  848. return convert_windows_error_code(GetLastError());
  849. __wasi_errno_t error = write_data_at_offset(
  850. handle->raw.handle, iov, iovcnt,
  851. (__wasi_filesize_t)write_offset.QuadPart, nwritten);
  852. if (error != __WASI_ESUCCESS)
  853. return error;
  854. write_offset.QuadPart += (LONGLONG)(*nwritten);
  855. // Update the write offset to match how many bytes we've written
  856. ret = SetFilePointerEx(handle->raw.handle, write_offset, NULL, FILE_BEGIN);
  857. if (ret == 0)
  858. error = convert_windows_error_code(GetLastError());
  859. return error;
  860. }
  861. __wasi_errno_t
  862. os_fallocate(os_file_handle handle, __wasi_filesize_t offset,
  863. __wasi_filesize_t length)
  864. {
  865. CHECK_VALID_FILE_HANDLE(handle);
  866. return __WASI_ENOSYS;
  867. }
  868. __wasi_errno_t
  869. os_ftruncate(os_file_handle handle, __wasi_filesize_t size)
  870. {
  871. CHECK_VALID_FILE_HANDLE(handle);
  872. return __WASI_ENOSYS;
  873. }
  874. __wasi_errno_t
  875. os_futimens(os_file_handle handle, __wasi_timestamp_t access_time,
  876. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags)
  877. {
  878. CHECK_VALID_FILE_HANDLE(handle);
  879. return __WASI_ENOSYS;
  880. }
  881. __wasi_errno_t
  882. os_utimensat(os_file_handle handle, const char *path,
  883. __wasi_timestamp_t access_time,
  884. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags,
  885. __wasi_lookupflags_t lookup_flags)
  886. {
  887. CHECK_VALID_FILE_HANDLE(handle);
  888. return __WASI_ENOSYS;
  889. }
  890. __wasi_errno_t
  891. os_readlinkat(os_file_handle handle, const char *path, char *buf,
  892. size_t bufsize, size_t *nread)
  893. {
  894. CHECK_VALID_FILE_HANDLE(handle);
  895. wchar_t symlink_path[PATH_MAX];
  896. __wasi_errno_t error =
  897. get_absolute_filepath(handle->raw.handle, path, symlink_path, PATH_MAX);
  898. if (error != __WASI_ESUCCESS)
  899. return error;
  900. DWORD symlink_attributes = GetFileAttributesW(symlink_path);
  901. if (!has_symlink_attribute(symlink_attributes))
  902. return __WASI_EINVAL;
  903. HANDLE link_handle = create_handle(
  904. symlink_path, has_directory_attribute(symlink_attributes), false, true);
  905. if (link_handle == INVALID_HANDLE_VALUE)
  906. return convert_windows_error_code(GetLastError());
  907. #if WINAPI_PARTITION_DESKTOP != 0
  908. // MinGW32 already has a definition for REPARSE_DATA_BUFFER
  909. #if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
  910. // See
  911. // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer
  912. // for more details.
  913. typedef struct _REPARSE_DATA_BUFFER {
  914. ULONG ReparseTag;
  915. USHORT ReparseDataLength;
  916. USHORT Reserved;
  917. union {
  918. struct {
  919. USHORT SubstituteNameOffset;
  920. USHORT SubstituteNameLength;
  921. USHORT PrintNameOffset;
  922. USHORT PrintNameLength;
  923. ULONG Flags;
  924. WCHAR PathBuffer[1];
  925. } SymbolicLinkReparseBuffer;
  926. struct {
  927. USHORT SubstituteNameOffset;
  928. USHORT SubstituteNameLength;
  929. USHORT PrintNameOffset;
  930. USHORT PrintNameLength;
  931. WCHAR PathBuffer[1];
  932. } MountPointReparseBuffer;
  933. struct {
  934. UCHAR DataBuffer[1];
  935. } GenericReparseBuffer;
  936. } DUMMYUNIONNAME;
  937. } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
  938. #endif
  939. char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  940. REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)buffer;
  941. if (!DeviceIoControl(link_handle, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer,
  942. sizeof(buffer), NULL, NULL)) {
  943. error = convert_windows_error_code(GetLastError());
  944. goto fail;
  945. }
  946. int wbufsize = 0;
  947. wchar_t *wbuf = NULL;
  948. // The following checks are taken from the libuv windows filesystem
  949. // implementation,
  950. // https://github.com/libuv/libuv/blob/v1.x/src/win/fs.c#L181-L244. Real
  951. // symlinks can contain pretty much anything, but the only thing we really
  952. // care about is undoing the implicit conversion to an NT namespaced path
  953. // that CreateSymbolicLink will perform on absolute paths.
  954. if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
  955. wbuf = reparse_data->SymbolicLinkReparseBuffer.PathBuffer
  956. + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset
  957. / sizeof(wchar_t));
  958. wbufsize = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength
  959. / sizeof(wchar_t);
  960. if (wbufsize >= 4 && wbuf[0] == L'\\' && wbuf[1] == L'?'
  961. && wbuf[2] == L'?' && wbuf[3] == L'\\') {
  962. // Starts with \??\
  963. if (wbufsize >= 6
  964. && ((wbuf[4] >= L'A' && wbuf[4] <= L'Z')
  965. || (wbuf[4] >= L'a' && wbuf[4] <= L'z'))
  966. && wbuf[5] == L':' && (wbufsize == 6 || wbuf[6] == L'\\'))
  967. {
  968. // \??\<drive>:\
  969. wbuf += 4;
  970. wbufsize -= 4;
  971. }
  972. else if (wbufsize >= 8 && (wbuf[4] == L'U' || wbuf[4] == L'u')
  973. && (wbuf[5] == L'N' || wbuf[5] == L'n')
  974. && (wbuf[6] == L'C' || wbuf[6] == L'c')
  975. && wbuf[7] == L'\\')
  976. {
  977. // \??\UNC\<server>\<share>\ - make sure the final path looks like \\<server>\<share>\
  978. wbuf += 6;
  979. wbuf[0] = L'\\';
  980. wbufsize -= 6;
  981. }
  982. }
  983. }
  984. else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
  985. // Junction
  986. wbuf = reparse_data->MountPointReparseBuffer.PathBuffer
  987. + (reparse_data->MountPointReparseBuffer.SubstituteNameOffset
  988. / sizeof(wchar_t));
  989. wbufsize = reparse_data->MountPointReparseBuffer.SubstituteNameLength
  990. / sizeof(wchar_t);
  991. // Only treat junctions that look like \??\<drive>:\ as a symlink.
  992. if (!(wbufsize >= 6 && wbuf[0] == L'\\' && wbuf[1] == L'?'
  993. && wbuf[2] == L'?' && wbuf[3] == L'\\'
  994. && ((wbuf[4] >= L'A' && wbuf[4] <= L'Z')
  995. || (wbuf[4] >= L'a' && wbuf[4] <= L'z'))
  996. && wbuf[5] == L':' && (wbufsize == 6 || wbuf[6] == L'\\'))) {
  997. error = __WASI_EINVAL;
  998. goto fail;
  999. }
  1000. /* Remove leading \??\ */
  1001. wbuf += 4;
  1002. wbufsize -= 4;
  1003. }
  1004. else {
  1005. error = __WASI_EINVAL;
  1006. goto fail;
  1007. }
  1008. if (wbuf != NULL)
  1009. *nread = (size_t)WideCharToMultiByte(CP_UTF8, 0, wbuf, wbufsize, buf,
  1010. (int)bufsize, NULL, NULL);
  1011. if (*nread == 0 && wbuf != NULL) {
  1012. DWORD win_error = GetLastError();
  1013. if (win_error == ERROR_INSUFFICIENT_BUFFER)
  1014. *nread = bufsize;
  1015. else
  1016. error = convert_windows_error_code(win_error);
  1017. }
  1018. #else
  1019. error = __WASI_ENOTSUP;
  1020. #endif
  1021. fail:
  1022. CloseHandle(link_handle);
  1023. return error;
  1024. }
  1025. __wasi_errno_t
  1026. os_linkat(os_file_handle from_handle, const char *from_path,
  1027. os_file_handle to_handle, const char *to_path,
  1028. __wasi_lookupflags_t lookup_flags)
  1029. {
  1030. CHECK_VALID_FILE_HANDLE(from_handle);
  1031. CHECK_VALID_FILE_HANDLE(to_handle);
  1032. return __WASI_ENOSYS;
  1033. }
  1034. __wasi_errno_t
  1035. os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path)
  1036. {
  1037. CHECK_VALID_FILE_HANDLE(handle);
  1038. return __WASI_ENOSYS;
  1039. }
  1040. __wasi_errno_t
  1041. os_mkdirat(os_file_handle handle, const char *path)
  1042. {
  1043. CHECK_VALID_FILE_HANDLE(handle);
  1044. wchar_t absolute_path[PATH_MAX];
  1045. __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path,
  1046. absolute_path, PATH_MAX);
  1047. if (error != __WASI_ESUCCESS)
  1048. return error;
  1049. bool success = CreateDirectoryW(absolute_path, NULL);
  1050. if (!success)
  1051. error = convert_windows_error_code(GetLastError());
  1052. return error;
  1053. }
  1054. __wasi_errno_t
  1055. os_renameat(os_file_handle old_handle, const char *old_path,
  1056. os_file_handle new_handle, const char *new_path)
  1057. {
  1058. CHECK_VALID_FILE_HANDLE(old_handle);
  1059. CHECK_VALID_FILE_HANDLE(new_handle);
  1060. return __WASI_ENOSYS;
  1061. }
  1062. __wasi_errno_t
  1063. os_unlinkat(os_file_handle handle, const char *path, bool is_dir)
  1064. {
  1065. CHECK_VALID_FILE_HANDLE(handle);
  1066. wchar_t absolute_path[PATH_MAX];
  1067. __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path,
  1068. absolute_path, PATH_MAX);
  1069. if (error != __WASI_ESUCCESS)
  1070. return error;
  1071. DWORD attributes = GetFileAttributesW(absolute_path);
  1072. if (has_symlink_attribute(attributes)) {
  1073. // Override is_dir for symlinks. A symlink to a directory counts
  1074. // as a directory itself in Windows.
  1075. is_dir = has_directory_attribute(attributes);
  1076. }
  1077. int ret =
  1078. is_dir ? RemoveDirectoryW(absolute_path) : DeleteFileW(absolute_path);
  1079. if (ret == 0)
  1080. error = convert_windows_error_code(GetLastError());
  1081. return error;
  1082. }
  1083. __wasi_errno_t
  1084. os_lseek(os_file_handle handle, __wasi_filedelta_t offset,
  1085. __wasi_whence_t whence, __wasi_filesize_t *new_offset)
  1086. {
  1087. CHECK_VALID_FILE_HANDLE(handle);
  1088. return __WASI_ENOSYS;
  1089. }
  1090. __wasi_errno_t
  1091. os_fadvise(os_file_handle handle, __wasi_filesize_t offset,
  1092. __wasi_filesize_t length, __wasi_advice_t advice)
  1093. {
  1094. CHECK_VALID_FILE_HANDLE(handle);
  1095. return __WASI_ENOSYS;
  1096. }
  1097. __wasi_errno_t
  1098. os_isatty(os_file_handle handle)
  1099. {
  1100. CHECK_VALID_HANDLE(handle);
  1101. DWORD console_mode;
  1102. return GetConsoleMode(handle->raw.handle, &console_mode) ? __WASI_ESUCCESS
  1103. : __WASI_ENOTTY;
  1104. }
  1105. static os_file_handle
  1106. create_stdio_handle(HANDLE raw_stdio_handle, DWORD stdio)
  1107. {
  1108. os_file_handle stdio_handle = BH_MALLOC(sizeof(windows_handle));
  1109. if (stdio_handle == NULL)
  1110. return NULL;
  1111. stdio_handle->type = windows_handle_type_file;
  1112. stdio_handle->access_mode =
  1113. windows_access_mode_read | windows_access_mode_write;
  1114. stdio_handle->fdflags = 0;
  1115. if (raw_stdio_handle == INVALID_HANDLE_VALUE)
  1116. raw_stdio_handle = GetStdHandle(stdio);
  1117. stdio_handle->raw.handle = raw_stdio_handle;
  1118. return stdio_handle;
  1119. }
  1120. os_file_handle
  1121. os_convert_stdin_handle(os_raw_file_handle raw_stdin)
  1122. {
  1123. return create_stdio_handle(raw_stdin, STD_INPUT_HANDLE);
  1124. }
  1125. os_file_handle
  1126. os_convert_stdout_handle(os_raw_file_handle raw_stdout)
  1127. {
  1128. return create_stdio_handle(raw_stdout, STD_OUTPUT_HANDLE);
  1129. }
  1130. os_file_handle
  1131. os_convert_stderr_handle(os_raw_file_handle raw_stderr)
  1132. {
  1133. return create_stdio_handle(raw_stderr, STD_ERROR_HANDLE);
  1134. }
  1135. __wasi_errno_t
  1136. os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream)
  1137. {
  1138. return __WASI_ENOSYS;
  1139. }
  1140. __wasi_errno_t
  1141. os_rewinddir(os_dir_stream dir_stream)
  1142. {
  1143. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1144. return __WASI_ENOSYS;
  1145. }
  1146. __wasi_errno_t
  1147. os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position)
  1148. {
  1149. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1150. return __WASI_ENOSYS;
  1151. }
  1152. __wasi_errno_t
  1153. os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry,
  1154. const char **d_name)
  1155. {
  1156. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1157. return __WASI_ENOSYS;
  1158. }
  1159. __wasi_errno_t
  1160. os_closedir(os_dir_stream dir_stream)
  1161. {
  1162. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1163. return __WASI_ENOSYS;
  1164. }
  1165. os_dir_stream
  1166. os_get_invalid_dir_stream()
  1167. {
  1168. return NULL;
  1169. }
  1170. bool
  1171. os_is_dir_stream_valid(os_dir_stream *dir_stream)
  1172. {
  1173. assert(dir_stream != NULL);
  1174. if (((*dir_stream) == NULL) || ((*dir_stream)->handle == NULL)
  1175. || ((*dir_stream)->handle->type != windows_handle_type_file)
  1176. || ((*dir_stream)->handle->raw.handle == INVALID_HANDLE_VALUE))
  1177. return false;
  1178. return true;
  1179. }
  1180. os_file_handle
  1181. os_get_invalid_handle()
  1182. {
  1183. return NULL;
  1184. }
  1185. bool
  1186. os_is_handle_valid(os_file_handle *handle)
  1187. {
  1188. assert(handle != NULL);
  1189. CHECK_VALID_HANDLE_WITH_RETURN_VALUE(*handle, false);
  1190. return true;
  1191. }
  1192. char *
  1193. os_realpath(const char *path, char *resolved_path)
  1194. {
  1195. resolved_path = _fullpath(resolved_path, path, PATH_MAX);
  1196. // Check the file/directory actually exists
  1197. DWORD attributes = GetFileAttributesA(resolved_path);
  1198. if (attributes == INVALID_FILE_ATTRIBUTES)
  1199. return NULL;
  1200. return resolved_path;
  1201. }