win_file.c 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447
  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 bool
  177. is_symlink(const wchar_t *path)
  178. {
  179. DWORD attributes = GetFileAttributesW(path);
  180. return has_symlink_attribute(attributes);
  181. }
  182. static void
  183. init_dir_stream(os_dir_stream dir_stream, os_file_handle handle)
  184. {
  185. dir_stream->cursor = 0;
  186. dir_stream->handle = handle;
  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. return __WASI_ENOSYS;
  448. }
  449. __wasi_errno_t
  450. os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
  451. {
  452. CHECK_VALID_HANDLE(handle);
  453. *flags = handle->fdflags;
  454. return __WASI_ESUCCESS;
  455. }
  456. __wasi_errno_t
  457. os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
  458. {
  459. CHECK_VALID_HANDLE(handle);
  460. return __WASI_ENOSYS;
  461. }
  462. __wasi_errno_t
  463. os_file_get_access_mode(os_file_handle handle,
  464. wasi_libc_file_access_mode *access_mode)
  465. {
  466. CHECK_VALID_HANDLE(handle);
  467. if ((handle->access_mode & windows_access_mode_read) != 0
  468. && (handle->access_mode & windows_access_mode_write) != 0)
  469. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  470. else if ((handle->access_mode & windows_access_mode_write) != 0)
  471. *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
  472. else
  473. *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
  474. return __WASI_ESUCCESS;
  475. }
  476. __wasi_errno_t
  477. os_fdatasync(os_file_handle handle)
  478. {
  479. CHECK_VALID_FILE_HANDLE(handle);
  480. return __WASI_ENOSYS;
  481. }
  482. __wasi_errno_t
  483. os_fsync(os_file_handle handle)
  484. {
  485. CHECK_VALID_FILE_HANDLE(handle);
  486. return __WASI_ENOSYS;
  487. }
  488. __wasi_errno_t
  489. os_open_preopendir(const char *path, os_file_handle *out)
  490. {
  491. *out = NULL;
  492. wchar_t wpath[PATH_MAX];
  493. __wasi_errno_t error = convert_to_wchar(path, wpath, PATH_MAX);
  494. if (error != __WASI_ESUCCESS)
  495. return error;
  496. HANDLE dir_handle = create_handle(wpath, true, true, true);
  497. if (dir_handle == INVALID_HANDLE_VALUE)
  498. return convert_windows_error_code(GetLastError());
  499. *out = BH_MALLOC(sizeof(windows_handle));
  500. if (*out == NULL) {
  501. CloseHandle(dir_handle);
  502. return __WASI_ENOMEM;
  503. }
  504. (*out)->type = windows_handle_type_file;
  505. (*out)->raw.handle = dir_handle;
  506. (*out)->fdflags = 0;
  507. (*out)->access_mode = windows_access_mode_read;
  508. return error;
  509. }
  510. __wasi_errno_t
  511. os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags,
  512. __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags,
  513. wasi_libc_file_access_mode access_mode, os_file_handle *out)
  514. {
  515. CHECK_VALID_FILE_HANDLE(handle);
  516. *out = BH_MALLOC(sizeof(windows_handle));
  517. if (*out == NULL)
  518. return __WASI_ENOMEM;
  519. (*out)->type = windows_handle_type_file;
  520. (*out)->fdflags = fs_flags;
  521. (*out)->raw.handle = INVALID_HANDLE_VALUE;
  522. DWORD attributes = FILE_FLAG_BACKUP_SEMANTICS;
  523. if ((fs_flags & (__WASI_FDFLAG_SYNC | __WASI_FDFLAG_RSYNC)) != 0)
  524. attributes |= (FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING);
  525. if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0)
  526. attributes |= FILE_FLAG_WRITE_THROUGH;
  527. if ((oflags & __WASI_O_DIRECTORY) != 0) {
  528. attributes |= FILE_ATTRIBUTE_DIRECTORY;
  529. oflags &= ~(__WASI_O_DIRECTORY);
  530. }
  531. // Use async operations on the handle if it's not a directory
  532. else {
  533. attributes |= FILE_FLAG_OVERLAPPED;
  534. }
  535. __wasi_errno_t error = __WASI_ESUCCESS;
  536. DWORD access_flags = 0;
  537. if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) {
  538. if ((attributes & (FILE_FLAG_NO_BUFFERING)) != 0) {
  539. // FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually
  540. // exclusive - CreateFile2 returns 87 (invalid parameter) when they
  541. // are combined.
  542. error = __WASI_ENOTSUP;
  543. goto fail;
  544. }
  545. access_flags |= FILE_APPEND_DATA;
  546. }
  547. switch (access_mode) {
  548. case WASI_LIBC_ACCESS_MODE_READ_ONLY:
  549. access_flags |= GENERIC_READ;
  550. (*out)->access_mode = windows_access_mode_read;
  551. break;
  552. case WASI_LIBC_ACCESS_MODE_WRITE_ONLY:
  553. access_flags |= GENERIC_WRITE;
  554. (*out)->access_mode = windows_access_mode_write;
  555. break;
  556. case WASI_LIBC_ACCESS_MODE_READ_WRITE:
  557. access_flags |= GENERIC_WRITE | GENERIC_READ;
  558. (*out)->access_mode =
  559. windows_access_mode_read | windows_access_mode_write;
  560. break;
  561. }
  562. DWORD creation_disposition = 0;
  563. switch (oflags) {
  564. case __WASI_O_CREAT | __WASI_O_EXCL:
  565. case __WASI_O_CREAT | __WASI_O_EXCL | __WASI_O_TRUNC:
  566. creation_disposition = CREATE_NEW;
  567. break;
  568. case __WASI_O_CREAT | __WASI_O_TRUNC:
  569. creation_disposition = CREATE_ALWAYS;
  570. break;
  571. case __WASI_O_CREAT:
  572. creation_disposition = OPEN_ALWAYS;
  573. break;
  574. case 0:
  575. case __WASI_O_EXCL:
  576. creation_disposition = OPEN_EXISTING;
  577. break;
  578. case __WASI_O_TRUNC:
  579. case __WASI_O_EXCL | __WASI_O_TRUNC:
  580. creation_disposition = TRUNCATE_EXISTING;
  581. // CreateFile2 requires write access if we truncate the file upon
  582. // opening
  583. access_flags |= GENERIC_WRITE;
  584. break;
  585. }
  586. wchar_t absolute_path[PATH_MAX];
  587. error = get_absolute_filepath(handle->raw.handle, path, absolute_path,
  588. PATH_MAX);
  589. if (error != __WASI_ESUCCESS)
  590. goto fail;
  591. if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0)
  592. attributes |= FILE_FLAG_OPEN_REPARSE_POINT;
  593. // Check that we're not trying to open an existing file as a directory.
  594. // Windows doesn't seem to throw an error in this case so add an
  595. // explicit check.
  596. if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0
  597. && creation_disposition == OPEN_EXISTING
  598. && !is_directory(absolute_path)) {
  599. error = __WASI_ENOTDIR;
  600. goto fail;
  601. }
  602. CREATEFILE2_EXTENDED_PARAMETERS create_params;
  603. create_params.dwSize = sizeof(create_params);
  604. create_params.dwFileAttributes = attributes & 0xFFF;
  605. create_params.dwFileFlags = attributes & 0xFFF00000;
  606. create_params.dwSecurityQosFlags = 0;
  607. create_params.lpSecurityAttributes = NULL;
  608. create_params.hTemplateFile = NULL;
  609. (*out)->raw.handle =
  610. CreateFile2(absolute_path, access_flags,
  611. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
  612. creation_disposition, &create_params);
  613. if ((*out)->raw.handle == INVALID_HANDLE_VALUE) {
  614. error = convert_windows_error_code(GetLastError());
  615. goto fail;
  616. }
  617. return error;
  618. fail:
  619. if (*out != NULL) {
  620. if ((*out)->raw.handle != INVALID_HANDLE_VALUE)
  621. CloseHandle((*out)->raw.handle);
  622. BH_FREE(*out);
  623. }
  624. return error;
  625. }
  626. __wasi_errno_t
  627. os_close(os_file_handle handle, bool is_stdio)
  628. {
  629. CHECK_VALID_HANDLE(handle);
  630. // We don't own the underlying raw handle so just free the handle and return
  631. // success.
  632. if (is_stdio) {
  633. BH_FREE(handle);
  634. return __WASI_ESUCCESS;
  635. }
  636. switch (handle->type) {
  637. case windows_handle_type_file:
  638. bool success = CloseHandle(handle->raw.handle);
  639. if (!success)
  640. return convert_windows_error_code(GetLastError());
  641. break;
  642. case windows_handle_type_socket:
  643. int ret = closesocket(handle->raw.socket);
  644. if (ret != 0)
  645. return convert_winsock_error_code(WSAGetLastError());
  646. break;
  647. default:
  648. assert(false && "unreachable");
  649. }
  650. BH_FREE(handle);
  651. return __WASI_ESUCCESS;
  652. }
  653. static __wasi_errno_t
  654. read_data_at_offset(HANDLE handle, const struct __wasi_iovec_t *iov, int iovcnt,
  655. __wasi_filesize_t offset, size_t *nwritten)
  656. {
  657. OVERLAPPED *read_operations =
  658. BH_MALLOC((uint32_t)(sizeof(OVERLAPPED) * (uint32_t)iovcnt));
  659. if (read_operations == NULL)
  660. return __WASI_ENOMEM;
  661. ULARGE_INTEGER query_offset = { .QuadPart = offset };
  662. __wasi_errno_t error = __WASI_ESUCCESS;
  663. size_t total_bytes_read = 0;
  664. const __wasi_iovec_t *current = iov;
  665. int successful_read_count = 0;
  666. for (int i = 0; i < iovcnt; ++i, ++current) {
  667. read_operations[i].Internal = 0;
  668. read_operations[i].InternalHigh = 0;
  669. read_operations[i].Offset = query_offset.LowPart;
  670. read_operations[i].OffsetHigh = query_offset.HighPart;
  671. read_operations[i].hEvent = NULL;
  672. if (!ReadFileEx(handle, current->buf, (DWORD)current->buf_len,
  673. &read_operations[i], NULL)) {
  674. DWORD win_error = GetLastError();
  675. if (win_error != ERROR_IO_PENDING) {
  676. error = convert_windows_error_code(win_error);
  677. break;
  678. }
  679. }
  680. ++successful_read_count;
  681. query_offset.QuadPart += (DWORD)current->buf_len;
  682. }
  683. // Get the result of all the asynchronous read operations
  684. for (int i = 0; i < successful_read_count; ++i) {
  685. DWORD bytes_transferred = 0;
  686. if (!GetOverlappedResult(handle, &read_operations[i],
  687. &bytes_transferred, true)) {
  688. DWORD win_error = GetLastError();
  689. if (win_error != ERROR_HANDLE_EOF)
  690. error = convert_windows_error_code(win_error);
  691. else
  692. total_bytes_read += (size_t)bytes_transferred;
  693. CancelIo(handle);
  694. for (int j = i + 1; j < iovcnt; ++j) {
  695. GetOverlappedResult(handle, &read_operations[j],
  696. &bytes_transferred, true);
  697. }
  698. break;
  699. }
  700. total_bytes_read += (size_t)bytes_transferred;
  701. }
  702. *nwritten = total_bytes_read;
  703. BH_FREE(read_operations);
  704. return error;
  705. }
  706. __wasi_errno_t
  707. os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  708. __wasi_filesize_t offset, size_t *nread)
  709. {
  710. CHECK_VALID_FILE_HANDLE(handle);
  711. return read_data_at_offset(handle->raw.handle, iov, iovcnt, offset, nread);
  712. }
  713. __wasi_errno_t
  714. os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  715. size_t *nread)
  716. {
  717. CHECK_VALID_HANDLE(handle);
  718. LARGE_INTEGER current_offset = { .QuadPart = 0 };
  719. // Seek to the current offset before reading
  720. int ret = SetFilePointerEx(handle->raw.handle, current_offset,
  721. &current_offset, FILE_CURRENT);
  722. if (ret == 0)
  723. return convert_windows_error_code(GetLastError());
  724. __wasi_errno_t error =
  725. read_data_at_offset(handle->raw.handle, iov, iovcnt,
  726. (__wasi_filesize_t)current_offset.QuadPart, nread);
  727. if (error != __WASI_ESUCCESS)
  728. return error;
  729. current_offset.QuadPart += (LONGLONG)(*nread);
  730. // Update the current offset to match how many bytes we've read
  731. ret =
  732. SetFilePointerEx(handle->raw.handle, current_offset, NULL, FILE_BEGIN);
  733. if (ret == 0)
  734. error = convert_windows_error_code(GetLastError());
  735. return error;
  736. }
  737. static __wasi_errno_t
  738. write_data_at_offset(HANDLE handle, const struct __wasi_ciovec_t *iov,
  739. int iovcnt, __wasi_filesize_t offset, size_t *nwritten)
  740. {
  741. OVERLAPPED *write_operations =
  742. BH_MALLOC((uint32_t)(sizeof(OVERLAPPED) * (uint32_t)iovcnt));
  743. if (write_operations == NULL)
  744. return __WASI_ENOMEM;
  745. ULARGE_INTEGER query_offset = { .QuadPart = offset };
  746. __wasi_errno_t error = __WASI_ESUCCESS;
  747. size_t total_bytes_written = 0;
  748. const __wasi_ciovec_t *current = iov;
  749. int successful_write_count = 0;
  750. for (int i = 0; i < iovcnt; ++i, ++current) {
  751. write_operations[i].Internal = 0;
  752. write_operations[i].InternalHigh = 0;
  753. write_operations[i].Offset = query_offset.LowPart;
  754. write_operations[i].OffsetHigh = query_offset.HighPart;
  755. write_operations[i].hEvent = NULL;
  756. if (!WriteFileEx(handle, current->buf, (DWORD)current->buf_len,
  757. &write_operations[i], NULL)) {
  758. DWORD win_error = GetLastError();
  759. if (win_error != ERROR_IO_PENDING) {
  760. error = convert_windows_error_code(win_error);
  761. break;
  762. }
  763. }
  764. ++successful_write_count;
  765. query_offset.QuadPart += (DWORD)current->buf_len;
  766. }
  767. // Get the result of all the asynchronous writes
  768. for (int i = 0; i < successful_write_count; ++i) {
  769. DWORD bytes_transferred = 0;
  770. if (!GetOverlappedResult(handle, &write_operations[i],
  771. &bytes_transferred, true)) {
  772. error = convert_windows_error_code(GetLastError());
  773. CancelIo(handle);
  774. for (int j = i + 1; j < iovcnt; ++j) {
  775. GetOverlappedResult(handle, &write_operations[j],
  776. &bytes_transferred, true);
  777. }
  778. break;
  779. }
  780. total_bytes_written += (size_t)bytes_transferred;
  781. }
  782. *nwritten = total_bytes_written;
  783. BH_FREE(write_operations);
  784. return error;
  785. }
  786. __wasi_errno_t
  787. os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  788. __wasi_filesize_t offset, size_t *nwritten)
  789. {
  790. CHECK_VALID_FILE_HANDLE(handle);
  791. return write_data_at_offset(handle->raw.handle, iov, iovcnt, offset,
  792. nwritten);
  793. }
  794. __wasi_errno_t
  795. os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  796. size_t *nwritten)
  797. {
  798. CHECK_VALID_HANDLE(handle);
  799. bool append = (handle->fdflags & __WASI_FDFLAG_APPEND) != 0;
  800. LARGE_INTEGER write_offset = { .QuadPart = 0 };
  801. DWORD move_method = append ? FILE_END : FILE_CURRENT;
  802. int ret = SetFilePointerEx(handle->raw.handle, write_offset, &write_offset,
  803. move_method);
  804. if (ret == 0)
  805. return convert_windows_error_code(GetLastError());
  806. __wasi_errno_t error = write_data_at_offset(
  807. handle->raw.handle, iov, iovcnt,
  808. (__wasi_filesize_t)write_offset.QuadPart, nwritten);
  809. if (error != __WASI_ESUCCESS)
  810. return error;
  811. write_offset.QuadPart += (LONGLONG)(*nwritten);
  812. // Update the write offset to match how many bytes we've written
  813. ret = SetFilePointerEx(handle->raw.handle, write_offset, NULL, FILE_BEGIN);
  814. if (ret == 0)
  815. error = convert_windows_error_code(GetLastError());
  816. return error;
  817. }
  818. __wasi_errno_t
  819. os_fallocate(os_file_handle handle, __wasi_filesize_t offset,
  820. __wasi_filesize_t length)
  821. {
  822. CHECK_VALID_FILE_HANDLE(handle);
  823. return __WASI_ENOSYS;
  824. }
  825. __wasi_errno_t
  826. os_ftruncate(os_file_handle handle, __wasi_filesize_t size)
  827. {
  828. CHECK_VALID_FILE_HANDLE(handle);
  829. return __WASI_ENOSYS;
  830. }
  831. __wasi_errno_t
  832. os_futimens(os_file_handle handle, __wasi_timestamp_t access_time,
  833. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags)
  834. {
  835. CHECK_VALID_FILE_HANDLE(handle);
  836. return __WASI_ENOSYS;
  837. }
  838. __wasi_errno_t
  839. os_utimensat(os_file_handle handle, const char *path,
  840. __wasi_timestamp_t access_time,
  841. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags,
  842. __wasi_lookupflags_t lookup_flags)
  843. {
  844. CHECK_VALID_FILE_HANDLE(handle);
  845. return __WASI_ENOSYS;
  846. }
  847. __wasi_errno_t
  848. os_readlinkat(os_file_handle handle, const char *path, char *buf,
  849. size_t bufsize, size_t *nread)
  850. {
  851. CHECK_VALID_FILE_HANDLE(handle);
  852. wchar_t symlink_path[PATH_MAX];
  853. __wasi_errno_t error =
  854. get_absolute_filepath(handle->raw.handle, path, symlink_path, PATH_MAX);
  855. if (error != __WASI_ESUCCESS)
  856. return error;
  857. DWORD symlink_attributes = GetFileAttributesW(symlink_path);
  858. if (!has_symlink_attribute(symlink_attributes))
  859. return __WASI_EINVAL;
  860. HANDLE link_handle = create_handle(
  861. symlink_path, has_directory_attribute(symlink_attributes), false, true);
  862. if (link_handle == INVALID_HANDLE_VALUE)
  863. return convert_windows_error_code(GetLastError());
  864. #if WINAPI_PARTITION_DESKTOP != 0
  865. // MinGW32 already has a definition for REPARSE_DATA_BUFFER
  866. #if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
  867. // See
  868. // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer
  869. // for more details.
  870. typedef struct _REPARSE_DATA_BUFFER {
  871. ULONG ReparseTag;
  872. USHORT ReparseDataLength;
  873. USHORT Reserved;
  874. union {
  875. struct {
  876. USHORT SubstituteNameOffset;
  877. USHORT SubstituteNameLength;
  878. USHORT PrintNameOffset;
  879. USHORT PrintNameLength;
  880. ULONG Flags;
  881. WCHAR PathBuffer[1];
  882. } SymbolicLinkReparseBuffer;
  883. struct {
  884. USHORT SubstituteNameOffset;
  885. USHORT SubstituteNameLength;
  886. USHORT PrintNameOffset;
  887. USHORT PrintNameLength;
  888. WCHAR PathBuffer[1];
  889. } MountPointReparseBuffer;
  890. struct {
  891. UCHAR DataBuffer[1];
  892. } GenericReparseBuffer;
  893. } DUMMYUNIONNAME;
  894. } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
  895. #endif
  896. char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
  897. REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)buffer;
  898. if (!DeviceIoControl(link_handle, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer,
  899. sizeof(buffer), NULL, NULL)) {
  900. error = convert_windows_error_code(GetLastError());
  901. goto fail;
  902. }
  903. int wbufsize = 0;
  904. wchar_t *wbuf = NULL;
  905. // The following checks are taken from the libuv windows filesystem
  906. // implementation,
  907. // https://github.com/libuv/libuv/blob/v1.x/src/win/fs.c#L181-L244. Real
  908. // symlinks can contain pretty much anything, but the only thing we really
  909. // care about is undoing the implicit conversion to an NT namespaced path
  910. // that CreateSymbolicLink will perform on absolute paths.
  911. if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
  912. wbuf = reparse_data->SymbolicLinkReparseBuffer.PathBuffer
  913. + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset
  914. / sizeof(wchar_t));
  915. wbufsize = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength
  916. / sizeof(wchar_t);
  917. if (wbufsize >= 4 && wbuf[0] == L'\\' && wbuf[1] == L'?'
  918. && wbuf[2] == L'?' && wbuf[3] == L'\\') {
  919. // Starts with \??\
  920. if (wbufsize >= 6
  921. && ((wbuf[4] >= L'A' && wbuf[4] <= L'Z')
  922. || (wbuf[4] >= L'a' && wbuf[4] <= L'z'))
  923. && wbuf[5] == L':' && (wbufsize == 6 || wbuf[6] == L'\\'))
  924. {
  925. // \??\<drive>:\
  926. wbuf += 4;
  927. wbufsize -= 4;
  928. }
  929. else if (wbufsize >= 8 && (wbuf[4] == L'U' || wbuf[4] == L'u')
  930. && (wbuf[5] == L'N' || wbuf[5] == L'n')
  931. && (wbuf[6] == L'C' || wbuf[6] == L'c')
  932. && wbuf[7] == L'\\')
  933. {
  934. // \??\UNC\<server>\<share>\ - make sure the final path looks like \\<server>\<share>\
  935. wbuf += 6;
  936. wbuf[0] = L'\\';
  937. wbufsize -= 6;
  938. }
  939. }
  940. }
  941. else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
  942. // Junction
  943. wbuf = reparse_data->MountPointReparseBuffer.PathBuffer
  944. + (reparse_data->MountPointReparseBuffer.SubstituteNameOffset
  945. / sizeof(wchar_t));
  946. wbufsize = reparse_data->MountPointReparseBuffer.SubstituteNameLength
  947. / sizeof(wchar_t);
  948. // Only treat junctions that look like \??\<drive>:\ as a symlink.
  949. if (!(wbufsize >= 6 && wbuf[0] == L'\\' && wbuf[1] == L'?'
  950. && wbuf[2] == L'?' && wbuf[3] == L'\\'
  951. && ((wbuf[4] >= L'A' && wbuf[4] <= L'Z')
  952. || (wbuf[4] >= L'a' && wbuf[4] <= L'z'))
  953. && wbuf[5] == L':' && (wbufsize == 6 || wbuf[6] == L'\\'))) {
  954. error = __WASI_EINVAL;
  955. goto fail;
  956. }
  957. /* Remove leading \??\ */
  958. wbuf += 4;
  959. wbufsize -= 4;
  960. }
  961. else {
  962. error = __WASI_EINVAL;
  963. goto fail;
  964. }
  965. if (wbuf != NULL)
  966. *nread = (size_t)WideCharToMultiByte(CP_UTF8, 0, wbuf, wbufsize, buf,
  967. (int)bufsize, NULL, NULL);
  968. if (*nread == 0 && wbuf != NULL) {
  969. DWORD win_error = GetLastError();
  970. if (win_error == ERROR_INSUFFICIENT_BUFFER)
  971. *nread = bufsize;
  972. else
  973. error = convert_windows_error_code(win_error);
  974. }
  975. #else
  976. error = __WASI_ENOTSUP;
  977. #endif
  978. fail:
  979. CloseHandle(link_handle);
  980. return error;
  981. }
  982. __wasi_errno_t
  983. os_linkat(os_file_handle from_handle, const char *from_path,
  984. os_file_handle to_handle, const char *to_path,
  985. __wasi_lookupflags_t lookup_flags)
  986. {
  987. CHECK_VALID_FILE_HANDLE(from_handle);
  988. CHECK_VALID_FILE_HANDLE(to_handle);
  989. return __WASI_ENOSYS;
  990. }
  991. __wasi_errno_t
  992. os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path)
  993. {
  994. CHECK_VALID_FILE_HANDLE(handle);
  995. return __WASI_ENOSYS;
  996. }
  997. __wasi_errno_t
  998. os_mkdirat(os_file_handle handle, const char *path)
  999. {
  1000. CHECK_VALID_FILE_HANDLE(handle);
  1001. wchar_t absolute_path[PATH_MAX];
  1002. __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path,
  1003. absolute_path, PATH_MAX);
  1004. if (error != __WASI_ESUCCESS)
  1005. return error;
  1006. bool success = CreateDirectoryW(absolute_path, NULL);
  1007. if (!success)
  1008. error = convert_windows_error_code(GetLastError());
  1009. return error;
  1010. }
  1011. __wasi_errno_t
  1012. os_renameat(os_file_handle old_handle, const char *old_path,
  1013. os_file_handle new_handle, const char *new_path)
  1014. {
  1015. CHECK_VALID_FILE_HANDLE(old_handle);
  1016. CHECK_VALID_FILE_HANDLE(new_handle);
  1017. return __WASI_ENOSYS;
  1018. }
  1019. __wasi_errno_t
  1020. os_unlinkat(os_file_handle handle, const char *path, bool is_dir)
  1021. {
  1022. CHECK_VALID_FILE_HANDLE(handle);
  1023. wchar_t absolute_path[PATH_MAX];
  1024. __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path,
  1025. absolute_path, PATH_MAX);
  1026. if (error != __WASI_ESUCCESS)
  1027. return error;
  1028. DWORD attributes = GetFileAttributesW(absolute_path);
  1029. if (has_symlink_attribute(attributes)) {
  1030. // Override is_dir for symlinks. A symlink to a directory counts
  1031. // as a directory itself in Windows.
  1032. is_dir = has_directory_attribute(attributes);
  1033. }
  1034. int ret =
  1035. is_dir ? RemoveDirectoryW(absolute_path) : DeleteFileW(absolute_path);
  1036. if (ret == 0)
  1037. error = convert_windows_error_code(GetLastError());
  1038. return error;
  1039. }
  1040. __wasi_errno_t
  1041. os_lseek(os_file_handle handle, __wasi_filedelta_t offset,
  1042. __wasi_whence_t whence, __wasi_filesize_t *new_offset)
  1043. {
  1044. CHECK_VALID_FILE_HANDLE(handle);
  1045. return __WASI_ENOSYS;
  1046. }
  1047. __wasi_errno_t
  1048. os_fadvise(os_file_handle handle, __wasi_filesize_t offset,
  1049. __wasi_filesize_t length, __wasi_advice_t advice)
  1050. {
  1051. CHECK_VALID_FILE_HANDLE(handle);
  1052. return __WASI_ENOSYS;
  1053. }
  1054. __wasi_errno_t
  1055. os_isatty(os_file_handle handle)
  1056. {
  1057. CHECK_VALID_HANDLE(handle);
  1058. DWORD console_mode;
  1059. return GetConsoleMode(handle->raw.handle, &console_mode) ? __WASI_ESUCCESS
  1060. : __WASI_ENOTTY;
  1061. }
  1062. static os_file_handle
  1063. create_stdio_handle(HANDLE raw_stdio_handle, DWORD stdio)
  1064. {
  1065. os_file_handle stdio_handle = BH_MALLOC(sizeof(windows_handle));
  1066. if (stdio_handle == NULL)
  1067. return NULL;
  1068. stdio_handle->type = windows_handle_type_file;
  1069. stdio_handle->access_mode =
  1070. windows_access_mode_read | windows_access_mode_write;
  1071. stdio_handle->fdflags = 0;
  1072. if (raw_stdio_handle == INVALID_HANDLE_VALUE)
  1073. raw_stdio_handle = GetStdHandle(stdio);
  1074. stdio_handle->raw.handle = raw_stdio_handle;
  1075. return stdio_handle;
  1076. }
  1077. os_file_handle
  1078. os_convert_stdin_handle(os_raw_file_handle raw_stdin)
  1079. {
  1080. return create_stdio_handle(raw_stdin, STD_INPUT_HANDLE);
  1081. }
  1082. os_file_handle
  1083. os_convert_stdout_handle(os_raw_file_handle raw_stdout)
  1084. {
  1085. return create_stdio_handle(raw_stdout, STD_OUTPUT_HANDLE);
  1086. }
  1087. os_file_handle
  1088. os_convert_stderr_handle(os_raw_file_handle raw_stderr)
  1089. {
  1090. return create_stdio_handle(raw_stderr, STD_ERROR_HANDLE);
  1091. }
  1092. __wasi_errno_t
  1093. os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream)
  1094. {
  1095. return __WASI_ENOSYS;
  1096. }
  1097. __wasi_errno_t
  1098. os_rewinddir(os_dir_stream dir_stream)
  1099. {
  1100. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1101. return __WASI_ENOSYS;
  1102. }
  1103. __wasi_errno_t
  1104. os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position)
  1105. {
  1106. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1107. return __WASI_ENOSYS;
  1108. }
  1109. __wasi_errno_t
  1110. os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry,
  1111. const char **d_name)
  1112. {
  1113. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1114. return __WASI_ENOSYS;
  1115. }
  1116. __wasi_errno_t
  1117. os_closedir(os_dir_stream dir_stream)
  1118. {
  1119. CHECK_VALID_WIN_DIR_STREAM(dir_stream);
  1120. return __WASI_ENOSYS;
  1121. }
  1122. os_dir_stream
  1123. os_get_invalid_dir_stream()
  1124. {
  1125. return NULL;
  1126. }
  1127. bool
  1128. os_is_dir_stream_valid(os_dir_stream *dir_stream)
  1129. {
  1130. assert(dir_stream != NULL);
  1131. if (((*dir_stream) == NULL) || ((*dir_stream)->handle == NULL)
  1132. || ((*dir_stream)->handle->type != windows_handle_type_file)
  1133. || ((*dir_stream)->handle->raw.handle == INVALID_HANDLE_VALUE))
  1134. return false;
  1135. return true;
  1136. }
  1137. bool
  1138. os_is_handle_valid(os_file_handle *handle)
  1139. {
  1140. assert(handle != NULL);
  1141. CHECK_VALID_HANDLE_WITH_RETURN_VALUE(*handle, false);
  1142. return true;
  1143. }
  1144. char *
  1145. os_realpath(const char *path, char *resolved_path)
  1146. {
  1147. resolved_path = _fullpath(resolved_path, path, PATH_MAX);
  1148. // Check the file/directory actually exists
  1149. DWORD attributes = GetFileAttributesA(resolved_path);
  1150. if (attributes == INVALID_FILE_ATTRIBUTES)
  1151. return NULL;
  1152. return resolved_path;
  1153. }