Beispiel #1
0
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
}
Beispiel #2
0
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
}