// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include "iosyscalls-internal.hpp" #include #include #include // for va_* #include // for PATH_MAX #include // for strlen #include #include "syscalls_real.hpp" #include "debug.hpp" namespace { namespace real { __REAL_DECL(fprintf); __REAL_DECL(fwrite); __REAL_DECL(fread); __REAL_DECL(fopen); __REAL_DECL(fopen64); __REAL_DECL(fclose); __REAL_DECL(fputc); __REAL_DECL(fputs); __REAL_DECL(putc); __REAL_DECL(fgetc); __REAL_DECL(fgets); __REAL_DECL(getc); __REAL_DECL(freopen); __REAL_DECL(fdopen); __REAL_DECL(fseek); __REAL_DECL(ftell); __REAL_DECL(fgetpos); __REAL_DECL(fgetpos64); __REAL_DECL(fsetpos); __REAL_DECL(fsetpos64); __REAL_DECL(feof); __REAL_DECL(rewind); __REAL_DECL(fileno); __REAL_DECL(ferror); __REAL_DECL(fflush); __REAL_DECL(remove); __REAL_DECL(rename); } // namespace real void __attribute__((constructor)) _lib_stdio_initialize() { __REAL_DLSYM(fprintf); __REAL_DLSYM(fwrite); __REAL_DLSYM(fread); __REAL_DLSYM(fopen); __REAL_DLSYM(fopen64); __REAL_DLSYM(fclose); __REAL_DLSYM(fputc); __REAL_DLSYM(fputs); __REAL_DLSYM(putc); __REAL_DLSYM(fgetc); __REAL_DLSYM(fgets); __REAL_DLSYM(getc); __REAL_DLSYM(freopen); __REAL_DLSYM(fdopen); __REAL_DLSYM(fseek); __REAL_DLSYM(ftell); __REAL_DLSYM(fgetpos); __REAL_DLSYM(fgetpos64); __REAL_DLSYM(fsetpos); __REAL_DLSYM(fsetpos64); __REAL_DLSYM(feof); __REAL_DLSYM(rewind); __REAL_DLSYM(fileno); __REAL_DLSYM(ferror); __REAL_DLSYM(fflush); __REAL_DLSYM(remove); __REAL_DLSYM(rename); if (!(real::fprintf && real::fwrite && real::fread && real::fopen && real::fopen64 && real::fclose && real::fputc && real::fputs && real::putc && real::fgetc && real::fgets && real::getc && real::freopen && real::fdopen && real::fseek && real::ftell && real::fgetpos && real::fgetpos64 && real::fsetpos && real::fsetpos64 && real::feof && real::rewind && real::fileno && real::ferror && real::fflush && real::remove && real::rename)) { abort(); } } auto fopen_to_open_flags(std::string_view flags) { int ret = 0; if (!flags.compare("r")) { ret = O_RDONLY; } else if (!flags.compare("w")) { ret = O_WRONLY | O_CREAT | O_TRUNC; } else if (!flags.compare("a")) { ret = O_WRONLY | O_CREAT | O_APPEND; } else if (!flags.compare("r+")) { ret = O_RDWR; } else if (!flags.compare("w+")) { ret = O_RDWR | O_CREAT | O_TRUNC; } else if (!flags.compare("a+")) { ret = O_RDWR | O_CREAT | O_APPEND; } return ret; } } // namespace extern "C" { namespace vfs = vfsn::linux::internal; using FILEX = vfs::FILEX; using fs = purefs::fs::filesystem; FILE *_iosys_fopen(const char *pathname, const char *mode) { FILE *ret{}; if (vfs::redirect_to_image(pathname)) { TRACE_SYSCALLN("(%s,%s) -> VFS", pathname, mode); const auto fd = vfs::invoke_fs(&fs::open, pathname, fopen_to_open_flags(mode), 0644); if (fd >= 0) { ret = reinterpret_cast(vfs::allocate_filex(fd)); } } else { char tmp[PATH_MAX]; const auto path = vfs::npath_translate(pathname, tmp); TRACE_SYSCALLN("(%s,%s) -> (%s) linux fs", pathname, mode, path); ret = real::fopen(path, mode); } TRACE_SYSCALLN("(%s,%s)=%p", pathname, mode, ret); return ret; } __asm__(".symver _iosys_fopen,fopen@GLIBC_2.2.5"); FILE *_iosys_fopen64(const char *pathname, const char *mode) { FILE *ret{}; if (vfs::redirect_to_image(pathname)) { TRACE_SYSCALLN("(%s,%s) -> fopen()", pathname, mode); return fopen(pathname, mode); } else { char tmp[PATH_MAX]; const auto path = vfs::npath_translate(pathname, tmp); TRACE_SYSCALLN("(%s,%s) -> (%s) linux fs", pathname, mode, path); ret = real::fopen64(path, mode); } TRACE_SYSCALLN("(%s,%s)=%p", pathname, mode, ret); return ret; } __asm__(".symver _iosys_fopen64,fopen64@GLIBC_2.2.5"); int _iosys_fclose(FILE *__stream) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); auto ret = vfs::invoke_fs(&fs::close, fx->fd); if (!ret) { vfs::remove_filex(fx); } else { fx->error = errno; } return ret; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fclose(__stream); } } __asm__(".symver _iosys_fclose,fclose@GLIBC_2.2.5"); FILE *_iosys_fdopen(int __fd, const char *__modes) __THROW { if (vfs::is_image_fd(__fd)) { TRACE_SYSCALLN("(%d) -> VFS", __fd); std::cerr << "Unimplemented syscall " << __PRETTY_FUNCTION__ << std::endl; errno = ENOTSUP; return nullptr; } else { TRACE_SYSCALLN("(%d) -> linux fs", __fd); return real::fdopen(__fd, __modes); } } __asm__(".symver _iosys_fdopen,fdopen@GLIBC_2.2.5"); int _iosys_feof(FILE *__stream) __THROW { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); do { const auto curr = vfs::invoke_fs(&fs::seek, fx->fd, 0, SEEK_CUR); if (curr < 0) { ret = curr; break; } const auto ends = vfs::invoke_fs(&fs::seek, fx->fd, 0, SEEK_END); if (ends < 0) { ret = ends; break; } const auto restored = vfs::invoke_fs(&fs::seek, fx->fd, curr, SEEK_SET); if (restored < 0) { ret = restored; break; } ret = curr >= ends; } while (0); } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::feof(__stream); } TRACE_SYSCALLN("(%p)=%i", __stream, ret); return ret; } __asm__(".symver _iosys_feof,feof@GLIBC_2.2.5"); int _iosys_ferror(FILE *stream) __THROW { if (vfs::is_filex(stream)) { TRACE_SYSCALLN("(%p) -> VFS", stream); auto fx = reinterpret_cast(stream); return fx->error; } else { TRACE_SYSCALLN("(%p) -> linux fs", stream); return real::ferror(stream); } } __asm__(".symver _iosys_ferror,ferror@GLIBC_2.2.5"); int _iosys_fflush(FILE *__stream) { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); ret = vfs::invoke_fs(&fs::fsync, fx->fd); fx->error = errno; } else { if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::fflush(__stream); } if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%p)=%i", __stream, ret); return ret; } __asm__(".symver _iosys_fflush,fflush@GLIBC_2.2.5"); int _iosys_fgetc(FILE *__stream) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); char ch; auto ret = vfs::invoke_fs(&fs::read, fx->fd, &ch, 1); fx->error = errno; return ret; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fgetc(__stream); } } __asm__(".symver _iosys_fgetc,fgetc@GLIBC_2.2.5"); int _iosys_fgetpos(FILE *__restrict __stream, fpos_t *__restrict __pos) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); auto ret = vfs::invoke_fs(&fs::seek, fx->fd, 0, SEEK_CUR); fx->error = errno; if (__pos) { __pos->__pos = ret; } return (ret >= 0) ? (0) : (-1); } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fgetpos(__stream, __pos); } } __asm__(".symver _iosys_fgetpos,fgetpos@GLIBC_2.2.5"); int _iosys_fgetpos64(FILE *__restrict __stream, fpos64_t *__restrict __pos) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); auto ret = vfs::invoke_fs(&fs::seek, fx->fd, 0, SEEK_CUR); fx->error = errno; if (__pos) { __pos->__pos = ret; } return (ret >= 0) ? (0) : (-1); } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fgetpos64(__stream, __pos); } } __asm__(".symver _iosys_fgetpos64,fgetpos64@GLIBC_2.2.5"); char *_iosys_fgets(char *__restrict __s, int __n, FILE *__restrict __stream) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); char ch; size_t pos = 0; do { auto ret = vfs::invoke_fs(&fs::read, fx->fd, &ch, 1); if (ret == 0) { fx->error = 0; return nullptr; } else if (ret < 0) { fx->error = errno; return nullptr; } else { __s[pos++] = ch; } } while (ch == '\n'); return __s; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fgets(__s, __n, __stream); } } __asm__(".symver _iosys_fgets,fgets@GLIBC_2.2.5"); int _iosys_fileno(FILE *__stream) __THROW { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); ret = vfs::to_native_fd(reinterpret_cast(__stream)->fd); } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::fileno(__stream); } TRACE_SYSCALLN("(%p)=%i", __stream, ret); return ret; } __asm__(".symver _iosys_fileno,fileno@GLIBC_2.2.5"); int _iosys_fprintf(FILE *__restrict __stream, const char *__restrict __format, ...) { constexpr auto buf_len = 4096; int iCount; size_t xResult; char *pcBuffer; va_list xArgs; if (!vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> linux fs", __stream); va_list arglist; va_start(arglist, __format); auto ret = vfprintf(__stream, __format, arglist); va_end(arglist); return ret; } TRACE_SYSCALLN("(%p) -> VFS", __stream); pcBuffer = new char[buf_len]; auto fx = reinterpret_cast(__stream); if (pcBuffer == NULL) { /* Store the errno to thread local storage. */ fx->error = ENOMEM; iCount = -1; } else { va_start(xArgs, __format); iCount = vsnprintf(pcBuffer, buf_len, __format, xArgs); va_end(xArgs); /* ff_fwrite() will set ff_errno. */ if (iCount > 0) { xResult = vfs::invoke_fs(&fs::write, fx->fd, pcBuffer, iCount); if (xResult < (size_t)iCount) { iCount = -1; } } delete[] pcBuffer; } fx->error = errno; return iCount; } __asm__(".symver _iosys_fprintf,fprintf@GLIBC_2.2.5"); int _iosys_fputc(int __c, FILE *__stream) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p,%d) -> VFS", __stream, __c); auto fx = reinterpret_cast(__stream); char ch = __c; int ret = vfs::invoke_fs(&fs::write, fx->fd, &ch, sizeof ch); fx->error = errno; return ret == 1 ? 0 : ret; } else { if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%p,%d) -> linux fs", __stream, __c); return real::fputc(__c, __stream); } } __asm__(".symver _iosys_fputc,fputc@GLIBC_2.2.5"); int _iosys_fputs(const char *__restrict __s, FILE *__restrict __stream) { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p,%p) -> VFS", __s, __stream); auto fx = reinterpret_cast(__stream); const auto len = strlen(__s); ret = vfs::invoke_fs(&fs::write, fx->fd, __s, len); fx->error = errno; ret = ret == int(len) ? 0 : -1; } else { if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::fputs(__s, __stream); } if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%s, %p)=%i", __s, __stream, ret); return ret; } __asm__(".symver _iosys_fputs,fputs@GLIBC_2.2.5"); size_t _iosys_fread(void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream) { size_t ret{}; if (__size != 0 && __n != 0) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); char *p = reinterpret_cast(__ptr); bool eof = false; auto size = __size * __n; assert(__size == (size / __n)); while (size && !eof) { size_t readsize = (size > 8192) ? 8192 : size; auto res = vfs::invoke_fs(&fs::read, fx->fd, p, readsize); fx->error = errno; if (res < 0) break; if (size_t(res) < readsize) eof = true; p += res; ret += res; size -= res; } } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::fread(__ptr, __size, __n, __stream); } } TRACE_SYSCALLN("(%p, %lu, %lu, %p)=%i", __ptr, __size, __n, __stream, ret); return ret; } __asm__(".symver _iosys_fread,fread@GLIBC_2.2.5"); FILE *_iosys_freopen(const char *__restrict __filename, const char *__restrict __modes, FILE *__restrict __stream) { if (vfs::is_filex(__filename)) { TRACE_SYSCALLN("(%s,%s) -> VFS", __filename, __modes); if (fclose(__stream) < 0) { return nullptr; } return fopen(__filename, __modes); } else { TRACE_SYSCALLN("(%s,%s) -> linux fs", __filename, __modes); return real::freopen(__filename, __modes, __stream); } } __asm__(".symver _iosys_freopen,freopen@GLIBC_2.2.5"); int _iosys_fseek(FILE *__stream, long int __off, int __whence) { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); ret = vfs::invoke_fs(&fs::seek, fx->fd, __off, __whence); ret = (ret > 0) ? 0 : ret; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::fseek(__stream, __off, __whence); } TRACE_SYSCALLN("(%p, %li, %i)=%i", __stream, __off, __whence, ret); return ret; } __asm__(".symver _iosys_fseek,fseek@GLIBC_2.2.5"); int _iosys_fsetpos(FILE *__stream, const fpos_t *__pos) { TRACE_SYSCALL(); if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); auto ret = vfs::invoke_fs(&fs::seek, fx->fd, __pos->__pos, SEEK_SET); return (ret > 0) ? 0 : ret; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fsetpos(__stream, __pos); } } __asm__(".symver _iosys_fsetpos,fsetpos@GLIBC_2.2.5"); int _iosys_fsetpos64(FILE *__stream, const fpos64_t *__pos) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); auto ret = vfs::invoke_fs(&fs::seek, fx->fd, __pos->__pos, SEEK_SET); return (ret > 0) ? 0 : ret; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); return real::fsetpos64(__stream, __pos); } } __asm__(".symver _iosys_fsetpos64,fsetpos64@GLIBC_2.2.5"); long int _iosys_ftell(FILE *__stream) { long int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); ret = vfs::invoke_fs(&fs::seek, fx->fd, 0, SEEK_CUR); } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::ftell(__stream); } TRACE_SYSCALLN("(%p)=%i", __stream, ret); return ret; } __asm__(".symver _iosys_ftell,ftell@GLIBC_2.2.5"); size_t _iosys_fwrite(const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s) { int ret{}; if (__size != 0 && __n != 0) { if (vfs::is_filex(__s)) { TRACE_SYSCALLN("(%p) -> VFS", __s); auto fx = reinterpret_cast(__s); const char *p = reinterpret_cast(__ptr); bool eos = false; auto size = __size * __n; assert(__size == (size / __n)); while (size && !eos) { size_t wrsize = (size > 8192) ? 8192 : size; auto res = vfs::invoke_fs(&fs::write, fx->fd, p, wrsize); fx->error = errno; if (res < 0) break; if (size_t(res) < wrsize) eos = true; p += res; ret += res; size -= res; } } else { if (__s != stdout && __s != stderr) TRACE_SYSCALLN("(%p) -> linux fs", __s); ret = real::fwrite(__ptr, __size, __n, __s); } } if (__s != stdout && __s != stderr) TRACE_SYSCALLN("(ptr: %p,size: %lu, n: %lu, fil: %p)=%i", __ptr, __size, __n, __s, ret); return ret; } __asm__(".symver _iosys_fwrite,fwrite@GLIBC_2.2.5"); int _iosys_getc(FILE *__stream) { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); char ch; ret = vfs::invoke_fs(&fs::read, fx->fd, &ch, 1); fx->error = errno; ret = (ret == 1) ? (ch) : (ret); } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::getc(__stream); } TRACE_SYSCALLN("(%p)=%i", __stream, ret); return ret; } __asm__(".symver _iosys_getc,getc@GLIBC_2.2.5"); int _iosys_putc(int __c, FILE *__stream) { int ret{}; if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); char ch = __c; ret = vfs::invoke_fs(&fs::write, fx->fd, &ch, sizeof ch); fx->error = errno; ret = (ret == 1) ? (__c) : (EOF); } else { if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%p) -> linux fs", __stream); ret = real::putc(__c, __stream); } if (__stream != stdout && __stream != stderr) TRACE_SYSCALLN("(%i %p)=%i", __c, __stream, ret); return ret; } __asm__(".symver _iosys_putc,putc@GLIBC_2.2.5"); int _iosys_remove(const char *__filename) __THROW { int ret{}; if (vfs::redirect_to_image(__filename)) { TRACE_SYSCALLN("(%s) -> VFS", __filename); ret = vfs::invoke_fs(&fs::unlink, __filename); } else { char tmp[PATH_MAX]; const auto npath = vfs::npath_translate(__filename, tmp); TRACE_SYSCALLN("(%s) -> (%s) linux fs", __filename, npath); ret = real::remove(npath); } TRACE_SYSCALLN("(%s)=%i", __filename, ret); return ret; } __asm__(".symver _iosys_remove,remove@GLIBC_2.2.5"); int _iosys_rename(const char *oldpath, const char *newpath) { TRACE_SYSCALL(); if (vfs::redirect_to_image(oldpath)) { TRACE_SYSCALLN("(%s,%s) -> VFS", oldpath, newpath); return vfs::invoke_fs(&fs::rename, oldpath, newpath); } else { char tmp[PATH_MAX], tmp2[PATH_MAX]; const auto oldp = vfs::npath_translate(oldpath, tmp); const auto newp = vfs::npath_translate(newpath, tmp2); TRACE_SYSCALLN("(%s,%s) -> (%s,%s) linux fs", oldpath, newpath, oldp, newp); return real::rename(oldp, newp); } } __asm__(".symver _iosys_rename,rename@GLIBC_2.2.5"); void _iosys_rewind(FILE *__stream) { if (vfs::is_filex(__stream)) { TRACE_SYSCALLN("(%p) -> VFS", __stream); auto fx = reinterpret_cast(__stream); vfs::invoke_fs(&fs::seek, fx->fd, 0, SEEK_SET); fx->error = errno; } else { TRACE_SYSCALLN("(%p) -> linux fs", __stream); real::rewind(__stream); } } __asm__(".symver _iosys_rewind,rewind@GLIBC_2.2.5"); void _iosys_setbuf(FILE *__restrict, char *__restrict) __THROW { TRACE_SYSCALL(); errno = ENOTSUP; } __asm__(".symver _iosys_setbuf,setbuf@GLIBC_2.2.5"); int _iosys_setvbuf(FILE *__restrict __stream, char *__restrict __buf, int __modes, size_t __n) __THROW { TRACE_SYSCALL(); errno = ENOTSUP; return 0; } __asm__(".symver _iosys_setvbuf,setvbuf@GLIBC_2.2.5"); void _iosys_setbuffer(FILE *__restrict __stream, char *__restrict __buf, size_t __size) __THROW { TRACE_SYSCALL(); errno = ENOTSUP; } __asm__(".symver _iosys_setbuffer,setbuffer@GLIBC_2.2.5"); /* Make STREAM line-buffered. */ void _iosys_setlinebuf(FILE *__stream) __THROW { TRACE_SYSCALL(); errno = ENOTSUP; } __asm__(".symver _iosys_setlinebuf,setlinebuf@GLIBC_2.2.5"); }