extern "C" int32_t FStat(int32_t fd, FileStatus* output) { struct stat_ result; int ret = fstat_(fd, &result); if (ret == 0) { ConvertFileStatus(result, output); } return ret; }
int32_t FStat(int32_t fileDescriptor, FileStats* output) { struct stat_ result; int ret = fstat_(fileDescriptor, &result); if (ret == 0) { ConvertFileStats(result, output); } return ret; // TODO: errno conversion }
extern "C" int32_t SystemNative_FStat(intptr_t fd, FileStatus* output) { struct stat_ result; int ret; while (CheckInterrupted(ret = fstat_(ToFileDescriptor(fd), &result))); if (ret == 0) { ConvertFileStatus(result, output); } return ret; }
int32_t SystemNative_FStat2(intptr_t fd, FileStatus* output) { struct stat_ result; int ret; while ((ret = fstat_(ToFileDescriptor(fd), &result)) < 0 && errno == EINTR); if (ret == 0) { ConvertFileStatus(&result, output); } return ret; }
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_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 }