zephyr_file.c 33 KB

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