win_file.c 54 KB

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