extern "C" 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); }
extern "C" int32_t SystemNative_GetPeerID(intptr_t socket, uid_t* euid) { int fd = ToFileDescriptor(socket); #ifdef SO_PEERCRED 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 }
int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, int32_t advice) { #if HAVE_POSIX_ADVISE int32_t result; while (( result = #if HAVE_POSIX_FADVISE64 posix_fadvise64( #else posix_fadvise( #endif ToFileDescriptor(fd), (off_t)offset, (off_t)length, advice)) < 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 }
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; }
extern "C" int64_t SystemNative_LSeek(intptr_t fd, int64_t offset, SeekWhence whence) { int64_t result; while (CheckInterrupted(result = lseek(ToFileDescriptor(fd), offset, whence))); return result; }
extern "C" int32_t SystemNative_FLock(intptr_t fd, LockOperations operation) { int32_t result; while (CheckInterrupted(result = flock(ToFileDescriptor(fd), operation))); return result; }
extern "C" int32_t SystemNative_FSync(intptr_t fd) { int32_t result; while (CheckInterrupted(result = fsync(ToFileDescriptor(fd)))); return result; }
extern "C" int32_t SystemNative_FChMod(intptr_t fd, int32_t mode) { int32_t result; while (CheckInterrupted(result = fchmod(ToFileDescriptor(fd), static_cast<mode_t>(mode)))); return result; }
extern "C" int32_t SystemNative_FcntlSetCloseOnExec(intptr_t fd) { int result; while (CheckInterrupted(result = fcntl(ToFileDescriptor(fd), F_SETFD, FD_CLOEXEC))); return result; }
extern "C" intptr_t SystemNative_Dup(intptr_t oldfd) { int result; while (CheckInterrupted(result = dup(ToFileDescriptor(oldfd)))); return result; }
extern "C" 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, nullptr, COPYFILE_ALL) == 0 ? 0 : -1; #else // Get the stats on the source file. int ret; struct stat_ sourceStat; bool copied = false; #if HAVE_SENDFILE // If sendfile is available (Linux), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. while (CheckInterrupted(ret = fstat_(inFd, &sourceStat))); if (ret != 0) { return -1; } // We use `auto' here to adapt the type of `size' depending on the running platform. // 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'. auto size = UnsignedCast(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, nullptr, (size >= SSIZE_MAX ? SSIZE_MAX : static_cast<size_t>(size))); if (sent < 0) { if (errno != EINVAL && errno != ENOSYS) { return -1; } else { break; } } else { assert(UnsignedCast(sent) <= size); size -= UnsignedCast(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 // 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. while (CheckInterrupted(ret = fstat_(inFd, &sourceStat))); if (ret == 0) { struct timeval origTimes[2]; origTimes[0].tv_sec = sourceStat.st_atime; origTimes[0].tv_usec = 0; origTimes[1].tv_sec = sourceStat.st_mtime; origTimes[1].tv_usec = 0; while (CheckInterrupted(ret = futimes(outFd, origTimes))); } if (ret != 0) { return -1; } // Then copy permissions. while (CheckInterrupted(ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))); if (ret != 0) { return -1; } return 0; #endif // HAVE_FCOPYFILE }
int32_t SystemNative_FLock(intptr_t fd, int32_t operation) { int32_t result; while ((result = flock(ToFileDescriptor(fd), operation)) < 0 && errno == EINTR); return result; }
extern "C" int32_t SystemNative_IsATty(intptr_t fd) { return isatty(ToFileDescriptor(fd)); }
int32_t SystemNative_FSync(intptr_t fd) { int32_t result; while ((result = fsync(ToFileDescriptor(fd))) < 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_FcntlSetCloseOnExec(intptr_t fd) { int result; while ((result = fcntl(ToFileDescriptor(fd), F_SETFD, FD_CLOEXEC)) < 0 && errno == EINTR); return result; }
extern "C" int32_t SystemNative_FTruncate(intptr_t fd, int64_t length) { int32_t result; while (CheckInterrupted(result = ftruncate(ToFileDescriptor(fd), length))); return result; }
extern "C" int32_t SystemNative_Close(intptr_t fd) { return close(ToFileDescriptor(fd)); }
extern "C" Error SystemNative_Poll(PollEvent* pollEvents, uint32_t eventCount, int32_t milliseconds, uint32_t* triggered) { if (pollEvents == nullptr || triggered == nullptr) { return PAL_EFAULT; } if (milliseconds < -1) { return PAL_EINVAL; } size_t bufferSize = sizeof(pollfd) * static_cast<size_t>(eventCount); bool useStackBuffer = bufferSize <= 2048; pollfd* pollfds = reinterpret_cast<pollfd*>(useStackBuffer ? alloca(bufferSize) : malloc(bufferSize)); for (uint32_t i = 0; i < eventCount; i++) { const PollEvent& event = pollEvents[i]; pollfds[i] = { .fd = event.FileDescriptor, .events = event.Events, .revents = 0 }; } int rv; while (CheckInterrupted(rv = poll(pollfds, static_cast<nfds_t>(eventCount), milliseconds))); if (rv < 0) { if (!useStackBuffer) { free(pollfds); } *triggered = 0; return SystemNative_ConvertErrorPlatformToPal(errno); } for (uint32_t i = 0; i < eventCount; i++) { const pollfd& pfd = pollfds[i]; assert(pfd.fd == pollEvents[i].FileDescriptor); assert(pfd.events == pollEvents[i].Events); pollEvents[i].TriggeredEvents = static_cast<PollEvents>(pfd.revents); } *triggered = static_cast<uint32_t>(rv); if (!useStackBuffer) { free(pollfds); } return PAL_SUCCESS; } extern "C" int32_t SystemNative_PosixFAdvise(intptr_t fd, int64_t offset, int64_t length, FileAdvice advice) { #if HAVE_POSIX_ADVISE int32_t result; while (CheckInterrupted(result = posix_fadvise(ToFileDescriptor(fd), offset, length, advice))); 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 }
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; #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. while ((ret = fstat_(inFd, &sourceStat)) < 0 && errno == EINTR); if (ret != 0) { return -1; } // 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. while ((ret = fstat_(inFd, &sourceStat)) < 0 && errno == EINTR); if (ret == 0) { #if 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); #elif HAVE_FUTIMENS // futimes is not a POSIX function, and not available on Android, // but futimens is 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); #endif } if (ret != 0) { return -1; } // Then copy permissions. while ((ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) < 0 && errno == EINTR); if (ret != 0) { return -1; } return 0; #endif // HAVE_FCOPYFILE }