diff --git a/core/shared/platform/common/posix/posix_convert_stat.c b/core/shared/platform/common/posix/posix_convert_stat.c new file mode 100644 index 0000000000..a154b9f2d9 --- /dev/null +++ b/core/shared/platform/common/posix/posix_convert_stat.c @@ -0,0 +1,79 @@ +#include "platform_api_extension.h" + +// Converts a POSIX timespec to a WASI timestamp. +__wasi_timestamp_t +static posix_convert_timespec(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + + (__wasi_timestamp_t)ts->tv_nsec; +} + +// Converts a POSIX stat structure to a WASI filestat structure +void +posix_convert_stat(os_file_handle handle, const struct stat *in, + __wasi_filestat_t *out) +{ + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_nlink = (__wasi_linkcount_t)in->st_nlink; + out->st_size = (__wasi_filesize_t)in->st_size; +#ifdef __APPLE__ + out->st_atim = posix_convert_timespec(&in->st_atimespec); + out->st_mtim = posix_convert_timespec(&in->st_mtimespec); + out->st_ctim = posix_convert_timespec(&in->st_ctimespec); +#else + out->st_atim = posix_convert_timespec(&in->st_atim); + out->st_mtim = posix_convert_timespec(&in->st_mtim); + out->st_ctim = posix_convert_timespec(&in->st_ctim); +#endif + + // Convert the file type. In the case of sockets there is no way we + // can easily determine the exact socket type. + if (S_ISBLK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + } + else if (S_ISCHR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + } + else if (S_ISDIR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_DIRECTORY; + } + else if (S_ISFIFO(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + } + else if (S_ISLNK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; + } + else if (S_ISREG(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_REGULAR_FILE; + } + else if (S_ISSOCK(in->st_mode)) { + int socktype; + socklen_t socktypelen = sizeof(socktype); + + if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) + < 0) { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + + switch (socktype) { + case SOCK_DGRAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + } + else { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + } +} diff --git a/core/shared/platform/common/posix/posix_convert_stat.h b/core/shared/platform/common/posix/posix_convert_stat.h new file mode 100644 index 0000000000..51f86d4b1f --- /dev/null +++ b/core/shared/platform/common/posix/posix_convert_stat.h @@ -0,0 +1,6 @@ +#include "platform_api_extension.h" + +// Converts a POSIX stat structure to a WASI filestat structure +void +posix_convert_stat(os_file_handle handle, const struct stat *in, + __wasi_filestat_t *out); diff --git a/core/shared/platform/common/posix/posix_file.c b/core/shared/platform/common/posix/posix_file.c index a05853f5e4..e2e92169f4 100644 --- a/core/shared/platform/common/posix/posix_file.c +++ b/core/shared/platform/common/posix/posix_file.c @@ -6,6 +6,7 @@ #include "platform_api_extension.h" #include "libc_errno.h" #include +#include "posix_util_convert.h" #if !defined(__APPLE__) && !defined(ESP_PLATFORM) #define CONFIG_HAS_PWRITEV 1 @@ -66,84 +67,6 @@ #define STDERR_FILENO 2 #endif -// Converts a POSIX timespec to a WASI timestamp. -static __wasi_timestamp_t -convert_timespec(const struct timespec *ts) -{ - if (ts->tv_sec < 0) - return 0; - if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) - return UINT64_MAX; - return (__wasi_timestamp_t)ts->tv_sec * 1000000000 - + (__wasi_timestamp_t)ts->tv_nsec; -} - -// Converts a POSIX stat structure to a WASI filestat structure -static void -convert_stat(os_file_handle handle, const struct stat *in, - __wasi_filestat_t *out) -{ - out->st_dev = in->st_dev; - out->st_ino = in->st_ino; - out->st_nlink = (__wasi_linkcount_t)in->st_nlink; - out->st_size = (__wasi_filesize_t)in->st_size; -#ifdef __APPLE__ - out->st_atim = convert_timespec(&in->st_atimespec); - out->st_mtim = convert_timespec(&in->st_mtimespec); - out->st_ctim = convert_timespec(&in->st_ctimespec); -#else - out->st_atim = convert_timespec(&in->st_atim); - out->st_mtim = convert_timespec(&in->st_mtim); - out->st_ctim = convert_timespec(&in->st_ctim); -#endif - - // Convert the file type. In the case of sockets there is no way we - // can easily determine the exact socket type. - if (S_ISBLK(in->st_mode)) { - out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; - } - else if (S_ISCHR(in->st_mode)) { - out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; - } - else if (S_ISDIR(in->st_mode)) { - out->st_filetype = __WASI_FILETYPE_DIRECTORY; - } - else if (S_ISFIFO(in->st_mode)) { - out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; - } - else if (S_ISLNK(in->st_mode)) { - out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; - } - else if (S_ISREG(in->st_mode)) { - out->st_filetype = __WASI_FILETYPE_REGULAR_FILE; - } - else if (S_ISSOCK(in->st_mode)) { - int socktype; - socklen_t socktypelen = sizeof(socktype); - - if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) - < 0) { - out->st_filetype = __WASI_FILETYPE_UNKNOWN; - return; - } - - switch (socktype) { - case SOCK_DGRAM: - out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM; - break; - case SOCK_STREAM: - out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; - break; - default: - out->st_filetype = __WASI_FILETYPE_UNKNOWN; - return; - } - } - else { - out->st_filetype = __WASI_FILETYPE_UNKNOWN; - } -} - static void convert_timestamp(__wasi_timestamp_t in, struct timespec *out) { @@ -196,7 +119,7 @@ os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) if (ret < 0) return convert_errno(errno); - convert_stat(handle, &stat_buf, buf); + posix_convert_stat(handle, &stat_buf, buf); return __WASI_ESUCCESS; } @@ -214,7 +137,7 @@ os_fstatat(os_file_handle handle, const char *path, if (ret < 0) return convert_errno(errno); - convert_stat(handle, &stat_buf, buf); + posix_convert_stat(handle, &stat_buf, buf); return __WASI_ESUCCESS; } diff --git a/core/shared/platform/zephyr/platform_internal.h b/core/shared/platform/zephyr/platform_internal.h index d5f0c80d81..9b3cd8ebb2 100644 --- a/core/shared/platform/zephyr/platform_internal.h +++ b/core/shared/platform/zephyr/platform_internal.h @@ -53,6 +53,8 @@ #include #endif /* end of KERNEL_VERSION_NUMBER < 0x030200 */ +// #include + #ifdef CONFIG_USERSPACE #include #include @@ -262,6 +264,7 @@ typedef struct zephyr_fs_desc { typedef struct zephyr_handle { int fd; bool is_sock; + bool is_posix_fd; } zephyr_handle; typedef struct zephyr_handle *os_file_handle; @@ -290,7 +293,9 @@ typedef struct timespec os_timespec; #define CLOCK_REALTIME 1 #endif +#ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 4 +#endif static inline int os_sched_yield(void) diff --git a/core/shared/platform/zephyr/shared_platform.cmake b/core/shared/platform/zephyr/shared_platform.cmake index f424b97204..d5f724393e 100644 --- a/core/shared/platform/zephyr/shared_platform.cmake +++ b/core/shared/platform/zephyr/shared_platform.cmake @@ -15,13 +15,19 @@ if(${CONFIG_MINIMAL_LIBC}) set (source_all ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) endif() +# list(APPEND source_all ${PLATFORM_SHARED_DIR}/../common/posix/posix_file.c) +list(APPEND source_all ${PLATFORM_SHARED_DIR}/../common/posix/posix_convert_stat.c) + +# list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_file.c) + + if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1) list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_socket.c) list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_file.c) list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_clock.c) else() - include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) - set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) + include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) + set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) endif () set (PLATFORM_SHARED_SOURCE ${source_all}) diff --git a/core/shared/platform/zephyr/zephyr_file.c b/core/shared/platform/zephyr/zephyr_file.c index 1f48bc010c..4b2c2b5595 100644 --- a/core/shared/platform/zephyr/zephyr_file.c +++ b/core/shared/platform/zephyr/zephyr_file.c @@ -7,8 +7,15 @@ #include "platform_api_extension.h" #include "libc_errno.h" +#include "bh_common.h" +#include "unistd.h" + +#include "../common/posix/posix_convert_stat.h" + + #include #include +#include #include #include @@ -168,6 +175,19 @@ os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) return __WASI_ESUCCESS; } + if (handle->is_posix_fd) { + struct stat stat_buf; + rc = fstat(handle->fd, &stat_buf); + + if (rc < 0) { + return convert_errno(errno); + } + + posix_convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; + } + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); // Get file information using Zephyr's fs_stat function @@ -278,6 +298,34 @@ os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags) return __WASI_ESUCCESS; } + if (handle->is_posix_fd) { + int ret = fcntl(handle->fd, F_GETFL); + + if (ret < 0) + return convert_errno(errno); + + *flags = 0; + + if ((ret & O_APPEND) != 0) + *flags |= __WASI_FDFLAG_APPEND; +#ifdef CONFIG_HAS_O_DSYNC + if ((ret & O_DSYNC) != 0) + *flags |= __WASI_FDFLAG_DSYNC; +#endif + if ((ret & O_NONBLOCK) != 0) + *flags |= __WASI_FDFLAG_NONBLOCK; +#ifdef CONFIG_HAS_O_RSYNC + if ((ret & O_RSYNC) != 0) + *flags |= __WASI_FDFLAG_RSYNC; +#endif +#ifdef CONFIG_HAS_O_SYNC + if ((ret & O_SYNC) != 0) + *flags |= __WASI_FDFLAG_SYNC; +#endif + + return __WASI_ESUCCESS; + } + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); if ((ptr->file.flags & FS_O_APPEND) != 0) { @@ -300,6 +348,40 @@ os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) return __WASI_ESUCCESS; } + if (handle->is_posix_fd) { + int fcntl_flags = 0; + + if ((flags & __WASI_FDFLAG_APPEND) != 0) + fcntl_flags |= O_APPEND; + if ((flags & __WASI_FDFLAG_DSYNC) != 0) + #ifdef CONFIG_HAS_O_DSYNC + fcntl_flags |= O_DSYNC; + #else + return __WASI_ENOTSUP; + #endif + if ((flags & __WASI_FDFLAG_NONBLOCK) != 0) + fcntl_flags |= O_NONBLOCK; + if ((flags & __WASI_FDFLAG_RSYNC) != 0) + #ifdef CONFIG_HAS_O_RSYNC + fcntl_flags |= O_RSYNC; + #else + return __WASI_ENOTSUP; + #endif + if ((flags & __WASI_FDFLAG_SYNC) != 0) + #ifdef CONFIG_HAS_O_SYNC + fcntl_flags |= O_SYNC; + #else + return __WASI_ENOTSUP; + #endif + + int ret = fcntl(handle->fd, F_SETFL, fcntl_flags); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; + } + struct zephyr_fs_desc *ptr = NULL; GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); @@ -314,7 +396,12 @@ os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) __wasi_errno_t os_fdatasync(os_file_handle handle) { - return os_fsync(handle); + if (handle->is_posix_fd) { + return fsync(handle->fd); + } + else { + return os_fsync(handle); + } } __wasi_errno_t @@ -327,13 +414,18 @@ os_fsync(os_file_handle handle) struct zephyr_fs_desc *ptr = NULL; int rc = 0; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + if (handle->is_posix_fd) { + rc = fsync(handle->fd); + } + else { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); - if (ptr->is_dir) { - return __WASI_EISDIR; + if (ptr->is_dir) { + return __WASI_EISDIR; + } + rc = fs_sync(&ptr->file); } - rc = fs_sync(&ptr->file); if (rc < 0) { return convert_errno(-rc); } @@ -369,6 +461,7 @@ os_open_preopendir(const char *path, os_file_handle *out) (*out)->fd = index; (*out)->is_sock = false; + (*out)->is_posix_fd = false; strncpy(prestat_dir, path, MAX_FILE_NAME + 1); prestat_dir[MAX_FILE_NAME] = '\0'; @@ -483,6 +576,7 @@ os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, (*out)->fd = index; (*out)->is_sock = false; + (*out)->is_posix_fd = false; return __WASI_ESUCCESS; } @@ -510,6 +604,13 @@ os_file_get_access_mode(os_file_handle handle, return __WASI_ESUCCESS; } + if (handle->is_posix_fd) { + // for socket we can use the following code + // TODO: Need to determine better logic + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + return __WASI_ESUCCESS; + } + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); if (ptr->is_dir) { @@ -548,9 +649,13 @@ os_close(os_file_handle handle, bool is_stdio) } // Handle is assumed to be a file descriptor else { - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); - - rc = ptr->is_dir ? fs_closedir(&ptr->dir) : fs_close(&ptr->file); + if (handle->is_posix_fd) { + rc = close(handle->fd); + } + else { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + rc = ptr->is_dir ? fs_closedir(&ptr->dir) : fs_close(&ptr->file); + } zephyr_fs_free_obj(ptr); // free in any case. } @@ -574,17 +679,28 @@ os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, int rc; ssize_t total_read = 0; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); - // Seek to the offset - rc = fs_seek(&ptr->file, offset, FS_SEEK_SET); + if (handle->is_posix_fd) { + rc = lseek(handle->fd, offset, SEEK_SET); + } + else { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + rc = fs_seek(&ptr->file, offset, FS_SEEK_SET); + } + if (rc < 0) { return convert_errno(-rc); } // Read data into each buffer for (int i = 0; i < iovcnt; i++) { - ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len); + ssize_t bytes_read; + if (handle->is_posix_fd) { + bytes_read = read(handle->fd, iov[i].buf, iov[i].buf_len); + } + else { + bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len); + } if (bytes_read < 0) { return convert_errno(-bytes_read); } @@ -609,18 +725,31 @@ os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, struct zephyr_fs_desc *ptr = NULL; ssize_t total_written = 0; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); - // Seek to the offset - int rc = fs_seek(&ptr->file, offset, FS_SEEK_SET); + int rc; + if (handle->is_posix_fd) { + rc = lseek(handle->fd, offset, SEEK_SET); + } + else { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + rc = fs_seek(&ptr->file, offset, FS_SEEK_SET); + } + if (rc < 0) { return convert_errno(-rc); } // Write data from each buffer for (int i = 0; i < iovcnt; i++) { - ssize_t bytes_written = - fs_write(&ptr->file, iov[i].buf, iov[i].buf_len); + ssize_t bytes_written; + if (handle->is_posix_fd) { + bytes_written = write(handle->fd,iov[i].buf,iov[i].buf_len); + } + else { + bytes_written = + fs_write(&ptr->file, iov[i].buf, iov[i].buf_len); + } + if (bytes_written < 0) { return convert_errno(-bytes_written); } @@ -645,11 +774,19 @@ os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, struct zephyr_fs_desc *ptr = NULL; ssize_t total_read = 0; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + if (!handle->is_posix_fd) { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + } // Read data into each buffer for (int i = 0; i < iovcnt; i++) { - ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len); + ssize_t bytes_read; + if(handle->is_posix_fd) { + bytes_read = read(handle->fd, iov[i].buf, iov[i].buf_len); + } + else { + bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len); + } if (bytes_read < 0) { // If an error occurred, return it return convert_errno(-bytes_read); @@ -690,12 +827,20 @@ os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, } struct zephyr_fs_desc *ptr = NULL; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + if (!handle->is_posix_fd) { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + } // Write data from each buffer for (int i = 0; i < iovcnt; i++) { - ssize_t bytes_written = - fs_write(&ptr->file, iov[i].buf, iov[i].buf_len); + ssize_t bytes_written; + if (handle->is_posix_fd) { + bytes_written = write(handle->fd, iov[i].buf, iov[i].buf_len); + } + else { + bytes_written = + fs_write(&ptr->file, iov[i].buf, iov[i].buf_len); + } if (bytes_written < 0) { return convert_errno(-bytes_written); } @@ -728,10 +873,15 @@ os_ftruncate(os_file_handle handle, __wasi_filesize_t size) return __WASI_EINVAL; } - struct zephyr_fs_desc *ptr = NULL; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); - - int rc = fs_truncate(&ptr->file, (off_t)size); + int rc; + if (handle->is_posix_fd) { + rc = ftruncate(handle->fd, (off_t)size); + } + else { + struct zephyr_fs_desc *ptr = NULL; + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + rc = fs_truncate(&ptr->file, (off_t)size); + } if (rc < 0) { return convert_errno(-rc); } @@ -805,6 +955,7 @@ os_mkdirat(os_file_handle handle, const char *path) handle->fd = index; handle->is_sock = false; + handle->is_posix_fd = false; return __WASI_ESUCCESS; } @@ -910,24 +1061,45 @@ os_lseek(os_file_handle handle, __wasi_filedelta_t offset, struct zephyr_fs_desc *ptr = NULL; int zwhence; - GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + off_t rc; + if(handle->is_posix_fd) { + switch (whence) { + case __WASI_WHENCE_SET: + zwhence = SEEK_SET; + break; + case __WASI_WHENCE_CUR: + zwhence = SEEK_CUR; + break; + case __WASI_WHENCE_END: + zwhence = SEEK_END; + break; + default: + return __WASI_EINVAL; + } - // They have the same value but this is more explicit - switch (whence) { - case __WASI_WHENCE_SET: - zwhence = FS_SEEK_SET; - break; - case __WASI_WHENCE_CUR: - zwhence = FS_SEEK_CUR; - break; - case __WASI_WHENCE_END: - zwhence = FS_SEEK_END; - break; - default: - return __WASI_EINVAL; + rc = lseek(handle->fd, (off_t)offset, zwhence); + } + else { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // They have the same value but this is more explicit + switch (whence) { + case __WASI_WHENCE_SET: + zwhence = FS_SEEK_SET; + break; + case __WASI_WHENCE_CUR: + zwhence = FS_SEEK_CUR; + break; + case __WASI_WHENCE_END: + zwhence = FS_SEEK_END; + break; + default: + return __WASI_EINVAL; + } + + rc = fs_seek(&ptr->file, (off_t)offset, zwhence); } - off_t rc = fs_seek(&ptr->file, (off_t)offset, zwhence); if (rc < 0) { return convert_errno(-rc); } @@ -963,6 +1135,7 @@ os_convert_stdin_handle(os_raw_file_handle raw_stdin) handle->fd = STDIN_FILENO; handle->is_sock = false; + handle->is_posix_fd = false; return handle; } @@ -975,6 +1148,7 @@ os_convert_stdout_handle(os_raw_file_handle raw_stdout) handle->fd = STDOUT_FILENO; handle->is_sock = false; + handle->is_posix_fd = false; return handle; } @@ -987,6 +1161,7 @@ os_convert_stderr_handle(os_raw_file_handle raw_stderr) handle->fd = STDERR_FILENO; handle->is_sock = false; + handle->is_posix_fd = false; return handle; } @@ -1176,7 +1351,9 @@ os_realpath(const char *path, char *resolved_path) bool os_compare_file_handle(os_file_handle handle1, os_file_handle handle2) { - return handle1->fd == handle2->fd && handle1->is_sock == handle2->is_sock; + return handle1->fd == handle2->fd && + handle1->is_sock == handle2->is_sock && + handle1->is_posix_fd == handle2->is_posix_fd; } bool @@ -1195,4 +1372,4 @@ bool os_is_stderr_handle(os_file_handle handle) { return (handle == (os_file_handle)stderr); -} \ No newline at end of file +}