From 4771afbc5d6e6326b250af6f4b0a7465df2480d5 Mon Sep 17 00:00:00 2001 From: Bill Holmes Date: Thu, 22 Jul 2021 10:54:23 -0400 Subject: [PATCH] Changes to fork mono-native code to our repo from corefx Copy over the pal_io files to our bugfix location and update the Makefile to use the forked files. --- external/corefx-bugfix/.gitignore | 280 +++ .../src/Native/Unix/System.Native/pal_io.c | 1494 +++++++++++++++++ .../src/Native/Unix/System.Native/pal_io.h | 783 +++++++++ mono/native/Makefile.am | 12 +- 4 files changed, 2563 insertions(+), 6 deletions(-) create mode 100644 external/corefx-bugfix/.gitignore create mode 100644 external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c create mode 100644 external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h diff --git a/external/corefx-bugfix/.gitignore b/external/corefx-bugfix/.gitignore new file mode 100644 index 000000000000..d95d659d0a5d --- /dev/null +++ b/external/corefx-bugfix/.gitignore @@ -0,0 +1,280 @@ +syntax: glob + +### VisualStudio ### + +# Tool Runtime Dir +/[Tt]ools/ + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Build results +.idea/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ +msbuild.log +msbuild.err +msbuild.wrn +msbuild.binlog +.deps/ +.dirstamp +.libs/ +*.lo +*.o + +# Cross building rootfs +cross/rootfs/ +cross/android-rootfs/ +# add x86 as it is ignored in 'Build results' +!cross/x86 + +# Visual Studio 2015 +.vs/ + +# Visual Studio 2015 Pre-CTP6 +*.sln.ide +*.ide/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +*.pubxml +*.publishproj + +# NuGet Packages +*.nuget.props +*.nuget.targets +*.nupkg +**/packages/* + +# NuGet package restore lockfiles +project.lock.json + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +*.metaproj +*.metaproj.tmp +bin.localpkg/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +### MonoDevelop ### + +*.pidb +*.userprefs + +### Windows ### + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Linux ### + +*~ + +# KDE directory preferences +.directory + +### OSX ### + +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# vim temporary files +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + +# Visual Studio Code +.vscode/ + +# Private test configuration and binaries. +config.ps1 +**/IISApplications diff --git a/external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c b/external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c new file mode 100644 index 000000000000..ddd56b91c93d --- /dev/null +++ b/external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c @@ -0,0 +1,1494 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "pal_compiler.h" +#include "pal_config.h" +#include "pal_errno.h" +#include "pal_io.h" +#include "pal_utilities.h" +#include "pal_safecrt.h" +#include "pal_types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_FCOPYFILE +#include +#elif HAVE_SENDFILE_4 +#include +#endif +#if HAVE_INOTIFY +#include +#endif + +#ifdef _AIX +#include +// Somehow, AIX mangles the definition for this behind a C++ def +// Redeclare it here +extern int getpeereid(int, uid_t *__restrict__, gid_t *__restrict__); +// This function declaration is hidden behind `_XOPEN_SOURCE=700`, but we need +// `_ALL_SOURCE` to build the runtime, and that resets that definition to 600. +// Instead of trying to wrangle ifdefs in system headers with more definitions, +// just declare it here. +extern ssize_t getline(char **, size_t *, FILE *); +#endif + +#if HAVE_STAT64 +#define stat_ stat64 +#define fstat_ fstat64 +#define lstat_ lstat64 +#else +#define stat_ stat +#define fstat_ fstat +#define lstat_ lstat +#endif + +// These numeric values are specified by POSIX. +// Validate that our definitions match. +c_static_assert(PAL_S_IRWXU == S_IRWXU); +c_static_assert(PAL_S_IRUSR == S_IRUSR); +c_static_assert(PAL_S_IWUSR == S_IWUSR); +c_static_assert(PAL_S_IXUSR == S_IXUSR); +c_static_assert(PAL_S_IRWXG == S_IRWXG); +c_static_assert(PAL_S_IRGRP == S_IRGRP); +c_static_assert(PAL_S_IWGRP == S_IWGRP); +c_static_assert(PAL_S_IXGRP == S_IXGRP); +c_static_assert(PAL_S_IRWXO == S_IRWXO); +c_static_assert(PAL_S_IROTH == S_IROTH); +c_static_assert(PAL_S_IWOTH == S_IWOTH); +c_static_assert(PAL_S_IXOTH == S_IXOTH); +c_static_assert(PAL_S_ISUID == S_ISUID); +c_static_assert(PAL_S_ISGID == S_ISGID); + +// These numeric values are not specified by POSIX, but the values +// are common to our current targets. If these static asserts fail, +// ConvertFileStatus needs to be updated to twiddle mode bits +// accordingly. +c_static_assert(PAL_S_IFMT == S_IFMT); +c_static_assert(PAL_S_IFIFO == S_IFIFO); +c_static_assert(PAL_S_IFCHR == S_IFCHR); +c_static_assert(PAL_S_IFDIR == S_IFDIR); +c_static_assert(PAL_S_IFREG == S_IFREG); +c_static_assert(PAL_S_IFLNK == S_IFLNK); +c_static_assert(PAL_S_IFSOCK == S_IFSOCK); + +// Validate that our enum for inode types is the same as what is +// declared by the dirent.h header on the local system. +// (AIX doesn't have dirent d_type, so none of this there) +#if defined(DT_UNKNOWN) +c_static_assert(PAL_DT_UNKNOWN == DT_UNKNOWN); +c_static_assert(PAL_DT_FIFO == DT_FIFO); +c_static_assert(PAL_DT_CHR == DT_CHR); +c_static_assert(PAL_DT_DIR == DT_DIR); +c_static_assert(PAL_DT_BLK == DT_BLK); +c_static_assert(PAL_DT_REG == DT_REG); +c_static_assert(PAL_DT_LNK == DT_LNK); +c_static_assert(PAL_DT_SOCK == DT_SOCK); +c_static_assert(PAL_DT_WHT == DT_WHT); +#endif + +// Validate that our Lock enum value are correct for the platform +c_static_assert(PAL_LOCK_SH == LOCK_SH); +c_static_assert(PAL_LOCK_EX == LOCK_EX); +c_static_assert(PAL_LOCK_NB == LOCK_NB); +c_static_assert(PAL_LOCK_UN == LOCK_UN); + +// Validate our AccessMode enum values are correct for the platform +c_static_assert(PAL_F_OK == F_OK); +c_static_assert(PAL_X_OK == X_OK); +c_static_assert(PAL_W_OK == W_OK); +c_static_assert(PAL_R_OK == R_OK); + +// Validate our SeekWhence enum values are correct for the platform +c_static_assert(PAL_SEEK_SET == SEEK_SET); +c_static_assert(PAL_SEEK_CUR == SEEK_CUR); +c_static_assert(PAL_SEEK_END == SEEK_END); + +// Validate our NotifyEvents enum values are correct for the platform +#if HAVE_INOTIFY +c_static_assert(PAL_IN_ACCESS == IN_ACCESS); +c_static_assert(PAL_IN_MODIFY == IN_MODIFY); +c_static_assert(PAL_IN_ATTRIB == IN_ATTRIB); +c_static_assert(PAL_IN_MOVED_FROM == IN_MOVED_FROM); +c_static_assert(PAL_IN_MOVED_TO == IN_MOVED_TO); +c_static_assert(PAL_IN_CREATE == IN_CREATE); +c_static_assert(PAL_IN_DELETE == IN_DELETE); +c_static_assert(PAL_IN_Q_OVERFLOW == IN_Q_OVERFLOW); +c_static_assert(PAL_IN_IGNORED == IN_IGNORED); +c_static_assert(PAL_IN_ONLYDIR == IN_ONLYDIR); +c_static_assert(PAL_IN_DONT_FOLLOW == IN_DONT_FOLLOW); +#if HAVE_IN_EXCL_UNLINK +c_static_assert(PAL_IN_EXCL_UNLINK == IN_EXCL_UNLINK); +#endif // HAVE_IN_EXCL_UNLINK +c_static_assert(PAL_IN_ISDIR == IN_ISDIR); +#endif // HAVE_INOTIFY + +static void ConvertFileStatus(const struct stat_* src, struct FileStatus* dst) +{ + dst->Dev = (int64_t)src->st_dev; + dst->Ino = (int64_t)src->st_ino; + dst->Flags = FILESTATUS_FLAGS_NONE; + dst->Mode = (int32_t)src->st_mode; + dst->Uid = src->st_uid; + dst->Gid = src->st_gid; + dst->Size = src->st_size; + + dst->ATime = src->st_atime; + dst->MTime = src->st_mtime; + dst->CTime = src->st_ctime; + + dst->ATimeNsec = ST_ATIME_NSEC(src); + dst->MTimeNsec = ST_MTIME_NSEC(src); + dst->CTimeNsec = ST_CTIME_NSEC(src); + +#if HAVE_STAT_BIRTHTIME + dst->BirthTime = src->st_birthtimespec.tv_sec; + dst->BirthTimeNsec = src->st_birthtimespec.tv_nsec; + dst->Flags |= FILESTATUS_FLAGS_HAS_BIRTHTIME; +#else + // Linux path: until we use statx() instead + dst->BirthTime = 0; + dst->BirthTimeNsec = 0; +#endif + +#if defined(HAVE_STAT_FLAGS) && defined(UF_HIDDEN) + dst->UserFlags = ((src->st_flags & UF_HIDDEN) == UF_HIDDEN) ? PAL_UF_HIDDEN : 0; +#else + dst->UserFlags = 0; +#endif +} + +// CoreCLR expects the "2" suffixes on these: they should be cleaned up in our +// next coordinated System.Native changes +int32_t SystemNative_Stat2(const char* path, struct FileStatus* output) +{ + struct stat_ result; + int ret; + while ((ret = stat_(path, &result)) < 0 && errno == EINTR); + + if (ret == 0) + { + ConvertFileStatus(&result, output); + } + + return ret; +} + +int32_t SystemNative_FStat2(intptr_t fd, struct FileStatus* output) +{ + struct stat_ result; + int ret; + while ((ret = fstat_(ToFileDescriptor(fd), &result)) < 0 && errno == EINTR); + + if (ret == 0) + { + ConvertFileStatus(&result, output); + } + + return ret; +} + +int32_t SystemNative_LStat2(const char* path, struct FileStatus* output) +{ + struct stat_ result; + int ret = lstat_(path, &result); + + if (ret == 0) + { + ConvertFileStatus(&result, output); + } + + return ret; +} + +static int32_t ConvertOpenFlags(int32_t flags) +{ + int32_t ret; + switch (flags & PAL_O_ACCESS_MODE_MASK) + { + case PAL_O_RDONLY: + ret = O_RDONLY; + break; + case PAL_O_RDWR: + ret = O_RDWR; + break; + case PAL_O_WRONLY: + ret = O_WRONLY; + break; + default: + assert_msg(false, "Unknown Open access mode", (int)flags); + return -1; + } + + if (flags & ~(PAL_O_ACCESS_MODE_MASK | PAL_O_CLOEXEC | PAL_O_CREAT | PAL_O_EXCL | PAL_O_TRUNC | PAL_O_SYNC)) + { + assert_msg(false, "Unknown Open flag", (int)flags); + return -1; + } + +#if HAVE_O_CLOEXEC + if (flags & PAL_O_CLOEXEC) + ret |= O_CLOEXEC; +#endif + if (flags & PAL_O_CREAT) + ret |= O_CREAT; + if (flags & PAL_O_EXCL) + ret |= O_EXCL; + if (flags & PAL_O_TRUNC) + ret |= O_TRUNC; + if (flags & PAL_O_SYNC) + ret |= O_SYNC; + + assert(ret != -1); + return ret; +} + +intptr_t SystemNative_Open(const char* path, int32_t flags, int32_t mode) +{ +// these two ifdefs are for platforms where we dont have the open version of CLOEXEC and thus +// must simulate it by doing a fcntl with the SETFFD version after the open instead +#if !HAVE_O_CLOEXEC + int32_t old_flags = flags; +#endif + flags = ConvertOpenFlags(flags); + if (flags == -1) + { + errno = EINVAL; + return -1; + } + + int result; + while ((result = open(path, flags, (mode_t)mode)) < 0 && errno == EINTR); +#if !HAVE_O_CLOEXEC + if (old_flags & PAL_O_CLOEXEC) + { + fcntl(result, F_SETFD, FD_CLOEXEC); + } +#endif + return result; +} + +int32_t SystemNative_Close(intptr_t fd) +{ + return close(ToFileDescriptor(fd)); +} + +intptr_t SystemNative_Dup(intptr_t oldfd) +{ + int result; +#if HAVE_F_DUPFD_CLOEXEC + while ((result = fcntl(ToFileDescriptor(oldfd), F_DUPFD_CLOEXEC, 0)) < 0 && errno == EINTR); +#else + while ((result = fcntl(ToFileDescriptor(oldfd), F_DUPFD, 0)) < 0 && errno == EINTR); + // do CLOEXEC here too + fcntl(result, F_SETFD, FD_CLOEXEC); +#endif + return result; +} + +int32_t SystemNative_Unlink(const char* path) +{ + int32_t result; + while ((result = unlink(path)) < 0 && errno == EINTR); + return result; +} + +intptr_t SystemNative_ShmOpen(const char* name, int32_t flags, int32_t mode) +{ +#if HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP + flags = ConvertOpenFlags(flags); + if (flags == -1) + { + errno = EINVAL; + return -1; + } + + return shm_open(name, flags, (mode_t)mode); +#else + (void)name, (void)flags, (void)mode; + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_ShmUnlink(const char* name) +{ +#if HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP + int32_t result; + while ((result = shm_unlink(name)) < 0 && errno == EINTR); + return result; +#else + // Not supported on e.g. Android. Also, prevent a compiler error because name is unused + (void)name; + errno = ENOTSUP; + return -1; +#endif +} + +static void ConvertDirent(const struct dirent* entry, struct DirectoryEntry* outputEntry) +{ + // We use Marshal.PtrToStringAnsi on the managed side, which takes a pointer to + // the start of the unmanaged string. Give the caller back a pointer to the + // location of the start of the string that exists in their own byte buffer. + outputEntry->Name = entry->d_name; +#if !defined(DT_UNKNOWN) + // AIX has no d_type, and since we can't get the directory that goes with + // the filename from ReadDir, we can't stat the file. Return unknown and + // hope that managed code can properly stat the file. + outputEntry->InodeType = PAL_DT_UNKNOWN; +#else + outputEntry->InodeType = (int32_t)entry->d_type; +#endif + +#if HAVE_DIRENT_NAME_LEN + outputEntry->NameLength = entry->d_namlen; +#else + outputEntry->NameLength = -1; // sentinel value to mean we have to walk to find the first \0 +#endif +} + +#if HAVE_READDIR_R +// struct dirent typically contains 64-bit numbers (e.g. d_ino), so we align it at 8-byte. +static const size_t dirent_alignment = 8; +#endif + +int32_t SystemNative_GetReadDirRBufferSize(void) +{ +#if HAVE_READDIR_R + // dirent should be under 2k in size + assert(sizeof(struct dirent) < 2048); + // add some extra space so we can align the buffer to dirent. + return sizeof(struct dirent) + dirent_alignment - 1; +#else + return 0; +#endif +} + +// To reduce the number of string copies, the caller of this function is responsible to ensure the memory +// referenced by outputEntry remains valid until it is read. +// If the platform supports readdir_r, the caller provides a buffer into which the data is read. +// If the platform uses readdir, the caller must ensure no calls are made to readdir/closedir since those will invalidate +// the current dirent. We assume the platform supports concurrent readdir calls to different DIRs. +int32_t SystemNative_ReadDirR(DIR* dir, uint8_t* buffer, int32_t bufferSize, struct DirectoryEntry* outputEntry) +{ + assert(dir != NULL); + assert(outputEntry != NULL); + +#if HAVE_READDIR_R + assert(buffer != NULL); + + // align to dirent + struct dirent* entry = (struct dirent*)((size_t)(buffer + dirent_alignment - 1) & ~(dirent_alignment - 1)); + + // check there is dirent size available at entry + if ((buffer + bufferSize) < ((uint8_t*)entry + sizeof(struct dirent))) + { + assert(false && "Buffer size too small; use GetReadDirRBufferSize to get required buffer size"); + return ERANGE; + } + + struct dirent* result = NULL; +#ifdef _AIX + // AIX returns 0 on success, but bizarrely, it returns 9 for both error and + // end-of-directory. result is NULL for both cases. The API returns the + // same thing for EOD/error, so disambiguation between the two is nearly + // impossible without clobbering errno for yourself and seeing if the API + // changed it. See: + // https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.basetrf2/readdir_r.htm + + errno = 0; // create a success condition for the API to clobber + int error = readdir_r(dir, entry, &result); + + if (error == 9) + { + memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized + return errno == 0 ? -1 : errno; + } +#else + int error = readdir_r(dir, entry, &result); + + // positive error number returned -> failure + if (error != 0) + { + assert(error > 0); + memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized + return error; + } + + // 0 returned with null result -> end-of-stream + if (result == NULL) + { + memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized + return -1; // shim convention for end-of-stream + } +#endif + + // 0 returned with non-null result (guaranteed to be set to entry arg) -> success + assert(result == entry); +#else + (void)buffer; // unused + (void)bufferSize; // unused + errno = 0; + struct dirent* entry = readdir(dir); + + // 0 returned with null result -> end-of-stream + if (entry == NULL) + { + memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized + + // kernel set errno -> failure + if (errno != 0) + { + assert_err(errno == EBADF, "Invalid directory stream descriptor dir", errno); + return errno; + } + return -1; + } +#endif + ConvertDirent(entry, outputEntry); + return 0; +} + +DIR* SystemNative_OpenDir(const char* path) +{ + return opendir(path); +} + +int32_t SystemNative_CloseDir(DIR* dir) +{ + return closedir(dir); +} + +int32_t SystemNative_Pipe(int32_t pipeFds[2], int32_t flags) +{ + switch (flags) + { + case 0: + break; + case PAL_O_CLOEXEC: +#if HAVE_O_CLOEXEC + flags = O_CLOEXEC; +#endif + break; + default: + assert_msg(false, "Unknown pipe flag", (int)flags); + errno = EINVAL; + return -1; + } + + int32_t result; +#if HAVE_PIPE2 + // If pipe2 is available, use it. This will handle O_CLOEXEC if it was set. + while ((result = pipe2(pipeFds, flags)) < 0 && errno == EINTR); +#else + // Otherwise, use pipe. + while ((result = pipe(pipeFds)) < 0 && errno == EINTR); + + // Then, if O_CLOEXEC was specified, use fcntl to configure the file descriptors appropriately. +#if HAVE_O_CLOEXEC + if ((flags & O_CLOEXEC) != 0 && result == 0) +#else + if ((flags & PAL_O_CLOEXEC) != 0 && result == 0) +#endif + { + while ((result = fcntl(pipeFds[0], F_SETFD, FD_CLOEXEC)) < 0 && errno == EINTR); + if (result == 0) + { + while ((result = fcntl(pipeFds[1], F_SETFD, FD_CLOEXEC)) < 0 && errno == EINTR); + } + + if (result != 0) + { + int tmpErrno = errno; + close(pipeFds[0]); + close(pipeFds[1]); + errno = tmpErrno; + } + } +#endif + return result; +} + +int32_t SystemNative_FcntlSetCloseOnExec(intptr_t fd) +{ + int result; + while ((result = fcntl(ToFileDescriptor(fd), F_SETFD, FD_CLOEXEC)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_FcntlCanGetSetPipeSz(void) +{ +#if defined(F_GETPIPE_SZ) && defined(F_SETPIPE_SZ) + return true; +#else + return false; +#endif +} + +int32_t SystemNative_FcntlGetPipeSz(intptr_t fd) +{ +#ifdef F_GETPIPE_SZ + int32_t result; + while ((result = fcntl(ToFileDescriptor(fd), F_GETPIPE_SZ)) < 0 && errno == EINTR); + return result; +#else + (void)fd; + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_FcntlSetPipeSz(intptr_t fd, int32_t size) +{ +#ifdef F_SETPIPE_SZ + int32_t result; + while ((result = fcntl(ToFileDescriptor(fd), F_SETPIPE_SZ, size)) < 0 && errno == EINTR); + return result; +#else + (void)fd, (void)size; + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_FcntlSetIsNonBlocking(intptr_t fd, int32_t isNonBlocking) +{ + int fileDescriptor = ToFileDescriptor(fd); + + int flags = fcntl(fileDescriptor, F_GETFL); + if (flags == -1) + { + return -1; + } + + if (isNonBlocking == 0) + { + flags &= ~O_NONBLOCK; + } + else + { + flags |= O_NONBLOCK; + } + + return fcntl(fileDescriptor, F_SETFL, flags); +} + +int32_t SystemNative_MkDir(const char* path, int32_t mode) +{ + int32_t result; + while ((result = mkdir(path, (mode_t)mode)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_ChMod(const char* path, int32_t mode) +{ + int32_t result; + while ((result = chmod(path, (mode_t)mode)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_FChMod(intptr_t fd, int32_t mode) +{ + int32_t result; + while ((result = fchmod(ToFileDescriptor(fd), (mode_t)mode)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_FSync(intptr_t fd) +{ + int32_t result; + while ((result = fsync(ToFileDescriptor(fd))) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_FLock(intptr_t fd, int32_t operation) +{ + int32_t result; + while ((result = flock(ToFileDescriptor(fd), operation)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_ChDir(const char* path) +{ + int32_t result; + while ((result = chdir(path)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_Access(const char* path, int32_t mode) +{ + return access(path, mode); +} + +int32_t SystemNative_FnMatch(const char* pattern, const char* path, int32_t flags) +{ + return fnmatch(pattern, path, flags); +} + +int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, int32_t whence) +{ + int64_t result; + while (( + result = +#if HAVE_LSEEK64 + lseek64( +#else + lseek( +#endif + ToFileDescriptor(fd), + (off_t)offset, + whence)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_Link(const char* source, const char* linkTarget) +{ + int32_t result; + while ((result = link(source, linkTarget)) < 0 && errno == EINTR); + return result; +} + +intptr_t SystemNative_MksTemps(char* pathTemplate, int32_t suffixLength) +{ + intptr_t result; +#if HAVE_MKSTEMPS + while ((result = mkstemps(pathTemplate, suffixLength)) < 0 && errno == EINTR); +#elif HAVE_MKSTEMP + // mkstemps is not available bionic/Android, but mkstemp is + // mkstemp doesn't allow the suffix that msktemps does allow, so we'll need to + // remove that before passisng pathTemplate to mkstemp + + int32_t pathTemplateLength = (int32_t)strlen(pathTemplate); + + // pathTemplate must include at least XXXXXX (6 characters) which are not part of + // the suffix + if (suffixLength < 0 || suffixLength > pathTemplateLength - 6) + { + errno = EINVAL; + return -1; + } + + // Make mkstemp ignore the suffix by setting the first char of the suffix to \0, + // if there is a suffix + int32_t firstSuffixIndex = 0; + char firstSuffixChar = 0; + + if (suffixLength > 0) + { + firstSuffixIndex = pathTemplateLength - suffixLength; + firstSuffixChar = pathTemplate[firstSuffixIndex]; + pathTemplate[firstSuffixIndex] = 0; + } + + while ((result = mkstemp(pathTemplate)) < 0 && errno == EINTR); + + // Reset the first char of the suffix back to its original value, if there is a suffix + if (suffixLength > 0) + { + pathTemplate[firstSuffixIndex] = firstSuffixChar; + } +#else +#error "Cannot find mkstemps nor mkstemp on this platform" +#endif + return result; +} + +static int32_t ConvertMMapProtection(int32_t protection) +{ + if (protection == PAL_PROT_NONE) + return PROT_NONE; + + if (protection & ~(PAL_PROT_READ | PAL_PROT_WRITE | PAL_PROT_EXEC)) + { + assert_msg(false, "Unknown protection", (int)protection); + return -1; + } + + int32_t ret = 0; + if (protection & PAL_PROT_READ) + ret |= PROT_READ; + if (protection & PAL_PROT_WRITE) + ret |= PROT_WRITE; + if (protection & PAL_PROT_EXEC) + ret |= PROT_EXEC; + + assert(ret != -1); + return ret; +} + +static int32_t ConvertMMapFlags(int32_t flags) +{ + if (flags & ~(PAL_MAP_SHARED | PAL_MAP_PRIVATE | PAL_MAP_ANONYMOUS)) + { + assert_msg(false, "Unknown MMap flag", (int)flags); + return -1; + } + + int32_t ret = 0; + if (flags & PAL_MAP_PRIVATE) + ret |= MAP_PRIVATE; + if (flags & PAL_MAP_SHARED) + ret |= MAP_SHARED; + if (flags & PAL_MAP_ANONYMOUS) + ret |= MAP_ANON; + + assert(ret != -1); + return ret; +} + +static int32_t ConvertMSyncFlags(int32_t flags) +{ + if (flags & ~(PAL_MS_SYNC | PAL_MS_ASYNC | PAL_MS_INVALIDATE)) + { + assert_msg(false, "Unknown MSync flag", (int)flags); + return -1; + } + + int32_t ret = 0; + if (flags & PAL_MS_SYNC) + ret |= MS_SYNC; + if (flags & PAL_MS_ASYNC) + ret |= MS_ASYNC; + if (flags & PAL_MS_INVALIDATE) + ret |= MS_INVALIDATE; + + assert(ret != -1); + return ret; +} + +void* SystemNative_MMap(void* address, + uint64_t length, + int32_t protection, // bitwise OR of PAL_PROT_* + int32_t flags, // bitwise OR of PAL_MAP_*, but PRIVATE and SHARED are mutually exclusive. + intptr_t fd, + int64_t offset) +{ + if (length > SIZE_MAX) + { + errno = ERANGE; + return NULL; + } + + protection = ConvertMMapProtection(protection); + flags = ConvertMMapFlags(flags); + + if (flags == -1 || protection == -1) + { + errno = EINVAL; + return NULL; + } + + // Use ToFileDescriptorUnchecked to allow -1 to be passed for the file descriptor, since managed code explicitly uses -1 + void* ret = +#if HAVE_MMAP64 + mmap64( +#else + mmap( +#endif + address, + (size_t)length, + protection, + flags, + ToFileDescriptorUnchecked(fd), + (off_t)offset); + + if (ret == MAP_FAILED) + { + return NULL; + } + + assert(ret != NULL); + return ret; +} + +int32_t SystemNative_MUnmap(void* address, uint64_t length) +{ + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + return munmap(address, (size_t)length); +} + +int32_t SystemNative_MAdvise(void* address, uint64_t length, int32_t advice) +{ + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + switch (advice) + { + case PAL_MADV_DONTFORK: +#ifdef MADV_DONTFORK + return madvise(address, (size_t)length, MADV_DONTFORK); +#else + (void)address, (void)length, (void)advice; + errno = ENOTSUP; + return -1; +#endif + } + + assert_msg(false, "Unknown MemoryAdvice", (int)advice); + errno = EINVAL; + return -1; +} + +int32_t SystemNative_MLock(void* address, uint64_t length) +{ +#if !defined (__HAIKU__) + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + return mlock(address, (size_t)length); +#else + errno = ENOSYS; + return -1; +#endif +} + +int32_t SystemNative_MUnlock(void* address, uint64_t length) +{ +#if !defined (__HAIKU__) + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + return munlock(address, (size_t)length); +#else + errno = ENOSYS; + return -1; +#endif +} + +int32_t SystemNative_MProtect(void* address, uint64_t length, int32_t protection) +{ + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + protection = ConvertMMapProtection(protection); + if (protection == -1) + { + errno = EINVAL; + return -1; + } + + return mprotect(address, (size_t)length, protection); +} + +int32_t SystemNative_MSync(void* address, uint64_t length, int32_t flags) +{ + if (length > SIZE_MAX) + { + errno = ERANGE; + return -1; + } + + flags = ConvertMSyncFlags(flags); + if (flags == -1) + { + errno = EINVAL; + return -1; + } + + return msync(address, (size_t)length, flags); +} + +int64_t SystemNative_SysConf(int32_t name) +{ + switch (name) + { + case PAL_SC_CLK_TCK: + return sysconf(_SC_CLK_TCK); + case PAL_SC_PAGESIZE: + return sysconf(_SC_PAGESIZE); + } + + assert_msg(false, "Unknown SysConf name", (int)name); + errno = EINVAL; + return -1; +} + +int32_t SystemNative_FTruncate(intptr_t fd, int64_t length) +{ + int32_t result; + while (( + result = +#if HAVE_FTRUNCATE64 + ftruncate64( +#else + ftruncate( +#endif + ToFileDescriptor(fd), + (off_t)length)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_Poll(struct PollEvent* pollEvents, uint32_t eventCount, int32_t milliseconds, uint32_t* triggered) +{ + if (pollEvents == NULL || triggered == NULL) + { + return Error_EFAULT; + } + + if (milliseconds < -1) + { + return Error_EINVAL; + } + + size_t bufferSize; + if (!multiply_s(sizeof(struct pollfd), (size_t)eventCount, &bufferSize)) + { + return SystemNative_ConvertErrorPlatformToPal(EOVERFLOW); + } + + + int useStackBuffer = bufferSize <= 2048; + struct pollfd* pollfds = (struct pollfd*)(useStackBuffer ? alloca(bufferSize) : malloc(bufferSize)); + if (pollfds == NULL) + { + return Error_ENOMEM; + } + + for (uint32_t i = 0; i < eventCount; i++) + { + const struct PollEvent* event = &pollEvents[i]; + pollfds[i].fd = event->FileDescriptor; + // we need to do this for platforms like AIX where PAL_POLL* doesn't + // match up to their reality; this is PollEvent -> system polling + switch (event->Events) + { + case PAL_POLLIN: + pollfds[i].events = POLLIN; + break; + case PAL_POLLPRI: + pollfds[i].events = POLLPRI; + break; + case PAL_POLLOUT: + pollfds[i].events = POLLOUT; + break; + case PAL_POLLERR: + pollfds[i].events = POLLERR; + break; + case PAL_POLLHUP: + pollfds[i].events = POLLHUP; + break; + case PAL_POLLNVAL: + pollfds[i].events = POLLNVAL; + break; + default: + pollfds[i].events = event->Events; + break; + } + pollfds[i].revents = 0; + } + + int rv; + while ((rv = poll(pollfds, (nfds_t)eventCount, milliseconds)) < 0 && errno == EINTR); + + if (rv < 0) + { + if (!useStackBuffer) + { + free(pollfds); + } + + *triggered = 0; + return SystemNative_ConvertErrorPlatformToPal(errno); + } + + for (uint32_t i = 0; i < eventCount; i++) + { + const struct pollfd* pfd = &pollfds[i]; + assert(pfd->fd == pollEvents[i].FileDescriptor); + assert(pfd->events == pollEvents[i].Events); + + // same as the other switch, just system -> PollEvent + switch (pfd->revents) + { + case POLLIN: + pollEvents[i].TriggeredEvents = PAL_POLLIN; + break; + case POLLPRI: + pollEvents[i].TriggeredEvents = PAL_POLLPRI; + break; + case POLLOUT: + pollEvents[i].TriggeredEvents = PAL_POLLOUT; + break; + case POLLERR: + pollEvents[i].TriggeredEvents = PAL_POLLERR; + break; + case POLLHUP: + pollEvents[i].TriggeredEvents = PAL_POLLHUP; + break; + case POLLNVAL: + pollEvents[i].TriggeredEvents = PAL_POLLNVAL; + break; + default: + pollEvents[i].TriggeredEvents = (int16_t)pfd->revents; + break; + } + } + + *triggered = (uint32_t)rv; + + if (!useStackBuffer) + { + free(pollfds); + } + + return Error_SUCCESS; +} + +int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, int32_t advice) +{ +#if HAVE_POSIX_ADVISE + // POSIX_FADV_* may be different on each platform. Convert the values from PAL to the system's. + int32_t actualAdvice; + switch (advice) + { + case PAL_POSIX_FADV_NORMAL: actualAdvice = POSIX_FADV_NORMAL; break; + case PAL_POSIX_FADV_RANDOM: actualAdvice = POSIX_FADV_RANDOM; break; + case PAL_POSIX_FADV_SEQUENTIAL: actualAdvice = POSIX_FADV_SEQUENTIAL; break; + case PAL_POSIX_FADV_WILLNEED: actualAdvice = POSIX_FADV_WILLNEED; break; + case PAL_POSIX_FADV_DONTNEED: actualAdvice = POSIX_FADV_DONTNEED; break; + case PAL_POSIX_FADV_NOREUSE: actualAdvice = POSIX_FADV_NOREUSE; break; + default: return EINVAL; // According to the man page + } + int32_t result; + while (( + result = +#if HAVE_POSIX_FADVISE64 + posix_fadvise64( +#else + posix_fadvise( +#endif + ToFileDescriptor(fd), + (off_t)offset, + (off_t)length, + actualAdvice)) < 0 && errno == EINTR); + return result; +#else + // Not supported on this platform. Caller can ignore this failure since it's just a hint. + (void)fd, (void)offset, (void)length, (void)advice; + return ENOTSUP; +#endif +} + +#ifndef TARGET_ANDROID + +char* SystemNative_GetLine(FILE* stream) +{ + assert(stream != NULL); + + char* lineptr = NULL; + size_t n = 0; + ssize_t length = getline(&lineptr, &n, stream); + + return length >= 0 ? lineptr : NULL; +} + +#endif + +int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t bufferSize) +{ + assert(buffer != NULL || bufferSize == 0); + assert(bufferSize >= 0); + + if (bufferSize < 0) + { + errno = EINVAL; + return -1; + } + + ssize_t count; +#if !MONO + while ((count = read(ToFileDescriptor(fd), buffer, (uint32_t)bufferSize)) < 0 && errno == EINTR); +#else // The Mono thread abort process can cause an EINTR here that needs to be handled + count = read(ToFileDescriptor(fd), buffer, (uint32_t)bufferSize); +#endif + + assert(count >= -1 && count <= bufferSize); + return (int32_t)count; +} + +int32_t SystemNative_ReadLink(const char* path, char* buffer, int32_t bufferSize) +{ + assert(buffer != NULL || bufferSize == 0); + assert(bufferSize >= 0); + + if (bufferSize <= 0) + { + errno = EINVAL; + return -1; + } + + ssize_t count = readlink(path, buffer, (size_t)bufferSize); + assert(count >= -1 && count <= bufferSize); + + return (int32_t)count; +} + +int32_t SystemNative_Rename(const char* oldPath, const char* newPath) +{ + int32_t result; + while ((result = rename(oldPath, newPath)) < 0 && errno == EINTR); + return result; +} + +int32_t SystemNative_RmDir(const char* path) +{ + int32_t result; + while ((result = rmdir(path)) < 0 && errno == EINTR); + return result; +} + +void SystemNative_Sync(void) +{ + sync(); +} + +int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize) +{ + assert(buffer != NULL || bufferSize == 0); + assert(bufferSize >= 0); + + if (bufferSize < 0) + { + errno = ERANGE; + return -1; + } + + ssize_t count; + while ((count = write(ToFileDescriptor(fd), buffer, (uint32_t)bufferSize)) < 0 && errno == EINTR); + + assert(count >= -1 && count <= bufferSize); + return (int32_t)count; +} + +#if !HAVE_FCOPYFILE +// Read all data from inFd and write it to outFd +static int32_t CopyFile_ReadWrite(int inFd, int outFd) +{ + // Allocate a buffer + const int BufferLength = 80 * 1024 * sizeof(char); + char* buffer = (char*)malloc(BufferLength); + if (buffer == NULL) + { + return -1; + } + + // Repeatedly read from the source and write to the destination + while (true) + { + // Read up to what will fit in our buffer. We're done if we get back 0 bytes. + ssize_t bytesRead; + while ((bytesRead = read(inFd, buffer, BufferLength)) < 0 && errno == EINTR); + if (bytesRead == -1) + { + int tmp = errno; + free(buffer); + errno = tmp; + return -1; + } + if (bytesRead == 0) + { + break; + } + assert(bytesRead > 0); + + // Write what was read. + ssize_t offset = 0; + while (bytesRead > 0) + { + ssize_t bytesWritten; + while ((bytesWritten = write(outFd, buffer + offset, (size_t)bytesRead)) < 0 && errno == EINTR); + if (bytesWritten == -1) + { + int tmp = errno; + free(buffer); + errno = tmp; + return -1; + } + assert(bytesWritten >= 0); + bytesRead -= bytesWritten; + offset += bytesWritten; + } + } + + free(buffer); + return 0; +} +#endif // !HAVE_FCOPYFILE + +int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) +{ + int inFd = ToFileDescriptor(sourceFd); + int outFd = ToFileDescriptor(destinationFd); + +#if HAVE_FCOPYFILE + // If fcopyfile is available (OS X), try to use it, as the whole copy + // can be performed in the kernel, without lots of unnecessary copying. + // Copy data and metadata. + return fcopyfile(inFd, outFd, NULL, COPYFILE_ALL) == 0 ? 0 : -1; +#else + // Get the stats on the source file. + int ret; + struct stat_ sourceStat; + bool copied = false; + + // First, stat the source file. + while ((ret = fstat_(inFd, &sourceStat)) < 0 && errno == EINTR); + if (ret != 0) + { + // If we can't stat() it, then we likely don't have permission to read it. + return -1; + } + + // Copy permissions. This fchmod() needs to happen prior to writing anything into + // the file to avoid possibly leaking any private data. + while ((ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) < 0 && errno == EINTR); +#if !TARGET_ANDROID + // On Android, we are not allowed to modify permissions, but the copy should still succeed; + // see https://github.com/mono/mono/issues/17133 for details. + if (ret != 0) + { + return -1; + } +#endif + +#if HAVE_SENDFILE_4 + // If sendfile is available (Linux), try to use it, as the whole copy + // can be performed in the kernel, without lots of unnecessary copying. + + // On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a + // `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t. + // So `size' will have to be `uint64_t'. In all other cases, it will be `size_t'. + uint64_t size = (uint64_t)sourceStat.st_size; + + // Note that per man page for large files, you have to iterate until the + // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). + while (size > 0) + { + ssize_t sent = sendfile(outFd, inFd, NULL, (size >= SSIZE_MAX ? SSIZE_MAX : (size_t)size)); + if (sent < 0) + { + if (errno != EINVAL && errno != ENOSYS) + { + return -1; + } + else + { + break; + } + } + else + { + assert((size_t)sent <= size); + size -= (size_t)sent; + } + } + if (size == 0) + { + copied = true; + } + // sendfile couldn't be used; fall back to a manual copy below. This could happen + // if we're on an old kernel, for example, where sendfile could only be used + // with sockets and not regular files. +#endif // HAVE_SENDFILE_4 + + // Manually read all data from the source and write it to the destination. + if (!copied && CopyFile_ReadWrite(inFd, outFd) != 0) + { + return -1; + } + + // Now that the data from the file has been copied, copy over metadata + // from the source file. First copy the file times. + // If futimes nor futimes are available on this platform, file times will + // not be copied over. +#if HAVE_FUTIMENS + // futimens is prefered because it has a higher resolution. + struct timespec origTimes[2]; + origTimes[0].tv_sec = (time_t)sourceStat.st_atime; + origTimes[0].tv_nsec = ST_ATIME_NSEC(&sourceStat); + origTimes[1].tv_sec = (time_t)sourceStat.st_mtime; + origTimes[1].tv_nsec = ST_MTIME_NSEC(&sourceStat); + while ((ret = futimens(outFd, origTimes)) < 0 && errno == EINTR); +#elif HAVE_FUTIMES + struct timeval origTimes[2]; + origTimes[0].tv_sec = sourceStat.st_atime; + origTimes[0].tv_usec = ST_ATIME_NSEC(&sourceStat) / 1000; + origTimes[1].tv_sec = sourceStat.st_mtime; + origTimes[1].tv_usec = ST_MTIME_NSEC(&sourceStat) / 1000; + while ((ret = futimes(outFd, origTimes)) < 0 && errno == EINTR); +#endif + +#if !TARGET_ANDROID + // On Android, the copy should still succeed even if copying the file times didn't. + if (ret != 0) + { + return -1; + } +#endif + + return 0; +#endif // HAVE_FCOPYFILE +} + +intptr_t SystemNative_INotifyInit(void) +{ +#if HAVE_INOTIFY + return inotify_init(); +#else + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_INotifyAddWatch(intptr_t fd, const char* pathName, uint32_t mask) +{ + assert(fd >= 0); + assert(pathName != NULL); + +#if HAVE_INOTIFY +#if !HAVE_IN_EXCL_UNLINK + mask &= ~((uint32_t)PAL_IN_EXCL_UNLINK); +#endif + return inotify_add_watch(ToFileDescriptor(fd), pathName, mask); +#else + (void)fd, (void)pathName, (void)mask; + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_INotifyRemoveWatch(intptr_t fd, int32_t wd) +{ + assert(fd >= 0); + assert(wd >= 0); + +#if HAVE_INOTIFY + return inotify_rm_watch( + ToFileDescriptor(fd), +#if INOTIFY_RM_WATCH_WD_UNSIGNED + (uint32_t)wd); +#else + wd); +#endif +#else + (void)fd, (void)wd; + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid) +{ + int fd = ToFileDescriptor(socket); + + // ucred causes Emscripten to fail even though it's defined, + // but getting peer credentials won't work for WebAssembly anyway +#if defined(SO_PEERCRED) && !defined(_WASM_) + struct ucred creds; + socklen_t len = sizeof(creds); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &len) == 0) + { + *euid = creds.uid; + return 0; + } + return -1; +#elif HAVE_GETPEEREID + uid_t egid; + return getpeereid(fd, euid, &egid); +#else + (void)fd; + (void)*euid; + errno = ENOTSUP; + return -1; +#endif +} + +char* SystemNative_RealPath(const char* path) +{ + assert(path != NULL); + return realpath(path, NULL); +} + +int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType) +{ + if (offset < 0 || length < 0) + { + errno = EINVAL; + return -1; + } + +#if HAVE_FLOCK64 + struct flock64 lockArgs; +#else + struct flock lockArgs; +#endif + + lockArgs.l_type = lockType; + lockArgs.l_whence = SEEK_SET; + lockArgs.l_start = (off_t)offset; + lockArgs.l_len = (off_t)length; + + int32_t ret; + while ((ret = fcntl (ToFileDescriptor(fd), F_SETLK, &lockArgs)) < 0 && errno == EINTR); + return ret; +} + +int32_t SystemNative_LChflags(const char* path, uint32_t flags) +{ +#if HAVE_LCHFLAGS + int32_t result; + while ((result = lchflags(path, flags)) < 0 && errno == EINTR); + return result; +#else + (void)path, (void)flags; + errno = ENOTSUP; + return -1; +#endif +} + +int32_t SystemNative_LChflagsCanSetHiddenFlag(void) +{ +#if defined(UF_HIDDEN) && defined(HAVE_STAT_FLAGS) && defined(HAVE_LCHFLAGS) + return true; +#else + return false; +#endif +} + +int32_t SystemNative_Symlink(const char* target, const char* linkPath) +{ + return symlink(target, linkPath); +} diff --git a/external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h b/external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h new file mode 100644 index 000000000000..f13cdc660655 --- /dev/null +++ b/external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h @@ -0,0 +1,783 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma once + +#include "pal_compiler.h" + +BEGIN_EXTERN_C + +#include "pal_types.h" +#include "pal_errno.h" +#include +#include +#include +#include + +/** + * File status returned by Stat or FStat. + */ +struct FileStatus +{ + int32_t Flags; // flags for testing if some members are present (see FileStatusFlags) + int32_t Mode; // file mode (see S_I* constants above for bit values) + uint32_t Uid; // user ID of owner + uint32_t Gid; // group ID of owner + int64_t Size; // total size, in bytes + int64_t ATime; // time of last access + int64_t ATimeNsec; // nanosecond part + int64_t MTime; // time of last modification + int64_t MTimeNsec; // nanosecond part + int64_t CTime; // time of last status change + int64_t CTimeNsec; // nanosecond part + int64_t BirthTime; // time the file was created + int64_t BirthTimeNsec; // nanosecond part + int64_t Dev; // ID of the device containing the file + int64_t Ino; // inode number of the file + uint32_t UserFlags; // user defined flags +}; + +/* Provide consistent access to nanosecond fields, if they exist. */ +/* Seconds are always available through st_atime, st_mtime, st_ctime. */ + +#if HAVE_STAT_TIMESPEC + +#define ST_ATIME_NSEC(statstruct) ((statstruct)->st_atimespec.tv_nsec) +#define ST_MTIME_NSEC(statstruct) ((statstruct)->st_mtimespec.tv_nsec) +#define ST_CTIME_NSEC(statstruct) ((statstruct)->st_ctimespec.tv_nsec) + +#else /* HAVE_STAT_TIMESPEC */ + +#if HAVE_STAT_TIM + +#define ST_ATIME_NSEC(statstruct) ((statstruct)->st_atim.tv_nsec) +#define ST_MTIME_NSEC(statstruct) ((statstruct)->st_mtim.tv_nsec) +#define ST_CTIME_NSEC(statstruct) ((statstruct)->st_ctim.tv_nsec) + +#else /* HAVE_STAT_TIM */ + +#if HAVE_STAT_NSEC + +#define ST_ATIME_NSEC(statstruct) ((statstruct)->st_atimensec) +#define ST_MTIME_NSEC(statstruct) ((statstruct)->st_mtimensec) +#define ST_CTIME_NSEC(statstruct) ((statstruct)->st_ctimensec) + +#else /* HAVE_STAT_NSEC */ + +#define ST_ATIME_NSEC(statstruct) 0 +#define ST_MTIME_NSEC(statstruct) 0 +#define ST_CTIME_NSEC(statstruct) 0 + +#endif /* HAVE_STAT_NSEC */ +#endif /* HAVE_STAT_TIM */ +#endif /* HAVE_STAT_TIMESPEC */ + +/************ + * The values below in the header are fixed and correct for managed callers to use forever. + * We must never change them. The implementation must either static_assert that they are equal + * to the native equivalent OR convert them appropriately. + */ + +/** + * Constants for interpreting the permissions encoded in FileStatus.Mode. + * Both the names (without the PAL_ prefix and numeric values are specified by POSIX.1.2008 + */ +enum +{ + PAL_S_IRWXU = 00700, // Read, write, execute/search by owner. + PAL_S_IRUSR = 00400, // Read permission, owner. + PAL_S_IWUSR = 00200, // Write permission, owner. + PAL_S_IXUSR = 00100, // Execute/search permission, owner. + PAL_S_IRWXG = 00070, // Read, write, execute/search by group. + PAL_S_IRGRP = 00040, // Read permission, group. + PAL_S_IWGRP = 00020, // Write permission, group. + PAL_S_IXGRP = 00010, // Execute/search permission, group. + PAL_S_IRWXO = 00007, // Read, write, execute/search by others. + PAL_S_IROTH = 00004, // Read permission, others. + PAL_S_IWOTH = 00002, // Write permission, others. + PAL_S_IXOTH = 00001, // Execute/search permission, others. + PAL_S_ISUID = 04000, // Set-user-ID on execution. + PAL_S_ISGID = 02000, // Set-group-ID on execution. +}; + +/** + * Constants for interpreting the permissions encoded in FileStatus.Mode + * Only the names (without the PAL_ prefix) are specified by POSIX.1.2008. + * The values chosen below are in common use, but not guaranteed. + */ +enum +{ + PAL_S_IFMT = 0xF000, // Type of file (apply as mask to FileStatus.Mode and one of S_IF*) + PAL_S_IFIFO = 0x1000, // FIFO (named pipe) + PAL_S_IFCHR = 0x2000, // Character special + PAL_S_IFDIR = 0x4000, // Directory + PAL_S_IFREG = 0x8000, // Regular file + PAL_S_IFLNK = 0xA000, // Symbolic link + PAL_S_IFSOCK = 0xC000, // Socket +}; + +/** + * Constants for interpreting the flags passed to Open or ShmOpen. + * There are several other values defined by POSIX but not implemented + * everywhere. The set below is restricted to the current needs of + * COREFX, which increases portability and speeds up conversion. We + * can add more as needed. + */ +enum +{ + // Access modes (mutually exclusive). + PAL_O_RDONLY = 0x0000, // Open for read-only + PAL_O_WRONLY = 0x0001, // Open for write-only + PAL_O_RDWR = 0x0002, // Open for read-write + + // Mask to get just the access mode. Some room is left for more. + // POSIX also defines O_SEARCH and O_EXEC that are not available + // everywhere. + PAL_O_ACCESS_MODE_MASK = 0x000F, + + // Flags (combinable) + // These numeric values are not defined by POSIX and vary across targets. + PAL_O_CLOEXEC = 0x0010, // Close-on-exec + PAL_O_CREAT = 0x0020, // Create file if it doesn't already exist + PAL_O_EXCL = 0x0040, // When combined with CREAT, fails if file already exists + PAL_O_TRUNC = 0x0080, // Truncate file to length 0 if it already exists + PAL_O_SYNC = 0x0100, // Block writes call will block until physically written +}; + +/** + * Constants for interpreting FileStatus.Flags. + */ +enum +{ + FILESTATUS_FLAGS_NONE = 0, + FILESTATUS_FLAGS_HAS_BIRTHTIME = 1, +}; + +/** + * Constants for interpreting FileStatus.UserFlags. + */ +enum +{ + PAL_UF_HIDDEN = 0x8000 +}; + +/** + * Constants from dirent.h for the inode type returned from readdir variants + */ +enum NodeType +{ + PAL_DT_UNKNOWN = 0, // Unknown file type + PAL_DT_FIFO = 1, // Named Pipe + PAL_DT_CHR = 2, // Character Device + PAL_DT_DIR = 4, // Directory + PAL_DT_BLK = 6, // Block Device + PAL_DT_REG = 8, // Regular file + PAL_DT_LNK = 10, // Symlink + PAL_DT_SOCK = 12, // Socket + PAL_DT_WHT = 14 // BSD Whiteout +}; + +/** + * Constants from sys/file.h for lock types + */ +enum LockOperations +{ + PAL_LOCK_SH = 1, /* shared lock */ + PAL_LOCK_EX = 2, /* exclusive lock */ + PAL_LOCK_NB = 4, /* don't block when locking*/ + PAL_LOCK_UN = 8, /* unlock */ +}; + +/** + * Constants for changing the access permissions of a path + */ +enum AccessMode +{ + PAL_F_OK = 0, /* Check for existence */ + PAL_X_OK = 1, /* Check for execute */ + PAL_W_OK = 2, /* Check for write */ + PAL_R_OK = 4, /* Check for read */ +}; + +/** + * Flags to pass to fnmatch for what type of pattern matching to do + */ +enum FnMatchFlags +{ + PAL_FNM_NONE = 0, +}; + +/** + * Constants passed to lseek telling the OS where to seek from + */ +enum SeekWhence +{ + PAL_SEEK_SET = 0, /* seek from the beginning of the stream */ + PAL_SEEK_CUR = 1, /* seek from the current position */ + PAL_SEEK_END = 2, /* seek from the end of the stream, wrapping if necessary */ +}; + +/** + * Constants for protection argument to MMap or MProtect. + */ +enum +{ + PAL_PROT_NONE = 0, // pages may not be accessed (unless combined with one of below) + PAL_PROT_READ = 1, // pages may be read + PAL_PROT_WRITE = 2, // pages may be written + PAL_PROT_EXEC = 4, // pages may be executed +}; + +/** + * Constants for flags argument passed to MMap. + */ +enum +{ + // shared/private are mutually exclusive + PAL_MAP_SHARED = 0x01, // shared mapping + PAL_MAP_PRIVATE = 0x02, // private copy-on-write-mapping + + PAL_MAP_ANONYMOUS = 0x10, // mapping is not backed by any file +}; + +/** + * Constants for flags argument passed to MSync. + */ +enum +{ + // sync/async are mutually exclusive + PAL_MS_ASYNC = 0x01, // request sync, but don't block on completion + PAL_MS_SYNC = 0x02, // block until sync completes + + PAL_MS_INVALIDATE = 0x10, // cause other mappings of the same file to be updated +}; + +/** + * Advice argument to MAdvise. + */ +enum MemoryAdvice +{ + PAL_MADV_DONTFORK = 1, // don't map pages in to forked process +}; + +/** + * Name argument to SysConf. + */ +enum SysConfName +{ + PAL_SC_CLK_TCK = 1, // Number of clock ticks per second + PAL_SC_PAGESIZE = 2, // Size of a page in bytes +}; + +/** + * Constants passed to and from poll describing what to poll for and what + * kind of data was received from poll. + */ +enum PollEvents +{ + PAL_POLLIN = 0x0001, /* non-urgent readable data available */ + PAL_POLLPRI = 0x0002, /* urgent readable data available */ + PAL_POLLOUT = 0x0004, /* data can be written without blocked */ + PAL_POLLERR = 0x0008, /* an error occurred */ + PAL_POLLHUP = 0x0010, /* the file descriptor hung up */ + PAL_POLLNVAL = 0x0020, /* the requested events were invalid */ +}; + +/** + * Constants passed to posix_advise to give hints to the kernel about the type of I/O + * operations that will occur. + */ +enum FileAdvice +{ + PAL_POSIX_FADV_NORMAL = 0, /* no special advice, the default value */ + PAL_POSIX_FADV_RANDOM = 1, /* random I/O access */ + PAL_POSIX_FADV_SEQUENTIAL = 2, /* sequential I/O access */ + PAL_POSIX_FADV_WILLNEED = 3, /* will need specified pages */ + PAL_POSIX_FADV_DONTNEED = 4, /* don't need the specified pages */ + PAL_POSIX_FADV_NOREUSE = 5, /* data will only be acessed once */ +}; + +/** + * Our intermediate dirent struct that only gives back the data we need + */ +struct DirectoryEntry +{ + const char* Name; // Address of the name of the inode + int32_t NameLength; // Length (in chars) of the inode name + int32_t InodeType; // The inode type as described in the NodeType enum +}; + +/** + * Our intermediate pollfd struct to normalize the data types + */ +struct PollEvent +{ + int32_t FileDescriptor; // The file descriptor to poll + int16_t Events; // The events to poll for + int16_t TriggeredEvents; // The events that triggered the poll +}; + +/** +* Constants passed in the mask argument of INotifyAddWatch which identify inotify events. +*/ +enum NotifyEvents +{ + PAL_IN_ACCESS = 0x00000001, + PAL_IN_MODIFY = 0x00000002, + PAL_IN_ATTRIB = 0x00000004, + PAL_IN_MOVED_FROM = 0x00000040, + PAL_IN_MOVED_TO = 0x00000080, + PAL_IN_CREATE = 0x00000100, + PAL_IN_DELETE = 0x00000200, + PAL_IN_Q_OVERFLOW = 0x00004000, + PAL_IN_IGNORED = 0x00008000, + PAL_IN_ONLYDIR = 0x01000000, + PAL_IN_DONT_FOLLOW = 0x02000000, + PAL_IN_EXCL_UNLINK = 0x04000000, + PAL_IN_ISDIR = 0x40000000, +}; + + +int32_t SystemNative_Stat2(const char* path, struct FileStatus* output); +int32_t SystemNative_FStat2(intptr_t fd, struct FileStatus* output); +int32_t SystemNative_LStat2(const char* path, struct FileStatus* output); + +/** + * Get file status from a descriptor. Implemented as shim to fstat(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_FStat2(intptr_t fd, struct FileStatus* output); + +/** + * Get file status from a full path. Implemented as shim to stat(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_Stat2(const char* path, struct FileStatus* output); + +/** + * Get file stats from a full path. Implemented as shim to lstat(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_LStat2(const char* path, struct FileStatus* output); + +/** + * Open or create a file or device. Implemented as shim to open(2). + * + * Returns file descriptor or -1 for failure. Sets errno on failure. + */ +DLLEXPORT intptr_t SystemNative_Open(const char* path, int32_t flags, int32_t mode); + +/** + * Close a file descriptor. Implemented as shim to open(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_Close(intptr_t fd); + +/** + * Duplicates a file descriptor. + * + * Returns the duplication descriptor for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT intptr_t SystemNative_Dup(intptr_t oldfd); + +/** + * Delete an entry from the file system. Implemented as shim to unlink(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_Unlink(const char* path); + +/** + * Open or create a shared memory object. Implemented as shim to shm_open(3). + * + * Returns file descriptor or -1 on fiailure. Sets errno on failure. + */ +DLLEXPORT intptr_t SystemNative_ShmOpen(const char* name, int32_t flags, int32_t mode); + +/** + * Unlink a shared memory object. Implemented as shim to shm_unlink(3). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_ShmUnlink(const char* name); + +/** + * Returns the size of the dirent struct on the current architecture + */ +DLLEXPORT int32_t SystemNative_GetReadDirRBufferSize(void); + +/** + * Re-entrant readdir that will retrieve the next dirent from the directory stream pointed to by dir. + * + * Returns 0 when data is retrieved; returns -1 when end-of-stream is reached; returns an error code on failure + */ +DLLEXPORT int32_t SystemNative_ReadDirR(DIR* dir, uint8_t* buffer, int32_t bufferSize, struct DirectoryEntry* outputEntry); + +/** + * Returns a DIR struct containing info about the current path or NULL on failure; sets errno on fail. + */ +DLLEXPORT DIR* SystemNative_OpenDir(const char* path); + +/** + * Closes the directory stream opened by opendir and returns 0 on success. On fail, -1 is returned and errno is set + */ +DLLEXPORT int32_t SystemNative_CloseDir(DIR* dir); + +/** + * Creates a pipe. Implemented as shim to pipe(2) or pipe2(2) if available. + * Flags are ignored if pipe2 is not available. + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_Pipe(int32_t pipefd[2], // [out] pipefds[0] gets read end, pipefd[1] gets write end. + int32_t flags); // 0 for defaults or PAL_O_CLOEXEC for close-on-exec + +// NOTE: Rather than a general fcntl shim, we opt to export separate functions +// for each command. This allows use to have strongly typed arguments and saves +// complexity around converting command codes. + +/** + * Sets the O_CLOEXEC flag on a file descriptor. + * + * Returns 0 for success; -1 for failure. Sets errno for failure. + */ +DLLEXPORT int32_t SystemNative_FcntlSetCloseOnExec(intptr_t fd); + +/** + * Determines if the current platform supports getting and setting pipe capacity. + * + * Returns true (non-zero) if supported, false (zero) if not. + */ +DLLEXPORT int32_t SystemNative_FcntlCanGetSetPipeSz(void); + +/** + * Gets the capacity of a pipe. + * + * Returns the capacity or -1 with errno set aprropriately on failure. + * + * NOTE: Some platforms do not support this operation and will always fail with errno = ENOTSUP. + */ +DLLEXPORT int32_t SystemNative_FcntlGetPipeSz(intptr_t fd); + +/** + * Sets the capacity of a pipe. + * + * Returns 0 for success, -1 for failure. Sets errno for failure. + * + * NOTE: Some platforms do not support this operation and will always fail with errno = ENOTSUP. + */ +DLLEXPORT int32_t SystemNative_FcntlSetPipeSz(intptr_t fd, int32_t size); + +/** + * Sets whether or not a file descriptor is non-blocking. + * + * Returns 0 for success, -1 for failure. Sets errno for failure. + */ +DLLEXPORT int32_t SystemNative_FcntlSetIsNonBlocking(intptr_t fd, int32_t isNonBlocking); + +/** + * Create a directory. Implemented as a shim to mkdir(2). + * + * Returns 0 for success, -1 for failure. Sets errno for failure. + */ +DLLEXPORT int32_t SystemNative_MkDir(const char* path, int32_t mode); + +/** + * Change permissions of a file. Implemented as a shim to chmod(2). + * + * Returns 0 for success, -1 for failure. Sets errno for failure. + */ +DLLEXPORT int32_t SystemNative_ChMod(const char* path, int32_t mode); + +/** +* Change permissions of a file. Implemented as a shim to fchmod(2). +* +* Returns 0 for success, -1 for failure. Sets errno for failure. +*/ +DLLEXPORT int32_t SystemNative_FChMod(intptr_t fd, int32_t mode); + +/** + * Flushes all modified data and attribtues of the specified File Descriptor to the storage medium. + * + * Returns 0 for success; on fail, -1 is returned and errno is set. + */ +DLLEXPORT int32_t SystemNative_FSync(intptr_t fd); + +/** + * Changes the advisory lock status on a given File Descriptor + * + * Returns 0 on success; otherwise, -1 is returned and errno is set + */ +DLLEXPORT int32_t SystemNative_FLock(intptr_t fd, int32_t operation); + +/** + * Changes the current working directory to be the specified path. + * + * Returns 0 on success; otherwise, returns -1 and errno is set + */ +DLLEXPORT int32_t SystemNative_ChDir(const char* path); + +/** + * Checks the access permissions of the current calling user on the specified path for the specified mode. + * + * Returns -1 if the path cannot be found or the if desired access is not granted and errno is set; otherwise, returns + * 0. + */ +DLLEXPORT int32_t SystemNative_Access(const char* path, int32_t mode); + +/** + * Tests whether a pathname matches a specified pattern. + * + * Returns 0 if the string matches; returns FNM_NOMATCH if the call succeeded but the + * string does not match; otherwise, returns a non-zero error code. + */ +DLLEXPORT int32_t SystemNative_FnMatch(const char* pattern, const char* path, int32_t flags); + +/** + * Seek to a specified location within a seekable stream + * + * On success, the resulting offet, in bytes, from the beginning of the stream; otherwise, + * returns -1 and errno is set. + */ +DLLEXPORT int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, int32_t whence); + +/** + * Creates a hard-link at link pointing to source. + * + * Returns 0 on success; otherwise, returns -1 and errno is set. + */ +DLLEXPORT int32_t SystemNative_Link(const char* source, const char* linkTarget); + +/** + * Creates a file name that adheres to the specified template, creates the file on disk with + * 0600 permissions, and returns an open r/w File Descriptor on the file. + * + * Returns a valid File Descriptor on success; otherwise, returns -1 and errno is set. + */ +DLLEXPORT intptr_t SystemNative_MksTemps(char* pathTemplate, int32_t suffixLength); + +/** + * Map file or device into memory. Implemented as shim to mmap(2). + * + * Returns 0 for success, nullptr for failure. Sets errno on failure. + * + * Note that null failure result is a departure from underlying + * mmap(2) using non-null sentinel. + */ +DLLEXPORT void* SystemNative_MMap(void* address, + uint64_t length, + int32_t protection, // bitwise OR of PAL_PROT_* + int32_t flags, // bitwise OR of PAL_MAP_*, but PRIVATE and SHARED are mutually exclusive. + intptr_t fd, + int64_t offset); + +/** + * Unmap file or device from memory. Implemented as shim to mmap(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_MUnmap(void* address, uint64_t length); + +/** + * Give advice about use of memory. Implemented as shim to madvise(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_MAdvise(void* address, uint64_t length, int32_t advice); + +/** + * Lock memory from being swapped out. Implemented as shim to mlock(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_MLock(void* address, uint64_t length); + +/** + * Unlock memory, allowing it to be swapped out. Implemented as shim to munlock(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_MUnlock(void* address, uint64_t length); + +/** + * Set protection on a region of memory. Implemented as shim to mprotect(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_MProtect(void* address, uint64_t length, int32_t protection); + +/** + * Sycnhronize a file with a memory map. Implemented as shim to mmap(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_MSync(void* address, uint64_t length, int32_t flags); + +/** + * Get system configuration value. Implemented as shim to sysconf(3). + * + * Returns configuration value. + * + * Sets errno to EINVAL and returns -1 if name is invalid, but make + * note that -1 can also be a meaningful successful return value, in + * which case errno is unchanged. + */ +DLLEXPORT int64_t SystemNative_SysConf(int32_t name); + +/** + * Truncate a file to given length. Implemented as shim to ftruncate(2). + * + * Returns 0 for success, -1 for failure. Sets errno on failure. + */ +DLLEXPORT int32_t SystemNative_FTruncate(intptr_t fd, int64_t length); + +/** + * Examines one or more file descriptors for the specified state(s) and blocks until the state(s) occur or the timeout + * ellapses. + * + * Returns an error or Error_SUCCESS. `triggered` is set to the number of ready descriptors if any. The number of + * triggered descriptors may be zero in the event of a timeout. + */ +DLLEXPORT int32_t SystemNative_Poll(struct PollEvent* pollEvents, uint32_t eventCount, int32_t milliseconds, uint32_t* triggered); + +/** + * Notifies the OS kernel that the specified file will be accessed in a particular way soon; this allows the kernel to + * potentially optimize the access pattern of the file. + * + * Returns 0 on success; otherwise, the error code is returned and errno is NOT set. + */ +DLLEXPORT int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, int32_t advice); + +/** +* Reads a line from the provided stream. +* +* Returns the read line, or null if no line could be read. The caller is responsible for freeing the malloc'd line. +*/ +DLLEXPORT char* SystemNative_GetLine(FILE* stream); + +/** + * Reads the number of bytes specified into the provided buffer from the specified, opened file descriptor. + * + * Returns the number of bytes read on success; otherwise, -1 is returned an errno is set. + * + * Note - on fail. the position of the stream may change depending on the platform; consult man 2 read for more info + */ +DLLEXPORT int32_t SystemNative_Read(intptr_t fd, void* buffer, int32_t bufferSize); + +/** + * Takes a path to a symbolic link and attempts to place the link target path into the buffer. If the buffer is too + * small, the path will be truncated. No matter what, the buffer will not be null terminated. + * + * Returns the number of bytes placed into the buffer on success; otherwise, -1 is returned and errno is set. + */ +DLLEXPORT int32_t SystemNative_ReadLink(const char* path, char* buffer, int32_t bufferSize); + +/** + * Renames a file, moving to the correct destination if necessary. There are many edge cases to this call, check man 2 + * rename for more info + * + * Returns 0 on succes; otherwise, returns -1 and errno is set. + */ +DLLEXPORT int32_t SystemNative_Rename(const char* oldPath, const char* newPath); + +/** + * Deletes the specified empty directory. + * + * Returns 0 on success; otherwise, returns -1 and errno is set. + */ +DLLEXPORT int32_t SystemNative_RmDir(const char* path); + +/** + * Forces a write of all modified I/O buffers to their storage mediums. + */ +DLLEXPORT void SystemNative_Sync(void); + +/** + * Writes the specified buffer to the provided open file descriptor + * + * Returns the number of bytes written on success; otherwise, returns -1 and sets errno + */ +DLLEXPORT int32_t SystemNative_Write(intptr_t fd, const void* buffer, int32_t bufferSize); + +/** + * Copies all data from the source file descriptor to the destination file descriptor. + * + * Returns 0 on success; otherwise, returns -1 and sets errno. + */ +DLLEXPORT int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd); + +/** +* Initializes a new inotify instance and returns a file +* descriptor associated with a new inotify event queue. +* +* Returns a new file descriptor on success. +* On error, -1 is returned, and errno is set to indicate the error. +*/ +DLLEXPORT intptr_t SystemNative_INotifyInit(void); + +/** +* Adds a new watch, or modifies an existing watch, +* for the file whose location is specified in pathname. +* +* Returns a nonnegative watch descriptor on success. +* On error -1 is returned and errno is set appropriately. +*/ +DLLEXPORT int32_t SystemNative_INotifyAddWatch(intptr_t fd, const char* pathName, uint32_t mask); + +/** +* Removes the watch associated with the watch descriptor wd +* from the inotify instance associated with the file descriptor fd. +* +* Returns 0 on success, or -1 if an error occurred (in which case, errno is set appropriately). +*/ +DLLEXPORT int32_t SystemNative_INotifyRemoveWatch(intptr_t fd, int32_t wd); + +/** +* Expands all symbolic links and expands all paths to return an absolute path +* +* Returns the result absolute path on success or null on error with errno set appropriately. +*/ +DLLEXPORT char* SystemNative_RealPath(const char* path); + +/** +* Attempts to retrieve the ID of the process at the end of the given socket +* +* Returns 0 on success, or -1 if an error occurred (in which case, errno is set appropriately). +*/ +DLLEXPORT int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid); + +/** +* Attempts to lock/unlock the region of the file "fd" specified by the offset and length. lockType +* can be set to F_UNLCK (2) for unlock or F_WRLCK (3) for lock. +* +* Returns 0 on success, or -1 if an error occurred (in which case, errno is set appropriately). +*/ +DLLEXPORT int32_t SystemNative_LockFileRegion(intptr_t fd, int64_t offset, int64_t length, int16_t lockType); + +/** +* Changes the file flags of the file whose location is specified in path +* +* Returns 0 for success, -1 for failure. Sets errno for failure. +*/ +DLLEXPORT int32_t SystemNative_LChflags(const char* path, uint32_t flags); + +/** + * Determines if the current platform supports setting UF_HIDDEN (0x8000) flag + * + * Returns true (non-zero) if supported, false (zero) if not. + */ +DLLEXPORT int32_t SystemNative_LChflagsCanSetHiddenFlag(void); + +/** +* Creates a symbolic link at "linkPath", pointing at "target". +* "target" may or may not exist (dangling symbolic links are valid filesystem objects) +* Returns 0 on success; otherwise, returns -1 and errno is set. +*/ +DLLEXPORT int32_t SystemNative_Symlink(const char* target, const char* linkPath); + +END_EXTERN_C diff --git a/mono/native/Makefile.am b/mono/native/Makefile.am index 2237b25e5c67..1fcdf2c9bfab 100644 --- a/mono/native/Makefile.am +++ b/mono/native/Makefile.am @@ -62,8 +62,8 @@ macos_sources = $(unix_sources) ios_sources = \ pal-icalls.h \ pal-icalls.c \ - ../../external/corefx/src/Native/Unix/System.Native/pal_io.c \ - ../../external/corefx/src/Native/Unix/System.Native/pal_io.h \ + ../../external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c \ + ../../external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h \ ../../external/corefx/src/Native/Unix/System.Native/pal_networking.c \ ../../external/corefx/src/Native/Unix/System.Native/pal_networking.h \ ../../external/corefx/src/Native/Unix/System.Native/pal_tcpstate.c \ @@ -86,8 +86,8 @@ android_sources = \ pal-android.c \ pal-icalls.h \ pal-icalls.c \ - ../../external/corefx/src/Native/Unix/System.Native/pal_io.c \ - ../../external/corefx/src/Native/Unix/System.Native/pal_io.h \ + ../../external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c \ + ../../external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h \ ../../external/corefx/src/Native/Unix/System.Native/pal_networkstatistics.c \ ../../external/corefx/src/Native/Unix/System.Native/pal_networkstatistics.h \ ../../external/corefx/src/Native/Unix/System.Native/pal_random.c \ @@ -97,8 +97,8 @@ android_sources = \ unix_sources = \ pal-icalls.h \ pal-icalls.c \ - ../../external/corefx/src/Native/Unix/System.Native/pal_io.c \ - ../../external/corefx/src/Native/Unix/System.Native/pal_io.h \ + ../../external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c \ + ../../external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h \ ../../external/corefx/src/Native/Unix/System.Native/pal_networking.c \ ../../external/corefx/src/Native/Unix/System.Native/pal_networking.h \ ../../external/corefx/src/Native/Unix/System.Native/pal_networkstatistics.c \