espidf_file.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  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 <unistd.h>
  8. #if !defined(__APPLE__) && !defined(ESP_PLATFORM)
  9. #define CONFIG_HAS_PWRITEV 1
  10. #define CONFIG_HAS_PREADV 1
  11. #else
  12. #define CONFIG_HAS_PWRITEV 0
  13. #define CONFIG_HAS_PREADV 0
  14. #endif
  15. #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(ESP_PLATFORM)
  16. #define CONFIG_HAS_FDATASYNC 1
  17. #else
  18. #define CONFIG_HAS_FDATASYNC 0
  19. #endif
  20. /*
  21. * For NuttX, CONFIG_HAS_ISATTY is provided by its platform header.
  22. * (platform_internal.h)
  23. */
  24. #if !defined(CONFIG_HAS_D_INO)
  25. #if !defined(__NuttX__)
  26. #define CONFIG_HAS_D_INO 1
  27. #define CONFIG_HAS_ISATTY 1
  28. #else
  29. #define CONFIG_HAS_D_INO 0
  30. #endif
  31. #endif
  32. #if !defined(__APPLE__) && !defined(ESP_PLATFORM) && !defined(__COSMOPOLITAN__)
  33. #define CONFIG_HAS_POSIX_FALLOCATE 1
  34. #else
  35. #define CONFIG_HAS_POSIX_FALLOCATE 0
  36. #endif
  37. #if defined(O_DSYNC)
  38. #define CONFIG_HAS_O_DSYNC
  39. #endif
  40. // POSIX requires O_RSYNC to be defined, but Linux explicitly doesn't support
  41. // it.
  42. #if defined(O_RSYNC) && !defined(__linux__)
  43. #define CONFIG_HAS_O_RSYNC
  44. #endif
  45. #if defined(O_SYNC)
  46. #define CONFIG_HAS_O_SYNC
  47. #endif
  48. // Converts a POSIX timespec to a WASI timestamp.
  49. static __wasi_timestamp_t
  50. convert_timespec(const struct timespec *ts)
  51. {
  52. if (ts->tv_sec < 0)
  53. return 0;
  54. if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000)
  55. return UINT64_MAX;
  56. return (__wasi_timestamp_t)ts->tv_sec * 1000000000
  57. + (__wasi_timestamp_t)ts->tv_nsec;
  58. }
  59. // Converts a POSIX stat structure to a WASI filestat structure
  60. static void
  61. convert_stat(os_file_handle handle, const struct stat *in,
  62. __wasi_filestat_t *out)
  63. {
  64. out->st_dev = in->st_dev;
  65. out->st_ino = in->st_ino;
  66. out->st_nlink = (__wasi_linkcount_t)in->st_nlink;
  67. out->st_size = (__wasi_filesize_t)in->st_size;
  68. #ifdef __APPLE__
  69. out->st_atim = convert_timespec(&in->st_atimespec);
  70. out->st_mtim = convert_timespec(&in->st_mtimespec);
  71. out->st_ctim = convert_timespec(&in->st_ctimespec);
  72. #else
  73. out->st_atim = convert_timespec(&in->st_atim);
  74. out->st_mtim = convert_timespec(&in->st_mtim);
  75. out->st_ctim = convert_timespec(&in->st_ctim);
  76. #endif
  77. // Convert the file type. In the case of sockets there is no way we
  78. // can easily determine the exact socket type.
  79. if (S_ISBLK(in->st_mode)) {
  80. out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE;
  81. }
  82. else if (S_ISCHR(in->st_mode)) {
  83. out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE;
  84. }
  85. else if (S_ISDIR(in->st_mode)) {
  86. out->st_filetype = __WASI_FILETYPE_DIRECTORY;
  87. }
  88. else if (S_ISFIFO(in->st_mode)) {
  89. out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM;
  90. }
  91. else if (S_ISLNK(in->st_mode)) {
  92. out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK;
  93. }
  94. else if (S_ISREG(in->st_mode)) {
  95. out->st_filetype = __WASI_FILETYPE_REGULAR_FILE;
  96. }
  97. else if (S_ISSOCK(in->st_mode)) {
  98. int socktype;
  99. socklen_t socktypelen = sizeof(socktype);
  100. if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen)
  101. < 0) {
  102. out->st_filetype = __WASI_FILETYPE_UNKNOWN;
  103. return;
  104. }
  105. switch (socktype) {
  106. case SOCK_DGRAM:
  107. out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM;
  108. break;
  109. case SOCK_STREAM:
  110. out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM;
  111. break;
  112. default:
  113. out->st_filetype = __WASI_FILETYPE_UNKNOWN;
  114. return;
  115. }
  116. }
  117. else {
  118. out->st_filetype = __WASI_FILETYPE_UNKNOWN;
  119. }
  120. }
  121. static void
  122. convert_timestamp(__wasi_timestamp_t in, struct timespec *out)
  123. {
  124. // Store sub-second remainder.
  125. #if defined(__SYSCALL_SLONG_TYPE)
  126. out->tv_nsec = (__SYSCALL_SLONG_TYPE)(in % 1000000000);
  127. #else
  128. out->tv_nsec = (long)(in % 1000000000);
  129. #endif
  130. in /= 1000000000;
  131. // Clamp to the maximum in case it would overflow our system's time_t.
  132. out->tv_sec = (time_t)in < BH_TIME_T_MAX ? (time_t)in : BH_TIME_T_MAX;
  133. }
  134. // Converts the provided timestamps and flags to a set of arguments for
  135. // futimens() and utimensat().
  136. static void
  137. convert_utimens_arguments(__wasi_timestamp_t st_atim,
  138. __wasi_timestamp_t st_mtim,
  139. __wasi_fstflags_t fstflags, struct timespec *ts)
  140. {
  141. if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) {
  142. ts[0].tv_nsec = UTIME_NOW;
  143. }
  144. else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) {
  145. convert_timestamp(st_atim, &ts[0]);
  146. }
  147. else {
  148. ts[0].tv_nsec = UTIME_OMIT;
  149. }
  150. if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) {
  151. ts[1].tv_nsec = UTIME_NOW;
  152. }
  153. else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) {
  154. convert_timestamp(st_mtim, &ts[1]);
  155. }
  156. else {
  157. ts[1].tv_nsec = UTIME_OMIT;
  158. }
  159. }
  160. __wasi_errno_t
  161. os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf)
  162. {
  163. struct stat stat_buf;
  164. int ret = fstat(handle, &stat_buf);
  165. if (ret < 0)
  166. return convert_errno(errno);
  167. convert_stat(handle, &stat_buf, buf);
  168. return __WASI_ESUCCESS;
  169. }
  170. __wasi_errno_t
  171. os_fstatat(os_file_handle handle, const char *path,
  172. struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags)
  173. {
  174. struct stat stat_buf;
  175. int ret = fstatat(handle, path, &stat_buf,
  176. (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW)
  177. ? AT_SYMLINK_FOLLOW
  178. : AT_SYMLINK_NOFOLLOW);
  179. if (ret < 0)
  180. return convert_errno(errno);
  181. convert_stat(handle, &stat_buf, buf);
  182. return __WASI_ESUCCESS;
  183. }
  184. __wasi_errno_t
  185. os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
  186. {
  187. int ret = fcntl(handle, F_GETFL);
  188. if (ret < 0)
  189. return convert_errno(errno);
  190. *flags = 0;
  191. if ((ret & O_APPEND) != 0)
  192. *flags |= __WASI_FDFLAG_APPEND;
  193. #ifdef CONFIG_HAS_O_DSYNC
  194. if ((ret & O_DSYNC) != 0)
  195. *flags |= __WASI_FDFLAG_DSYNC;
  196. #endif
  197. if ((ret & O_NONBLOCK) != 0)
  198. *flags |= __WASI_FDFLAG_NONBLOCK;
  199. #ifdef CONFIG_HAS_O_RSYNC
  200. if ((ret & O_RSYNC) != 0)
  201. *flags |= __WASI_FDFLAG_RSYNC;
  202. #endif
  203. #ifdef CONFIG_HAS_O_SYNC
  204. if ((ret & O_SYNC) != 0)
  205. *flags |= __WASI_FDFLAG_SYNC;
  206. #endif
  207. return __WASI_ESUCCESS;
  208. }
  209. __wasi_errno_t
  210. os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
  211. {
  212. int fcntl_flags = 0;
  213. if ((flags & __WASI_FDFLAG_APPEND) != 0)
  214. fcntl_flags |= O_APPEND;
  215. if ((flags & __WASI_FDFLAG_DSYNC) != 0)
  216. #ifdef CONFIG_HAS_O_DSYNC
  217. fcntl_flags |= O_DSYNC;
  218. #else
  219. return __WASI_ENOTSUP;
  220. #endif
  221. if ((flags & __WASI_FDFLAG_NONBLOCK) != 0)
  222. fcntl_flags |= O_NONBLOCK;
  223. if ((flags & __WASI_FDFLAG_RSYNC) != 0)
  224. #ifdef CONFIG_HAS_O_RSYNC
  225. fcntl_flags |= O_RSYNC;
  226. #else
  227. return __WASI_ENOTSUP;
  228. #endif
  229. if ((flags & __WASI_FDFLAG_SYNC) != 0)
  230. #ifdef CONFIG_HAS_O_SYNC
  231. fcntl_flags |= O_SYNC;
  232. #else
  233. return __WASI_ENOTSUP;
  234. #endif
  235. int ret = fcntl(handle, F_SETFL, fcntl_flags);
  236. if (ret < 0)
  237. return convert_errno(errno);
  238. return __WASI_ESUCCESS;
  239. }
  240. __wasi_errno_t
  241. os_fdatasync(os_file_handle handle)
  242. {
  243. #if CONFIG_HAS_FDATASYNC
  244. int ret = fdatasync(handle);
  245. #else
  246. int ret = fsync(handle);
  247. #endif
  248. if (ret < 0)
  249. return convert_errno(errno);
  250. return __WASI_ESUCCESS;
  251. }
  252. __wasi_errno_t
  253. os_fsync(os_file_handle handle)
  254. {
  255. int ret = fsync(handle);
  256. if (ret < 0)
  257. return convert_errno(errno);
  258. return __WASI_ESUCCESS;
  259. }
  260. __wasi_errno_t
  261. os_open_preopendir(const char *path, os_file_handle *out)
  262. {
  263. int fd = open(path, O_RDONLY | O_DIRECTORY, 0);
  264. if (fd < 0)
  265. return convert_errno(errno);
  266. *out = fd;
  267. return __WASI_ESUCCESS;
  268. }
  269. __wasi_errno_t
  270. os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags,
  271. __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags,
  272. wasi_libc_file_access_mode read_write_mode, os_file_handle *out)
  273. {
  274. int open_flags = 0;
  275. // Convert open flags.
  276. if ((oflags & __WASI_O_CREAT) != 0) {
  277. open_flags |= O_CREAT;
  278. }
  279. if ((oflags & __WASI_O_DIRECTORY) != 0)
  280. open_flags |= O_DIRECTORY;
  281. if ((oflags & __WASI_O_EXCL) != 0)
  282. open_flags |= O_EXCL;
  283. if ((oflags & __WASI_O_TRUNC) != 0) {
  284. open_flags |= O_TRUNC;
  285. }
  286. // Convert file descriptor flags.
  287. if ((fs_flags & __WASI_FDFLAG_APPEND) != 0)
  288. open_flags |= O_APPEND;
  289. if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) {
  290. #ifdef CONFIG_HAS_O_DSYNC
  291. open_flags |= O_DSYNC;
  292. #else
  293. return __WASI_ENOTSUP;
  294. #endif
  295. }
  296. if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0)
  297. open_flags |= O_NONBLOCK;
  298. if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) {
  299. #ifdef CONFIG_HAS_O_RSYNC
  300. open_flags |= O_RSYNC;
  301. #else
  302. return __WASI_ENOTSUP;
  303. #endif
  304. }
  305. if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) {
  306. #ifdef CONFIG_HAS_O_SYNC
  307. open_flags |= O_SYNC;
  308. #else
  309. return __WASI_ENOTSUP;
  310. #endif
  311. }
  312. if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) {
  313. open_flags |= O_NOFOLLOW;
  314. }
  315. switch (read_write_mode) {
  316. case WASI_LIBC_ACCESS_MODE_READ_WRITE:
  317. open_flags |= O_RDWR;
  318. break;
  319. case WASI_LIBC_ACCESS_MODE_READ_ONLY:
  320. open_flags |= O_RDONLY;
  321. break;
  322. case WASI_LIBC_ACCESS_MODE_WRITE_ONLY:
  323. open_flags |= O_WRONLY;
  324. break;
  325. default:
  326. return __WASI_EINVAL;
  327. }
  328. int fd = openat(handle, path, open_flags, 0666);
  329. if (fd < 0) {
  330. int openat_errno = errno;
  331. // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket.
  332. if (openat_errno == ENXIO) {
  333. struct stat sb;
  334. int ret = fstatat(handle, path, &sb,
  335. (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW)
  336. ? 0
  337. : AT_SYMLINK_NOFOLLOW);
  338. return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP
  339. : __WASI_ENXIO;
  340. }
  341. // Linux returns ENOTDIR instead of ELOOP when using
  342. // O_NOFOLLOW|O_DIRECTORY on a symlink.
  343. if (openat_errno == ENOTDIR
  344. && (open_flags & (O_NOFOLLOW | O_DIRECTORY)) != 0) {
  345. struct stat sb;
  346. int ret = fstatat(handle, path, &sb, AT_SYMLINK_NOFOLLOW);
  347. if (S_ISLNK(sb.st_mode)) {
  348. return __WASI_ELOOP;
  349. }
  350. (void)ret;
  351. }
  352. // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on
  353. // a symlink.
  354. if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0
  355. && openat_errno == EMLINK)
  356. return __WASI_ELOOP;
  357. return convert_errno(openat_errno);
  358. }
  359. *out = fd;
  360. return __WASI_ESUCCESS;
  361. }
  362. __wasi_errno_t
  363. os_file_get_access_mode(os_file_handle handle,
  364. wasi_libc_file_access_mode *access_mode)
  365. {
  366. int ret = fcntl(handle, F_GETFL, 0);
  367. if (ret < 0)
  368. return convert_errno(errno);
  369. switch (ret & O_ACCMODE) {
  370. case O_RDONLY:
  371. *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
  372. break;
  373. case O_WRONLY:
  374. *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
  375. break;
  376. case O_RDWR:
  377. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  378. break;
  379. default:
  380. return __WASI_EINVAL;
  381. }
  382. return __WASI_ESUCCESS;
  383. }
  384. __wasi_errno_t
  385. os_close(os_file_handle handle, bool is_stdio)
  386. {
  387. if (is_stdio)
  388. return __WASI_ESUCCESS;
  389. int ret = close(handle);
  390. if (ret < 0)
  391. return convert_errno(errno);
  392. return __WASI_ESUCCESS;
  393. }
  394. __wasi_errno_t
  395. os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  396. __wasi_filesize_t offset, size_t *nread)
  397. {
  398. #if CONFIG_HAS_PREADV
  399. ssize_t len =
  400. preadv(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset);
  401. if (len < 0)
  402. return convert_errno(errno);
  403. *nread = (size_t)len;
  404. return __WASI_ESUCCESS;
  405. #else
  406. if (iovcnt == 1) {
  407. ssize_t len = pread(handle, iov->buf, iov->buf_len, offset);
  408. if (len < 0)
  409. return convert_errno(errno);
  410. *nread = len;
  411. return __WASI_ESUCCESS;
  412. }
  413. // Allocate a single buffer to fit all data.
  414. size_t totalsize = 0;
  415. for (int i = 0; i < iovcnt; ++i)
  416. totalsize += iov[i].buf_len;
  417. char *buf = BH_MALLOC(totalsize);
  418. if (buf == NULL) {
  419. return __WASI_ENOMEM;
  420. }
  421. // Perform a single read operation.
  422. ssize_t len = pread(handle, buf, totalsize, offset);
  423. if (len < 0) {
  424. BH_FREE(buf);
  425. return convert_errno(errno);
  426. }
  427. // Copy data back to vectors.
  428. size_t bufoff = 0;
  429. for (int i = 0; i < iovcnt; ++i) {
  430. if (bufoff + iov[i].buf_len < (size_t)len) {
  431. memcpy(iov[i].buf, buf + bufoff, iov[i].buf_len);
  432. bufoff += iov[i].buf_len;
  433. }
  434. else {
  435. memcpy(iov[i].buf, buf + bufoff, len - bufoff);
  436. break;
  437. }
  438. }
  439. BH_FREE(buf);
  440. *nread = len;
  441. return __WASI_ESUCCESS;
  442. #endif
  443. }
  444. __wasi_errno_t
  445. os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  446. __wasi_filesize_t offset, size_t *nwritten)
  447. {
  448. if (iovcnt == 0)
  449. return __WASI_EINVAL;
  450. ssize_t len = 0;
  451. #if CONFIG_HAS_PWRITEV
  452. len =
  453. pwritev(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset);
  454. #else
  455. if (iovcnt == 1) {
  456. len = pwrite(handle, iov->buf, iov->buf_len, offset);
  457. }
  458. else {
  459. // Allocate a single buffer to fit all data.
  460. size_t totalsize = 0;
  461. for (int i = 0; i < iovcnt; ++i)
  462. totalsize += iov[i].buf_len;
  463. char *buf = BH_MALLOC(totalsize);
  464. if (buf == NULL) {
  465. return __WASI_ENOMEM;
  466. }
  467. size_t bufoff = 0;
  468. for (int i = 0; i < iovcnt; ++i) {
  469. memcpy(buf + bufoff, iov[i].buf, iov[i].buf_len);
  470. bufoff += iov[i].buf_len;
  471. }
  472. // Perform a single write operation.
  473. len = pwrite(handle, buf, totalsize, offset);
  474. BH_FREE(buf);
  475. }
  476. #endif
  477. if (len < 0)
  478. return convert_errno(errno);
  479. *nwritten = (size_t)len;
  480. return __WASI_ESUCCESS;
  481. }
  482. __wasi_errno_t
  483. os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  484. size_t *nread)
  485. {
  486. ssize_t len = readv(handle, (const struct iovec *)iov, (int)iovcnt);
  487. if (len < 0)
  488. return convert_errno(errno);
  489. *nread = (size_t)len;
  490. return __WASI_ESUCCESS;
  491. }
  492. __wasi_errno_t
  493. os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  494. size_t *nwritten)
  495. {
  496. ssize_t len = writev(handle, (const struct iovec *)iov, (int)iovcnt);
  497. if (len < 0)
  498. return convert_errno(errno);
  499. *nwritten = (size_t)len;
  500. return __WASI_ESUCCESS;
  501. }
  502. __wasi_errno_t
  503. os_fallocate(os_file_handle handle, __wasi_filesize_t offset,
  504. __wasi_filesize_t length)
  505. {
  506. #if CONFIG_HAS_POSIX_FALLOCATE
  507. int ret = posix_fallocate(handle, (off_t)offset, (off_t)length);
  508. #else
  509. // At least ensure that the file is grown to the right size.
  510. // TODO(ed): See if this can somehow be implemented without any race
  511. // conditions. We may end up shrinking the file right now.
  512. struct stat sb;
  513. int ret = fstat(handle, &sb);
  514. off_t newsize = (off_t)(offset + length);
  515. if (ret == 0 && sb.st_size < newsize)
  516. ret = ftruncate(handle, newsize);
  517. #endif
  518. if (ret != 0)
  519. return convert_errno(ret);
  520. return __WASI_ESUCCESS;
  521. }
  522. __wasi_errno_t
  523. os_ftruncate(os_file_handle handle, __wasi_filesize_t size)
  524. {
  525. int ret = ftruncate(handle, (off_t)size);
  526. if (ret < 0)
  527. return convert_errno(errno);
  528. return __WASI_ESUCCESS;
  529. }
  530. __wasi_errno_t
  531. os_futimens(os_file_handle handle, __wasi_timestamp_t access_time,
  532. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags)
  533. {
  534. struct timespec ts[2];
  535. convert_utimens_arguments(access_time, modification_time, fstflags, ts);
  536. int ret = futimens(handle, ts);
  537. if (ret < 0)
  538. return convert_errno(errno);
  539. return __WASI_ESUCCESS;
  540. }
  541. __wasi_errno_t
  542. os_utimensat(os_file_handle handle, const char *path,
  543. __wasi_timestamp_t access_time,
  544. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags,
  545. __wasi_lookupflags_t lookup_flags)
  546. {
  547. struct timespec ts[2];
  548. convert_utimens_arguments(access_time, modification_time, fstflags, ts);
  549. int ret = utimensat(handle, path, ts,
  550. (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW)
  551. ? 0
  552. : AT_SYMLINK_NOFOLLOW);
  553. if (ret < 0)
  554. return convert_errno(errno);
  555. return __WASI_ESUCCESS;
  556. }
  557. __wasi_errno_t
  558. os_readlinkat(os_file_handle handle, const char *path, char *buf,
  559. size_t bufsize, size_t *nread)
  560. {
  561. // Linux requires that the buffer size is positive. whereas POSIX does
  562. // not. Use a fake buffer to store the results if the size is zero.
  563. char fakebuf[1];
  564. ssize_t len = readlinkat(handle, path, bufsize == 0 ? fakebuf : buf,
  565. bufsize == 0 ? sizeof(fakebuf) : bufsize);
  566. if (len < 0)
  567. return convert_errno(errno);
  568. *nread = (size_t)len < bufsize ? (size_t)len : bufsize;
  569. return __WASI_ESUCCESS;
  570. }
  571. __wasi_errno_t
  572. os_linkat(os_file_handle from_handle, const char *from_path,
  573. os_file_handle to_handle, const char *to_path,
  574. __wasi_lookupflags_t lookup_flags)
  575. {
  576. int ret = linkat(
  577. from_handle, from_path, to_handle, to_path,
  578. (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) ? AT_SYMLINK_FOLLOW : 0);
  579. if (ret < 0)
  580. return convert_errno(errno);
  581. return __WASI_ESUCCESS;
  582. }
  583. __wasi_errno_t
  584. os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path)
  585. {
  586. int ret = symlinkat(old_path, handle, new_path);
  587. if (ret < 0)
  588. return convert_errno(errno);
  589. return __WASI_ESUCCESS;
  590. }
  591. __wasi_errno_t
  592. os_mkdirat(os_file_handle handle, const char *path)
  593. {
  594. int ret = mkdirat(handle, path, 0777);
  595. if (ret < 0)
  596. return convert_errno(errno);
  597. return __WASI_ESUCCESS;
  598. }
  599. __wasi_errno_t
  600. os_renameat(os_file_handle old_handle, const char *old_path,
  601. os_file_handle new_handle, const char *new_path)
  602. {
  603. int ret = renameat(old_handle, old_path, new_handle, new_path);
  604. if (ret < 0)
  605. return convert_errno(errno);
  606. return __WASI_ESUCCESS;
  607. }
  608. __wasi_errno_t
  609. os_unlinkat(os_file_handle handle, const char *path, bool is_dir)
  610. {
  611. int ret = unlinkat(handle, path, is_dir ? AT_REMOVEDIR : 0);
  612. #ifndef __linux__
  613. if (ret < 0) {
  614. // Non-Linux implementations may return EPERM when attempting to remove
  615. // a directory without REMOVEDIR. While that's what POSIX specifies,
  616. // it's less useful. Adjust this to EISDIR. It doesn't matter that this
  617. // is not atomic with the unlinkat, because if the file is removed and a
  618. // directory is created before fstatat sees it, we're racing with that
  619. // change anyway and unlinkat could have legitimately seen the directory
  620. // if the race had turned out differently.
  621. if (errno == EPERM) {
  622. struct stat statbuf;
  623. if (fstatat(handle, path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0
  624. && S_ISDIR(statbuf.st_mode)) {
  625. errno = EISDIR;
  626. }
  627. }
  628. // POSIX permits either EEXIST or ENOTEMPTY when the directory is not
  629. // empty. Map it to ENOTEMPTY.
  630. else if (errno == EEXIST) {
  631. errno = ENOTEMPTY;
  632. }
  633. return convert_errno(errno);
  634. }
  635. #endif
  636. if (ret < 0)
  637. return convert_errno(errno);
  638. return __WASI_ESUCCESS;
  639. }
  640. __wasi_errno_t
  641. os_lseek(os_file_handle handle, __wasi_filedelta_t offset,
  642. __wasi_whence_t whence, __wasi_filesize_t *new_offset)
  643. {
  644. int nwhence;
  645. switch (whence) {
  646. case __WASI_WHENCE_CUR:
  647. nwhence = SEEK_CUR;
  648. break;
  649. case __WASI_WHENCE_END:
  650. nwhence = SEEK_END;
  651. break;
  652. case __WASI_WHENCE_SET:
  653. nwhence = SEEK_SET;
  654. break;
  655. default:
  656. return __WASI_EINVAL;
  657. }
  658. off_t ret = lseek(handle, offset, nwhence);
  659. if (ret < 0)
  660. return convert_errno(errno);
  661. *new_offset = (__wasi_filesize_t)ret;
  662. return __WASI_ESUCCESS;
  663. }
  664. __wasi_errno_t
  665. os_fadvise(os_file_handle handle, __wasi_filesize_t offset,
  666. __wasi_filesize_t length, __wasi_advice_t advice)
  667. {
  668. #ifdef POSIX_FADV_NORMAL
  669. int nadvice;
  670. switch (advice) {
  671. case __WASI_ADVICE_DONTNEED:
  672. nadvice = POSIX_FADV_DONTNEED;
  673. break;
  674. case __WASI_ADVICE_NOREUSE:
  675. nadvice = POSIX_FADV_NOREUSE;
  676. break;
  677. case __WASI_ADVICE_NORMAL:
  678. nadvice = POSIX_FADV_NORMAL;
  679. break;
  680. case __WASI_ADVICE_RANDOM:
  681. nadvice = POSIX_FADV_RANDOM;
  682. break;
  683. case __WASI_ADVICE_SEQUENTIAL:
  684. nadvice = POSIX_FADV_SEQUENTIAL;
  685. break;
  686. case __WASI_ADVICE_WILLNEED:
  687. nadvice = POSIX_FADV_WILLNEED;
  688. break;
  689. default:
  690. return __WASI_EINVAL;
  691. }
  692. int ret = posix_fadvise(handle, (off_t)offset, (off_t)length, nadvice);
  693. if (ret != 0)
  694. return convert_errno(ret);
  695. return __WASI_ESUCCESS;
  696. #else
  697. // Advisory information can be safely ignored if not supported
  698. switch (advice) {
  699. case __WASI_ADVICE_DONTNEED:
  700. case __WASI_ADVICE_NOREUSE:
  701. case __WASI_ADVICE_NORMAL:
  702. case __WASI_ADVICE_RANDOM:
  703. case __WASI_ADVICE_SEQUENTIAL:
  704. case __WASI_ADVICE_WILLNEED:
  705. return __WASI_ESUCCESS;
  706. default:
  707. return __WASI_EINVAL;
  708. }
  709. #endif
  710. }
  711. __wasi_errno_t
  712. os_isatty(os_file_handle handle)
  713. {
  714. #if CONFIG_HAS_ISATTY
  715. int ret = isatty(handle);
  716. if (ret == 1)
  717. return __WASI_ESUCCESS;
  718. return __WASI_ENOTTY;
  719. #else
  720. return __WASI_ENOTSUP;
  721. #endif
  722. }
  723. os_file_handle
  724. os_convert_stdin_handle(os_raw_file_handle raw_stdin)
  725. {
  726. #ifndef STDIN_FILENO
  727. #define STDIN_FILENO 0
  728. #endif
  729. return raw_stdin >= 0 ? raw_stdin : STDIN_FILENO;
  730. }
  731. os_file_handle
  732. os_convert_stdout_handle(os_raw_file_handle raw_stdout)
  733. {
  734. #ifndef STDOUT_FILENO
  735. #define STDOUT_FILENO 1
  736. #endif
  737. return raw_stdout >= 0 ? raw_stdout : STDOUT_FILENO;
  738. }
  739. os_file_handle
  740. os_convert_stderr_handle(os_raw_file_handle raw_stderr)
  741. {
  742. #ifndef STDERR_FILENO
  743. #define STDERR_FILENO 2
  744. #endif
  745. return raw_stderr >= 0 ? raw_stderr : STDERR_FILENO;
  746. }
  747. __wasi_errno_t
  748. os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream)
  749. {
  750. *dir_stream = fdopendir(handle);
  751. if (*dir_stream == NULL)
  752. return convert_errno(errno);
  753. return __WASI_ESUCCESS;
  754. }
  755. __wasi_errno_t
  756. os_rewinddir(os_dir_stream dir_stream)
  757. {
  758. rewinddir(dir_stream);
  759. return __WASI_ESUCCESS;
  760. }
  761. __wasi_errno_t
  762. os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position)
  763. {
  764. seekdir(dir_stream, (long)position);
  765. return __WASI_ESUCCESS;
  766. }
  767. __wasi_errno_t
  768. os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry,
  769. const char **d_name)
  770. {
  771. errno = 0;
  772. struct dirent *dent = readdir(dir_stream);
  773. if (dent == NULL) {
  774. *d_name = NULL;
  775. if (errno != 0) {
  776. return convert_errno(errno);
  777. }
  778. else {
  779. return 0;
  780. }
  781. }
  782. long offset = (__wasi_dircookie_t)telldir(dir_stream);
  783. size_t namlen = strlen(dent->d_name);
  784. *d_name = dent->d_name;
  785. entry->d_next = offset;
  786. entry->d_namlen = (__wasi_dirnamlen_t)namlen;
  787. #if CONFIG_HAS_D_INO
  788. entry->d_ino = dent->d_ino;
  789. #else
  790. entry->d_ino = 0;
  791. #endif
  792. switch (dent->d_type) {
  793. case DT_BLK:
  794. entry->d_type = __WASI_FILETYPE_BLOCK_DEVICE;
  795. break;
  796. case DT_CHR:
  797. entry->d_type = __WASI_FILETYPE_CHARACTER_DEVICE;
  798. break;
  799. case DT_DIR:
  800. entry->d_type = __WASI_FILETYPE_DIRECTORY;
  801. break;
  802. case DT_FIFO:
  803. entry->d_type = __WASI_FILETYPE_SOCKET_STREAM;
  804. break;
  805. case DT_LNK:
  806. entry->d_type = __WASI_FILETYPE_SYMBOLIC_LINK;
  807. break;
  808. case DT_REG:
  809. entry->d_type = __WASI_FILETYPE_REGULAR_FILE;
  810. break;
  811. #ifdef DT_SOCK
  812. case DT_SOCK:
  813. // Technically not correct, but good enough.
  814. entry->d_type = __WASI_FILETYPE_SOCKET_STREAM;
  815. break;
  816. #endif
  817. default:
  818. entry->d_type = __WASI_FILETYPE_UNKNOWN;
  819. break;
  820. }
  821. return __WASI_ESUCCESS;
  822. }
  823. __wasi_errno_t
  824. os_closedir(os_dir_stream dir_stream)
  825. {
  826. int ret = closedir(dir_stream);
  827. if (ret < 0)
  828. return convert_errno(errno);
  829. return __WASI_ESUCCESS;
  830. }
  831. os_dir_stream
  832. os_get_invalid_dir_stream()
  833. {
  834. return NULL;
  835. }
  836. bool
  837. os_is_dir_stream_valid(os_dir_stream *dir_stream)
  838. {
  839. assert(dir_stream != NULL);
  840. return *dir_stream != NULL;
  841. }
  842. bool
  843. os_is_handle_valid(os_file_handle *handle)
  844. {
  845. assert(handle != NULL);
  846. return *handle > -1;
  847. }
  848. char *
  849. os_realpath(const char *path, char *resolved_path)
  850. {
  851. return realpath(path, resolved_path);
  852. }