/* copy a file from one to another. */ void MVM_file_copy(MVMThreadContext *tc, MVMString *src, MVMString *dest) { uv_fs_t req; char * const a = MVM_string_utf8_encode_C_string(tc, src); const uv_file in_fd = uv_fs_open(tc->loop, &req, (const char *)a, O_RDONLY, 0, NULL); if (in_fd >= 0 && uv_fs_stat(tc->loop, &req, a, NULL) >= 0) { char * const b = MVM_string_utf8_encode_C_string(tc, dest); const uv_file out_fd = uv_fs_open(tc->loop, &req, (const char *)b, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_MODE, NULL); MVM_free(a); if (out_fd >= 0 && uv_fs_sendfile(tc->loop, &req, out_fd, in_fd, 0, req.statbuf.st_size, NULL) >= 0) { MVM_free(b); if (uv_fs_close(tc->loop, &req, in_fd, NULL) < 0) { uv_fs_close(tc->loop, &req, out_fd, NULL); /* should close out_fd before throw. */ MVM_exception_throw_adhoc(tc, "Failed to close file: %s", uv_strerror(req.result)); } if (uv_fs_close(tc->loop, &req, out_fd, NULL) < 0) { MVM_exception_throw_adhoc(tc, "Failed to close file: %s", uv_strerror(req.result)); } return; } else MVM_free(b); } else MVM_free(a); MVM_exception_throw_adhoc(tc, "Failed to copy file: %s", uv_strerror(req.result)); }
JL_DLLEXPORT int jl_fs_sendfile(int src_fd, int dst_fd, int64_t in_offset, size_t len) { uv_fs_t req; JL_SIGATOMIC_BEGIN(); int ret = uv_fs_sendfile(jl_io_loop, &req, dst_fd, src_fd, in_offset, len, NULL); uv_fs_req_cleanup(&req); JL_SIGATOMIC_END(); return ret; }
static ssize_t uv__fs_copyfile(uv_fs_t* req) { #if defined(__APPLE__) && !TARGET_OS_IPHONE /* On macOS, use the native copyfile(3). */ copyfile_flags_t flags; flags = COPYFILE_ALL; if (req->flags & UV_FS_COPYFILE_EXCL) flags |= COPYFILE_EXCL; return copyfile(req->path, req->new_path, NULL, flags); #else uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; struct stat statsbuf; int dst_flags; int result; int err; size_t bytes_to_send; int64_t in_offset; dstfd = -1; err = 0; /* Open the source file. */ srcfd = uv_fs_open(NULL, &fs_req, req->path, O_RDONLY, 0, NULL); uv_fs_req_cleanup(&fs_req); if (srcfd < 0) return srcfd; /* Get the source file's mode. */ if (fstat(srcfd, &statsbuf)) { err = -errno; goto out; } dst_flags = O_WRONLY | O_CREAT | O_TRUNC; if (req->flags & UV_FS_COPYFILE_EXCL) dst_flags |= O_EXCL; /* Open the destination file. */ dstfd = uv_fs_open(NULL, &fs_req, req->new_path, dst_flags, statsbuf.st_mode, NULL); uv_fs_req_cleanup(&fs_req); if (dstfd < 0) { err = dstfd; goto out; } if (fchmod(dstfd, statsbuf.st_mode) == -1) { err = -errno; goto out; } bytes_to_send = statsbuf.st_size; in_offset = 0; while (bytes_to_send != 0) { err = uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_to_send, NULL); uv_fs_req_cleanup(&fs_req); if (err < 0) break; bytes_to_send -= fs_req.result; in_offset += fs_req.result; } out: if (err < 0) result = err; else result = 0; /* Close the source file. */ err = uv__close_nocheckstdio(srcfd); /* Don't overwrite any existing errors. */ if (err != 0 && result == 0) result = err; /* Close the destination file if it is open. */ if (dstfd >= 0) { err = uv__close_nocheckstdio(dstfd); /* Don't overwrite any existing errors. */ if (err != 0 && result == 0) result = err; /* Remove the destination file if something went wrong. */ if (result != 0) { uv_fs_unlink(NULL, &fs_req, req->new_path, NULL); /* Ignore the unlink return value, as an error already happened. */ uv_fs_req_cleanup(&fs_req); } } return result; #endif }
/* copy a file from one to another */ void MVM_file_copy(MVMThreadContext *tc, MVMString *src, MVMString * dest) { /* TODO: on Windows we can use the CopyFile API, which is probaly more efficient, not to mention easier to use. */ uv_fs_t req; char * a, * b; uv_file in_fd = -1, out_fd = -1; MVMuint64 size, offset; a = MVM_string_utf8_c8_encode_C_string(tc, src); b = MVM_string_utf8_c8_encode_C_string(tc, dest); /* If the file cannot be stat(), there is little point in going any further. */ if (uv_fs_stat(tc->loop, &req, a, NULL) < 0) goto failure; size = req.statbuf.st_size; in_fd = uv_fs_open(tc->loop, &req, (const char *)a, O_RDONLY, 0, NULL); if (in_fd < 0) { goto failure; } out_fd = uv_fs_open(tc->loop, &req, (const char *)b, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_MODE, NULL); if (out_fd < 0) { goto failure; } offset = 0; do { /* sendfile() traditionally takes offset as a pointer argument * used a both input and output. libuv deviates by making * offset an integer and returning the number of bytes * sent. So it is necessary to add these explicitly. */ MVMint64 sent = uv_fs_sendfile(tc->loop, &req, out_fd, in_fd, offset, size - offset, NULL); if (sent < 0) { goto failure; } offset += sent; } while (offset < size); /* Cleanup */ if(uv_fs_close(tc->loop, &req, in_fd, NULL) < 0) { goto failure; } in_fd = -1; if (uv_fs_close(tc->loop, &req, out_fd, NULL) < 0) { goto failure; } MVM_free(b); MVM_free(a); return; failure: { /* First get the error, since it may be overwritten further on. */ const char * error = uv_strerror(req.result); /* Basic premise: dealing with all failure cases is hard. * So to simplify, a and b are allocated in all conditions. * Also to simplify, in_fd are nonnegative if open, negative * otherwise. */ MVM_free(b); MVM_free(a); /* If any of these fail there is nothing * further to do, since we're already failing */ if (in_fd >= 0) uv_fs_close(tc->loop, &req, in_fd, NULL); if (out_fd >= 0) uv_fs_close(tc->loop, &req, out_fd, NULL); /* This function only throws adhoc errors, so the message is for * progammer eyes only */ MVM_exception_throw_adhoc(tc, "Failed to copy file: %s", error); } }
static ssize_t uv__fs_copyfile(uv_fs_t* req) { #if defined(__APPLE__) && !TARGET_OS_IPHONE /* On macOS, use the native copyfile(3). */ copyfile_flags_t flags; flags = COPYFILE_ALL; if (req->flags & UV_FS_COPYFILE_EXCL) flags |= COPYFILE_EXCL; #ifdef COPYFILE_CLONE if (req->flags & UV_FS_COPYFILE_FICLONE) flags |= COPYFILE_CLONE; #endif if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { #ifdef COPYFILE_CLONE_FORCE flags |= COPYFILE_CLONE_FORCE; #else return UV_ENOSYS; #endif } return copyfile(req->path, req->new_path, NULL, flags); #else uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; struct stat statsbuf; int dst_flags; int result; int err; size_t bytes_to_send; int64_t in_offset; dstfd = -1; err = 0; /* Open the source file. */ srcfd = uv_fs_open(NULL, &fs_req, req->path, O_RDONLY, 0, NULL); uv_fs_req_cleanup(&fs_req); if (srcfd < 0) return srcfd; /* Get the source file's mode. */ if (fstat(srcfd, &statsbuf)) { err = UV__ERR(errno); goto out; } dst_flags = O_WRONLY | O_CREAT | O_TRUNC; if (req->flags & UV_FS_COPYFILE_EXCL) dst_flags |= O_EXCL; /* Open the destination file. */ dstfd = uv_fs_open(NULL, &fs_req, req->new_path, dst_flags, statsbuf.st_mode, NULL); uv_fs_req_cleanup(&fs_req); if (dstfd < 0) { err = dstfd; goto out; } if (fchmod(dstfd, statsbuf.st_mode) == -1) { err = UV__ERR(errno); goto out; } #ifdef FICLONE if (req->flags & UV_FS_COPYFILE_FICLONE || req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { if (ioctl(dstfd, FICLONE, srcfd) == -1) { /* If an error occurred that the sendfile fallback also won't handle, or this is a force clone then exit. Otherwise, fall through to try using sendfile(). */ if (errno != ENOTTY && errno != EOPNOTSUPP && errno != EXDEV) { err = UV__ERR(errno); goto out; } else if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { err = UV_ENOTSUP; goto out; } } else { goto out; } } #else if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { err = UV_ENOSYS; goto out; } #endif bytes_to_send = statsbuf.st_size; in_offset = 0; while (bytes_to_send != 0) { err = uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_to_send, NULL); uv_fs_req_cleanup(&fs_req); if (err < 0) break; bytes_to_send -= fs_req.result; in_offset += fs_req.result; } out: if (err < 0) result = err; else result = 0; /* Close the source file. */ err = uv__close_nocheckstdio(srcfd); /* Don't overwrite any existing errors. */ if (err != 0 && result == 0) result = err; /* Close the destination file if it is open. */ if (dstfd >= 0) { err = uv__close_nocheckstdio(dstfd); /* Don't overwrite any existing errors. */ if (err != 0 && result == 0) result = err; /* Remove the destination file if something went wrong. */ if (result != 0) { uv_fs_unlink(NULL, &fs_req, req->new_path, NULL); /* Ignore the unlink return value, as an error already happened. */ uv_fs_req_cleanup(&fs_req); } } if (result == 0) return 0; errno = UV__ERR(result); return -1; #endif }