zephyr_file.c 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. /*
  2. * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "platform_api_vmcore.h"
  6. #include "platform_api_extension.h"
  7. #include "libc_errno.h"
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <zephyr/fs/fs.h>
  11. #include <zephyr/fs/fs_interface.h>
  12. #include <zephyr/fs/fs_sys.h>
  13. #include <zephyr/fs/littlefs.h>
  14. /* Notes:
  15. * This is the implementation of a POSIX-like file system interface for Zephyr.
  16. * To manage our file descriptors, we created a struct `zephyr_fs_desc` that
  17. * represent a zephyr file descriptor and hold useful informations.
  18. * We also created a file descriptor table to keep track of all the file
  19. * descriptors.
  20. *
  21. * To pass the file descriptor reference to the higher level abstraction, we
  22. * pass the index of the fd table to an `os_file_handle` struct.
  23. * Then in the WASI implementation layer we can retrieve the file descriptor
  24. * reference.
  25. *
  26. * We also fake the stdin, stdout and stderr file descriptors.
  27. * We do not handle write on stdin and read on stdin, stdout and stderr.
  28. */
  29. // No OS API wrapper (Zephyr):
  30. // file:
  31. // off_t fs_tell(struct fs_file_t *zfp)
  32. // file system:
  33. // int fs_mount(struct fs_mount_t *mp)
  34. // int fs_unmount(struct fs_mount_t *mp
  35. // int fs_readmount(int *index, const char **name)
  36. // int fs_statvfs(const char *path, struct fs_statvfs *stat)
  37. // int fs_mkfs(int fs_type, uintptr_t dev_id, void *cfg, int flags)
  38. // int fs_register(int type, const struct fs_file_system_t *fs)
  39. // int fs_unregister(int type, const struct fs_file_system_t *fs)
  40. // We will take the maximum number of open files
  41. // from the Zephyr POSIX configuration
  42. #define CONFIG_WASI_MAX_OPEN_FILES CONFIG_ZVFS_OPEN_MAX
  43. static inline bool
  44. os_is_virtual_fd(int fd)
  45. {
  46. switch (fd) {
  47. case STDIN_FILENO:
  48. case STDOUT_FILENO:
  49. case STDERR_FILENO:
  50. return true;
  51. default:
  52. return false;
  53. };
  54. }
  55. // Macro to retrieve a file system descriptor and check it's validity.
  56. // fd's 0-2 are reserved for standard streams, hence the by-3 offsets.
  57. #define GET_FILE_SYSTEM_DESCRIPTOR(fd, ptr) \
  58. do { \
  59. if (os_is_virtual_fd(fd)) { \
  60. ptr = NULL; \
  61. break; \
  62. } \
  63. if (fd < 3 || fd >= CONFIG_WASI_MAX_OPEN_FILES + 3) { \
  64. return __WASI_EBADF; \
  65. } \
  66. k_mutex_lock(&desc_array_mutex, K_FOREVER); \
  67. ptr = &desc_array[(int)fd - 3]; \
  68. if (!ptr->used) { \
  69. k_mutex_unlock(&desc_array_mutex); \
  70. return __WASI_EBADF; \
  71. } \
  72. k_mutex_unlock(&desc_array_mutex); \
  73. } while (0)
  74. // Array to keep track of file system descriptors.
  75. static struct zephyr_fs_desc desc_array[CONFIG_WASI_MAX_OPEN_FILES];
  76. // mutex to protect the file descriptor array
  77. K_MUTEX_DEFINE(desc_array_mutex);
  78. static char prestat_dir[MAX_FILE_NAME + 1];
  79. bool
  80. build_absolute_path(char *abs_path, size_t abs_path_len, const char *path)
  81. {
  82. if (!path) {
  83. abs_path[0] = '\0';
  84. return false;
  85. }
  86. size_t len1 = strlen(prestat_dir);
  87. size_t len2 = strlen(path);
  88. if (len1 + 1 + len2 + 1 > abs_path_len) {
  89. abs_path[0] = '\0'; // Empty string on error
  90. return false; // Truncation would occur
  91. }
  92. snprintf(abs_path, abs_path_len, "%s/%s", prestat_dir, path);
  93. return true;
  94. }
  95. static struct zephyr_fs_desc *
  96. zephyr_fs_alloc_obj(bool is_dir, const char *path, int *index)
  97. {
  98. struct zephyr_fs_desc *ptr = NULL;
  99. *index = -1; // give a default value to index in case table is full
  100. k_mutex_lock(&desc_array_mutex, K_FOREVER);
  101. for (int i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) {
  102. if (desc_array[i].used == false) {
  103. ptr = &desc_array[i];
  104. ptr->used = true;
  105. ptr->is_dir = is_dir;
  106. ptr->path = bh_strdup(path);
  107. ptr->dir_index = 0;
  108. if (ptr->path == NULL) {
  109. ptr->used = false;
  110. k_mutex_unlock(&desc_array_mutex);
  111. return NULL;
  112. }
  113. *index = i + 3;
  114. break;
  115. }
  116. }
  117. k_mutex_unlock(&desc_array_mutex);
  118. if (ptr == NULL) {
  119. printk("Error: all file descriptor slots are in use (max = %d)\n",
  120. CONFIG_WASI_MAX_OPEN_FILES);
  121. }
  122. return ptr;
  123. }
  124. static inline void
  125. zephyr_fs_free_obj(struct zephyr_fs_desc *ptr)
  126. {
  127. BH_FREE(ptr->path);
  128. ptr->path = NULL;
  129. ptr->used = false;
  130. }
  131. /* /!\ Needed for socket to work */
  132. __wasi_errno_t
  133. os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf)
  134. {
  135. struct zephyr_fs_desc *ptr = NULL;
  136. int socktype, rc;
  137. if (!handle->is_sock) {
  138. if (os_is_virtual_fd(handle->fd)) {
  139. buf->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE;
  140. buf->st_size = 0;
  141. buf->st_atim = 0;
  142. buf->st_mtim = 0;
  143. buf->st_ctim = 0;
  144. return __WASI_ESUCCESS;
  145. }
  146. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  147. // Get file information using Zephyr's fs_stat function
  148. struct fs_dirent entry;
  149. rc = fs_stat(ptr->path, &entry);
  150. if (rc < 0) {
  151. return convert_errno(-rc);
  152. }
  153. // Fill in the __wasi_filestat_t structure
  154. buf->st_dev = 0; // Zephyr's fs_stat doesn't provide a device ID
  155. buf->st_ino = 0; // Zephyr's fs_stat doesn't provide an inode number
  156. buf->st_filetype = entry.type == FS_DIR_ENTRY_DIR
  157. ? __WASI_FILETYPE_DIRECTORY
  158. : __WASI_FILETYPE_REGULAR_FILE;
  159. buf->st_nlink = 1; // Zephyr's fs_stat doesn't provide a link count
  160. buf->st_size = entry.size;
  161. buf->st_atim = 0; // Zephyr's fs_stat doesn't provide timestamps
  162. buf->st_mtim = 0;
  163. buf->st_ctim = 0;
  164. return __WASI_ESUCCESS;
  165. // return os_fstatat(handle, ptr->path, buf, 0);
  166. }
  167. else {
  168. // socklen_t socktypelen = sizeof(socktype);
  169. // rc = zsock_getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &socktype,
  170. // &socktypelen); Using `zsock_getsockopt` will add a dependency on the
  171. // network stack
  172. // TODO: may add a type to the `zephyr_handle`.
  173. rc = 1;
  174. socktype = SOCK_STREAM;
  175. if (rc < 0) {
  176. return convert_errno(-rc);
  177. }
  178. switch (socktype) {
  179. case SOCK_DGRAM:
  180. buf->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM;
  181. break;
  182. case SOCK_STREAM:
  183. buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM;
  184. break;
  185. default:
  186. buf->st_filetype = __WASI_FILETYPE_UNKNOWN;
  187. break;
  188. }
  189. buf->st_size = 0;
  190. buf->st_atim = 0;
  191. buf->st_mtim = 0;
  192. buf->st_ctim = 0;
  193. return __WASI_ESUCCESS;
  194. }
  195. }
  196. __wasi_errno_t
  197. os_fstatat(os_file_handle handle, const char *path,
  198. struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags)
  199. {
  200. struct fs_dirent entry;
  201. int rc;
  202. if (handle->fd < 0) {
  203. return __WASI_EBADF;
  204. }
  205. char abs_path[MAX_FILE_NAME + 1];
  206. if (handle == NULL) {
  207. return __WASI_EINVAL; // Or another appropriate error code
  208. }
  209. if (!build_absolute_path(abs_path, sizeof(abs_path), path)) {
  210. return __WASI_ENOMEM;
  211. }
  212. // Get file information using Zephyr's fs_stat function
  213. rc = fs_stat(abs_path, &entry);
  214. if (rc < 0) {
  215. return convert_errno(-rc);
  216. }
  217. // Fill in the __wasi_filestat_t structure
  218. buf->st_dev = 0; // Zephyr's fs_stat doesn't provide a device ID
  219. // DSK: setting this to 0, in addition to d_ino = 1 causes failures with
  220. // readdir() So, here's a hack to to avoid this.
  221. buf->st_ino = 1; // Zephyr's fs_stat doesn't provide an inode number.
  222. buf->st_filetype = entry.type == FS_DIR_ENTRY_DIR
  223. ? __WASI_FILETYPE_DIRECTORY
  224. : __WASI_FILETYPE_REGULAR_FILE;
  225. buf->st_nlink = 1; // Zephyr's fs_stat doesn't provide a link count
  226. buf->st_size = entry.size;
  227. buf->st_atim = 0; // Zephyr's fs_stat doesn't provide timestamps
  228. buf->st_mtim = 0;
  229. buf->st_ctim = 0;
  230. return __WASI_ESUCCESS;
  231. }
  232. __wasi_errno_t
  233. os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags)
  234. {
  235. struct zephyr_fs_desc *ptr = NULL;
  236. if (os_is_virtual_fd(handle->fd)) {
  237. *flags = 0;
  238. return __WASI_ESUCCESS;
  239. }
  240. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  241. if ((ptr->file.flags & FS_O_APPEND) != 0) {
  242. *flags |= __WASI_FDFLAG_APPEND;
  243. }
  244. /* Others flags:
  245. * - __WASI_FDFLAG_DSYNC
  246. * - __WASI_FDFLAG_RSYNC
  247. * - __WASI_FDFLAG_SYNC
  248. * - __WASI_FDFLAG_NONBLOCK
  249. * Have no equivalent in Zephyr.
  250. */
  251. return __WASI_ESUCCESS;
  252. }
  253. __wasi_errno_t
  254. os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags)
  255. {
  256. if (os_is_virtual_fd(handle->fd)) {
  257. return __WASI_ESUCCESS;
  258. }
  259. struct zephyr_fs_desc *ptr = NULL;
  260. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  261. if ((flags & __WASI_FDFLAG_APPEND) != 0) {
  262. ptr->file.flags |= FS_O_APPEND;
  263. }
  264. /* Same as above */
  265. return __WASI_ESUCCESS;
  266. }
  267. __wasi_errno_t
  268. os_fdatasync(os_file_handle handle)
  269. {
  270. return os_fsync(handle);
  271. }
  272. __wasi_errno_t
  273. os_fsync(os_file_handle handle)
  274. {
  275. if (os_is_virtual_fd(handle->fd)) {
  276. return __WASI_ESUCCESS;
  277. }
  278. struct zephyr_fs_desc *ptr = NULL;
  279. int rc = 0;
  280. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  281. if (ptr->is_dir) {
  282. return __WASI_EISDIR;
  283. }
  284. rc = fs_sync(&ptr->file);
  285. if (rc < 0) {
  286. return convert_errno(-rc);
  287. }
  288. return __WASI_ESUCCESS;
  289. }
  290. __wasi_errno_t
  291. os_open_preopendir(const char *path, os_file_handle *out)
  292. {
  293. int rc, index;
  294. struct zephyr_fs_desc *ptr;
  295. *out = BH_MALLOC(sizeof(struct zephyr_handle));
  296. if (*out == NULL) {
  297. return __WASI_ENOMEM;
  298. }
  299. ptr = zephyr_fs_alloc_obj(true, path, &index);
  300. if (ptr == NULL) {
  301. BH_FREE(*out);
  302. return __WASI_EMFILE;
  303. }
  304. fs_dir_t_init(&ptr->dir);
  305. rc = fs_opendir(&ptr->dir, path);
  306. if (rc < 0) {
  307. zephyr_fs_free_obj(ptr);
  308. BH_FREE(*out);
  309. return convert_errno(-rc);
  310. }
  311. (*out)->fd = index;
  312. (*out)->is_sock = false;
  313. strncpy(prestat_dir, path, MAX_FILE_NAME + 1);
  314. prestat_dir[MAX_FILE_NAME] = '\0';
  315. return __WASI_ESUCCESS;
  316. }
  317. static int
  318. wasi_flags_to_zephyr(__wasi_oflags_t oflags, __wasi_fdflags_t fd_flags,
  319. __wasi_lookupflags_t lookup_flags,
  320. wasi_libc_file_access_mode access_mode)
  321. {
  322. int mode = 0;
  323. // Convert open flags.
  324. if ((oflags & __WASI_O_CREAT) != 0) {
  325. mode |= FS_O_CREATE;
  326. }
  327. if (((oflags & __WASI_O_EXCL) != 0) || ((oflags & __WASI_O_TRUNC) != 0)
  328. || ((oflags & __WASI_O_DIRECTORY) != 0)) {
  329. /* Zephyr is not POSIX no equivalent for these flags */
  330. /* __WASI_O_DIRECTORY: Open shouldn't handle directories */
  331. // TODO: log warning
  332. }
  333. // Convert file descriptor flags.
  334. if ((fd_flags & __WASI_FDFLAG_APPEND) != 0) {
  335. mode |= FS_O_APPEND;
  336. }
  337. if (((fd_flags & __WASI_FDFLAG_DSYNC) != 0)
  338. || ((fd_flags & __WASI_FDFLAG_RSYNC) != 0)
  339. || ((fd_flags & __WASI_FDFLAG_SYNC) != 0)
  340. || ((fd_flags & __WASI_FDFLAG_NONBLOCK) != 0)) {
  341. /* Zephyr is not POSIX no equivalent for these flags */
  342. // TODO: log warning
  343. }
  344. // Convert lookup flag.
  345. if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) {
  346. /* Zephyr is not POSIX no equivalent for these flags */
  347. // TODO: log warning
  348. return __WASI_ENOTSUP;
  349. }
  350. // Convert access mode.
  351. switch (access_mode) {
  352. case WASI_LIBC_ACCESS_MODE_READ_WRITE:
  353. mode |= FS_O_RDWR;
  354. break;
  355. case WASI_LIBC_ACCESS_MODE_READ_ONLY:
  356. mode |= FS_O_READ;
  357. break;
  358. case WASI_LIBC_ACCESS_MODE_WRITE_ONLY:
  359. mode |= FS_O_WRITE;
  360. break;
  361. default:
  362. // TODO: log warning
  363. break;
  364. }
  365. return mode;
  366. }
  367. __wasi_errno_t
  368. os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags,
  369. __wasi_fdflags_t fd_flags, __wasi_lookupflags_t lookup_flags,
  370. wasi_libc_file_access_mode access_mode, os_file_handle *out)
  371. {
  372. /*
  373. * `handle` will be unused because zephyr doesn't expose an openat
  374. * function and don't seem to have the concept of relative path.
  375. * We fill `out` with a new file descriptor.
  376. */
  377. int rc, index;
  378. struct zephyr_fs_desc *ptr = NULL;
  379. char abs_path[MAX_FILE_NAME + 1];
  380. *out = BH_MALLOC(sizeof(struct zephyr_handle));
  381. if (*out == NULL) {
  382. return __WASI_ENOMEM;
  383. }
  384. if (!build_absolute_path(abs_path, sizeof(abs_path), path)) {
  385. return __WASI_ENOMEM;
  386. }
  387. // Treat directories as a special case
  388. bool is_dir = oflags & __WASI_O_DIRECTORY;
  389. ptr = zephyr_fs_alloc_obj(is_dir, abs_path, &index);
  390. if (!ptr && (index < 0)) {
  391. BH_FREE(*out);
  392. return __WASI_EMFILE;
  393. }
  394. if (is_dir) {
  395. fs_dir_t_init(&ptr->dir);
  396. // fs_opendir() is called in libc later - don't call here
  397. }
  398. else {
  399. // Is a file
  400. int zmode =
  401. wasi_flags_to_zephyr(oflags, fd_flags, lookup_flags, access_mode);
  402. fs_file_t_init(&ptr->file);
  403. rc = fs_open(&ptr->file, abs_path, zmode);
  404. if (rc < 0) {
  405. zephyr_fs_free_obj(ptr);
  406. BH_FREE(*out);
  407. return convert_errno(-rc);
  408. }
  409. }
  410. (*out)->fd = index;
  411. (*out)->is_sock = false;
  412. return __WASI_ESUCCESS;
  413. }
  414. __wasi_errno_t
  415. os_file_get_access_mode(os_file_handle handle,
  416. wasi_libc_file_access_mode *access_mode)
  417. {
  418. if (handle->fd == STDIN_FILENO) {
  419. *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
  420. return __WASI_ESUCCESS;
  421. }
  422. else if (handle->fd == STDOUT_FILENO || handle->fd == STDERR_FILENO) {
  423. *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
  424. return __WASI_ESUCCESS;
  425. }
  426. struct zephyr_fs_desc *ptr = NULL;
  427. if (handle->is_sock) {
  428. // for socket we can use the following code
  429. // TODO: Need to determine better logic
  430. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  431. return __WASI_ESUCCESS;
  432. }
  433. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  434. if (ptr->is_dir) {
  435. // DSK: is this actually the correct mode for a dir?
  436. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  437. return __WASI_ESUCCESS;
  438. }
  439. if ((ptr->file.flags & FS_O_RDWR) != 0) {
  440. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  441. }
  442. else if ((ptr->file.flags & FS_O_READ) != 0) {
  443. *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY;
  444. }
  445. else if ((ptr->file.flags & FS_O_WRITE) != 0) {
  446. *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY;
  447. }
  448. else {
  449. // we return read/write by default
  450. *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE;
  451. }
  452. return __WASI_ESUCCESS;
  453. }
  454. __wasi_errno_t
  455. os_close(os_file_handle handle, bool is_stdio)
  456. {
  457. int rc;
  458. struct zephyr_fs_desc *ptr = NULL;
  459. if (is_stdio)
  460. return __WASI_ESUCCESS;
  461. if (handle->is_sock) {
  462. rc = zsock_close(handle->fd);
  463. }
  464. // Handle is assumed to be a file descriptor
  465. else {
  466. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  467. rc = ptr->is_dir ? fs_closedir(&ptr->dir) : fs_close(&ptr->file);
  468. zephyr_fs_free_obj(ptr); // free in any case.
  469. }
  470. BH_FREE(handle);
  471. if (rc < 0) {
  472. return convert_errno(-rc);
  473. }
  474. return __WASI_ESUCCESS;
  475. }
  476. __wasi_errno_t
  477. os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  478. __wasi_filesize_t offset, size_t *nread)
  479. {
  480. if (handle->fd == STDIN_FILENO) {
  481. return __WASI_ENOSYS;
  482. }
  483. struct zephyr_fs_desc *ptr = NULL;
  484. int rc;
  485. ssize_t total_read = 0;
  486. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  487. // Seek to the offset
  488. rc = fs_seek(&ptr->file, offset, FS_SEEK_SET);
  489. if (rc < 0) {
  490. return convert_errno(-rc);
  491. }
  492. // Read data into each buffer
  493. for (int i = 0; i < iovcnt; i++) {
  494. ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len);
  495. if (bytes_read < 0) {
  496. return convert_errno(-bytes_read);
  497. }
  498. total_read += bytes_read;
  499. // If we read less than we asked for, stop reading
  500. if (bytes_read < iov[i].buf_len) {
  501. break;
  502. }
  503. }
  504. *nread = total_read;
  505. return __WASI_ESUCCESS;
  506. }
  507. __wasi_errno_t
  508. os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  509. __wasi_filesize_t offset, size_t *nwritten)
  510. {
  511. struct zephyr_fs_desc *ptr = NULL;
  512. ssize_t total_written = 0;
  513. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  514. // Seek to the offset
  515. int rc = fs_seek(&ptr->file, offset, FS_SEEK_SET);
  516. if (rc < 0) {
  517. return convert_errno(-rc);
  518. }
  519. // Write data from each buffer
  520. for (int i = 0; i < iovcnt; i++) {
  521. ssize_t bytes_written =
  522. fs_write(&ptr->file, iov[i].buf, iov[i].buf_len);
  523. if (bytes_written < 0) {
  524. return convert_errno(-bytes_written);
  525. }
  526. total_written += bytes_written;
  527. // If we wrote less than we asked for, stop writing
  528. if (bytes_written < iov[i].buf_len) {
  529. break;
  530. }
  531. }
  532. *nwritten = total_written;
  533. return __WASI_ESUCCESS;
  534. }
  535. __wasi_errno_t
  536. os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt,
  537. size_t *nread)
  538. {
  539. struct zephyr_fs_desc *ptr = NULL;
  540. ssize_t total_read = 0;
  541. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  542. // Read data into each buffer
  543. for (int i = 0; i < iovcnt; i++) {
  544. ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len);
  545. if (bytes_read < 0) {
  546. // If an error occurred, return it
  547. return convert_errno(-bytes_read);
  548. }
  549. total_read += bytes_read;
  550. // If we read less than we asked for, stop reading
  551. if (bytes_read < iov[i].buf_len) {
  552. break;
  553. }
  554. }
  555. *nread = total_read;
  556. return __WASI_ESUCCESS;
  557. }
  558. /* With wasi-libc we need to redirect write on stdout/err to printf */
  559. // TODO: handle write on stdin
  560. __wasi_errno_t
  561. os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt,
  562. size_t *nwritten)
  563. {
  564. ssize_t total_written = 0;
  565. if (os_is_virtual_fd(handle->fd)) {
  566. FILE *fd = (handle->fd == STDERR_FILENO) ? stderr : stdout;
  567. for (int i = 0; i < iovcnt; i++) {
  568. ssize_t bytes_written = fwrite(iov[i].buf, 1, iov[i].buf_len, fd);
  569. if (bytes_written < 0)
  570. return convert_errno(-bytes_written);
  571. total_written += bytes_written;
  572. }
  573. *nwritten = total_written;
  574. return __WASI_ESUCCESS;
  575. }
  576. struct zephyr_fs_desc *ptr = NULL;
  577. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  578. // Write data from each buffer
  579. for (int i = 0; i < iovcnt; i++) {
  580. ssize_t bytes_written =
  581. fs_write(&ptr->file, iov[i].buf, iov[i].buf_len);
  582. if (bytes_written < 0) {
  583. return convert_errno(-bytes_written);
  584. }
  585. total_written += bytes_written;
  586. // If we wrote less than we asked for, stop writing
  587. if (bytes_written < iov[i].buf_len) {
  588. break;
  589. }
  590. }
  591. *nwritten = total_written;
  592. return __WASI_ESUCCESS;
  593. }
  594. __wasi_errno_t
  595. os_fallocate(os_file_handle handle, __wasi_filesize_t offset,
  596. __wasi_filesize_t length)
  597. {
  598. return __WASI_ENOSYS;
  599. }
  600. __wasi_errno_t
  601. os_ftruncate(os_file_handle handle, __wasi_filesize_t size)
  602. {
  603. if (os_is_virtual_fd(handle->fd)) {
  604. return __WASI_EINVAL;
  605. }
  606. struct zephyr_fs_desc *ptr = NULL;
  607. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  608. int rc = fs_truncate(&ptr->file, (off_t)size);
  609. if (rc < 0) {
  610. return convert_errno(-rc);
  611. }
  612. return __WASI_ESUCCESS;
  613. }
  614. __wasi_errno_t
  615. os_futimens(os_file_handle handle, __wasi_timestamp_t access_time,
  616. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags)
  617. {
  618. return __WASI_ENOSYS;
  619. }
  620. __wasi_errno_t
  621. os_utimensat(os_file_handle handle, const char *path,
  622. __wasi_timestamp_t access_time,
  623. __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags,
  624. __wasi_lookupflags_t lookup_flags)
  625. {
  626. return __WASI_ENOSYS;
  627. }
  628. __wasi_errno_t
  629. os_readlinkat(os_file_handle handle, const char *path, char *buf,
  630. size_t bufsize, size_t *nread)
  631. {
  632. return __WASI_ENOSYS;
  633. }
  634. __wasi_errno_t
  635. os_linkat(os_file_handle from_handle, const char *from_path,
  636. os_file_handle to_handle, const char *to_path,
  637. __wasi_lookupflags_t lookup_flags)
  638. {
  639. return __WASI_ENOSYS;
  640. }
  641. __wasi_errno_t
  642. os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path)
  643. {
  644. return __WASI_ENOSYS;
  645. }
  646. __wasi_errno_t
  647. os_mkdirat(os_file_handle handle, const char *path)
  648. {
  649. struct zephyr_fs_desc *ptr = NULL;
  650. int index, rc;
  651. char abs_path[MAX_FILE_NAME + 1];
  652. if (handle == NULL) {
  653. return __WASI_EINVAL; // Or another appropriate error code
  654. }
  655. if (!build_absolute_path(abs_path, sizeof(abs_path), path)) {
  656. return __WASI_ENOMEM;
  657. }
  658. rc = fs_mkdir(abs_path);
  659. if (rc < 0) {
  660. return convert_errno(-rc);
  661. }
  662. ptr = zephyr_fs_alloc_obj(true, abs_path, &index);
  663. if (!ptr) {
  664. fs_unlink(abs_path);
  665. return __WASI_EMFILE;
  666. }
  667. fs_dir_t_init(&ptr->dir);
  668. handle->fd = index;
  669. handle->is_sock = false;
  670. return __WASI_ESUCCESS;
  671. }
  672. // DSK: Somewhere along the WASI libc implementation path, the knowledge
  673. // was lost that `old_handle` and `new_handle` refer to directories that
  674. // contain the files to be renamed, rather than the file fds themselves:
  675. //
  676. // __wasilibc_nocwd_renameat(old_dirfd, old_relative_path,
  677. // new_dirfd, new_relative_path);
  678. //
  679. // Therefore we won't mess with the supplied fd's, and work only off
  680. // of the supplied paths. Note: this will change when more than one
  681. // pre-opened dir is supported in the future.
  682. __wasi_errno_t
  683. os_renameat(os_file_handle old_handle, const char *old_path,
  684. os_file_handle new_handle, const char *new_path)
  685. {
  686. // directories, safe to ignore
  687. (void)old_handle;
  688. (void)new_handle;
  689. char abs_old_path[MAX_FILE_NAME + 1];
  690. char abs_new_path[MAX_FILE_NAME + 1];
  691. if (!build_absolute_path(abs_old_path, sizeof(abs_old_path), old_path)) {
  692. return __WASI_ENOMEM;
  693. }
  694. if (!build_absolute_path(abs_new_path, sizeof(abs_new_path), new_path)) {
  695. return __WASI_ENOMEM;
  696. }
  697. // Attempt to perform the rename
  698. int rc = fs_rename(abs_old_path, abs_new_path);
  699. if (rc < 0) {
  700. return convert_errno(-rc);
  701. }
  702. // If there is an allocated fd in our table, update the descriptor table
  703. // entry DSK: better approach here?
  704. k_mutex_lock(&desc_array_mutex, K_FOREVER);
  705. for (int i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) {
  706. struct zephyr_fs_desc *ptr = &desc_array[i];
  707. if (ptr->used && ptr->path && strcmp(ptr->path, abs_old_path) == 0) {
  708. char *new_path_copy = bh_strdup(new_path);
  709. if (new_path_copy != NULL) {
  710. BH_FREE(ptr->path);
  711. ptr->path = new_path_copy;
  712. }
  713. break; // Only one descriptor should match
  714. }
  715. }
  716. k_mutex_unlock(&desc_array_mutex);
  717. return __WASI_ESUCCESS;
  718. }
  719. // DSK: Same thing as renameat: `handle` refers to the containing directory,
  720. // not the file handle to unlink. We ignore the handle and use the path
  721. // exclusively.
  722. //
  723. // TODO: is there anything we need to do in case is_dir=true?
  724. __wasi_errno_t
  725. os_unlinkat(os_file_handle handle, const char *path, bool is_dir)
  726. {
  727. (void)handle;
  728. char abs_path[MAX_FILE_NAME + 1];
  729. if (!build_absolute_path(abs_path, sizeof(abs_path), path)) {
  730. return __WASI_ENOMEM;
  731. }
  732. int rc = fs_unlink(abs_path);
  733. if (rc < 0) {
  734. return convert_errno(-rc);
  735. }
  736. // Search for any active descriptor referencing this path and free it.
  737. k_mutex_lock(&desc_array_mutex, K_FOREVER);
  738. for (int i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) {
  739. struct zephyr_fs_desc *ptr = &desc_array[i];
  740. if (ptr->used && ptr->path && strcmp(ptr->path, abs_path) == 0) {
  741. zephyr_fs_free_obj(ptr);
  742. break;
  743. }
  744. }
  745. k_mutex_unlock(&desc_array_mutex);
  746. return __WASI_ESUCCESS;
  747. }
  748. __wasi_errno_t
  749. os_lseek(os_file_handle handle, __wasi_filedelta_t offset,
  750. __wasi_whence_t whence, __wasi_filesize_t *new_offset)
  751. {
  752. if (os_is_virtual_fd(handle->fd)) {
  753. return __WASI_ESPIPE; // Seeking not supported on character streams
  754. }
  755. struct zephyr_fs_desc *ptr = NULL;
  756. int zwhence;
  757. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  758. // They have the same value but this is more explicit
  759. switch (whence) {
  760. case __WASI_WHENCE_SET:
  761. zwhence = FS_SEEK_SET;
  762. break;
  763. case __WASI_WHENCE_CUR:
  764. zwhence = FS_SEEK_CUR;
  765. break;
  766. case __WASI_WHENCE_END:
  767. zwhence = FS_SEEK_END;
  768. break;
  769. default:
  770. return __WASI_EINVAL;
  771. }
  772. off_t rc = fs_seek(&ptr->file, (off_t)offset, zwhence);
  773. if (rc < 0) {
  774. return convert_errno(-rc);
  775. }
  776. *new_offset = (__wasi_filesize_t)rc;
  777. return __WASI_ESUCCESS;
  778. }
  779. __wasi_errno_t
  780. os_fadvise(os_file_handle handle, __wasi_filesize_t offset,
  781. __wasi_filesize_t length, __wasi_advice_t advice)
  782. {
  783. return __WASI_ENOSYS;
  784. }
  785. __wasi_errno_t
  786. os_isatty(os_file_handle handle)
  787. {
  788. if (os_is_virtual_fd(handle->fd)) {
  789. return __WASI_ESUCCESS;
  790. }
  791. return __WASI_ENOTTY;
  792. }
  793. os_file_handle
  794. os_convert_stdin_handle(os_raw_file_handle raw_stdin)
  795. {
  796. os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle));
  797. if (!handle)
  798. return NULL;
  799. handle->fd = STDIN_FILENO;
  800. handle->is_sock = false;
  801. return handle;
  802. }
  803. os_file_handle
  804. os_convert_stdout_handle(os_raw_file_handle raw_stdout)
  805. {
  806. os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle));
  807. if (!handle)
  808. return NULL;
  809. handle->fd = STDOUT_FILENO;
  810. handle->is_sock = false;
  811. return handle;
  812. }
  813. os_file_handle
  814. os_convert_stderr_handle(os_raw_file_handle raw_stderr)
  815. {
  816. os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle));
  817. if (!handle)
  818. return NULL;
  819. handle->fd = STDERR_FILENO;
  820. handle->is_sock = false;
  821. return handle;
  822. }
  823. __wasi_errno_t
  824. os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream)
  825. {
  826. /* Here we assume that either mdkdir or preopendir was called
  827. * before otherwise function will fail.
  828. */
  829. struct zephyr_fs_desc *ptr = NULL;
  830. GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr);
  831. if (!ptr->is_dir) {
  832. return __WASI_ENOTDIR;
  833. }
  834. int rc = fs_opendir(&ptr->dir, ptr->path);
  835. if (rc < 0) {
  836. return convert_errno(-rc);
  837. }
  838. /* we store the fd in the `os_dir_stream` to use other function */
  839. *dir_stream = handle->fd;
  840. return __WASI_ESUCCESS;
  841. }
  842. // DSK: simple open and close to rewind index.
  843. __wasi_errno_t
  844. os_rewinddir(os_dir_stream dir_stream)
  845. {
  846. struct zephyr_fs_desc *ptr = NULL;
  847. GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr);
  848. if (!ptr->is_dir)
  849. return __WASI_ENOTDIR;
  850. int rc = fs_closedir(&ptr->dir); // Close current stream
  851. if (rc < 0)
  852. return convert_errno(-rc);
  853. rc = fs_opendir(&ptr->dir, ptr->path); // Reopen from start
  854. if (rc < 0)
  855. return convert_errno(-rc);
  856. ptr->dir_index = 0; // Reset virtual position tracker
  857. return __WASI_ESUCCESS;
  858. }
  859. // DSK: start from 0 and linear seek since there's no cookies in the zephyr fs
  860. // TODO: duplicated code with rewinddir
  861. __wasi_errno_t
  862. os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position)
  863. {
  864. struct zephyr_fs_desc *ptr = NULL;
  865. GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr);
  866. if (!ptr->is_dir)
  867. return __WASI_ENOTDIR;
  868. int rc = fs_closedir(&ptr->dir);
  869. if (rc < 0)
  870. return convert_errno(-rc);
  871. rc = fs_opendir(&ptr->dir, ptr->path);
  872. if (rc < 0)
  873. return convert_errno(-rc);
  874. // Emulate seek by re-reading entries up to 'position'
  875. struct fs_dirent tmp;
  876. for (__wasi_dircookie_t i = 0; i < position; i++) {
  877. rc = fs_readdir(&ptr->dir, &tmp);
  878. if (rc < 0)
  879. return convert_errno(-rc);
  880. if (tmp.name[0] == '\0')
  881. break; // End of directory
  882. }
  883. ptr->dir_index = position;
  884. return __WASI_ESUCCESS;
  885. }
  886. __wasi_errno_t
  887. os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry,
  888. const char **d_name)
  889. {
  890. struct fs_dirent fs_entry;
  891. struct zephyr_fs_desc *ptr = NULL;
  892. GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr);
  893. if (!ptr->is_dir) {
  894. return __WASI_ENOTDIR;
  895. }
  896. int rc = fs_readdir(&ptr->dir, &fs_entry);
  897. if (rc < 0) {
  898. return convert_errno(-rc);
  899. }
  900. if (fs_entry.name[0] == '\0') {
  901. // DSK: the caller expects the name buffer to be null
  902. // when we've reached the end of the directory.
  903. *d_name = NULL;
  904. return __WASI_ESUCCESS;
  905. }
  906. // DSK: emulated increasing value for rewinddir and seekdir
  907. entry->d_next = ++ptr->dir_index;
  908. // DSK: A hack to get readdir working. This needs to be non-zero along with
  909. // st_ino for the libc side of readdir to work correctly.
  910. entry->d_ino = 1 + ptr->dir_index;
  911. entry->d_namlen = strlen(fs_entry.name);
  912. entry->d_type = fs_entry.type == FS_DIR_ENTRY_DIR
  913. ? __WASI_FILETYPE_DIRECTORY
  914. : __WASI_FILETYPE_REGULAR_FILE;
  915. // DSK: name exists in fs_entry and we need to return it
  916. static char name_buf[MAX_FILE_NAME + 1];
  917. strncpy(name_buf, fs_entry.name, MAX_FILE_NAME);
  918. name_buf[MAX_FILE_NAME] = '\0';
  919. *d_name = name_buf;
  920. return __WASI_ESUCCESS;
  921. }
  922. __wasi_errno_t
  923. os_closedir(os_dir_stream dir_stream)
  924. {
  925. struct zephyr_fs_desc *ptr = NULL;
  926. GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr);
  927. if (!ptr->is_dir) {
  928. return __WASI_ENOTDIR;
  929. }
  930. int rc = fs_closedir(&ptr->dir);
  931. zephyr_fs_free_obj(ptr); // free in any case.
  932. if (rc < 0) {
  933. return convert_errno(-rc);
  934. }
  935. return __WASI_ESUCCESS;
  936. }
  937. os_dir_stream
  938. os_get_invalid_dir_stream()
  939. {
  940. return OS_DIR_STREAM_INVALID;
  941. }
  942. bool
  943. os_is_dir_stream_valid(os_dir_stream *dir_stream)
  944. {
  945. // DSK: this probably needs a check...
  946. return false;
  947. }
  948. bool
  949. os_is_handle_valid(os_file_handle *handle)
  950. {
  951. if (handle == NULL || *handle == NULL) {
  952. return false;
  953. }
  954. return (*handle)->fd > -1;
  955. }
  956. char *
  957. os_realpath(const char *path, char *resolved_path)
  958. {
  959. /* In fact we could implement a path resolving method, because every paths
  960. * are at one point put into memory.
  961. * We could then maintain a 'tree' to represent the file system.
  962. * --> The file system root is easily accessable with:
  963. * * (fs_dir_t) dir.mp->mnt_point
  964. * * (fs_file_t) file.mp->mnt_point
  965. * But we will just use absolute path for now.
  966. */
  967. if ((!path) || (strlen(path) > PATH_MAX)) {
  968. // Invalid input, path has to be valid and less than PATH_MAX
  969. return NULL;
  970. }
  971. return strncpy(resolved_path, path, PATH_MAX);
  972. }
  973. bool
  974. os_compare_file_handle(os_file_handle handle1, os_file_handle handle2)
  975. {
  976. return handle1->fd == handle2->fd && handle1->is_sock == handle2->is_sock;
  977. }
  978. bool
  979. os_is_stdin_handle(os_file_handle handle)
  980. {
  981. return (handle == (os_file_handle)stdin);
  982. }
  983. bool
  984. os_is_stdout_handle(os_file_handle handle)
  985. {
  986. return (handle == (os_file_handle)stdout);
  987. }
  988. bool
  989. os_is_stderr_handle(os_file_handle handle)
  990. {
  991. return (handle == (os_file_handle)stderr);
  992. }