unix: handle src, dest same in uv_fs_copyfile()

This commit handles the case where the source and destination
are the same. This behavior was originally addressed in #2298,
but the test added in that PR doesn't validate the file size
after the operation. This commit also updates the test to check
for that case.

Refs: https://github.com/libuv/libuv/pull/2298
Refs: https://github.com/nodejs/node/issues/34624
PR-URL: https://github.com/libuv/libuv/pull/2947
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Jameson Nash <vtjnash@gmail.com>
This commit is contained in:
cjihrig 2020-08-04 20:56:19 -04:00
parent b2cec846ef
commit 278cfa0183
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
2 changed files with 26 additions and 11 deletions

View File

@ -1146,7 +1146,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
goto out; goto out;
} }
dst_flags = O_WRONLY | O_CREAT | O_TRUNC; dst_flags = O_WRONLY | O_CREAT;
if (req->flags & UV_FS_COPYFILE_EXCL) if (req->flags & UV_FS_COPYFILE_EXCL)
dst_flags |= O_EXCL; dst_flags |= O_EXCL;
@ -1165,16 +1165,26 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
goto out; goto out;
} }
/* Get the destination file's mode. */ /* If the file is not being opened exclusively, verify that the source and
if (fstat(dstfd, &dst_statsbuf)) { destination are not the same file. If they are the same, bail out early. */
err = UV__ERR(errno); if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) {
goto out; /* Get the destination file's mode. */
} if (fstat(dstfd, &dst_statsbuf)) {
err = UV__ERR(errno);
goto out;
}
/* Check if srcfd and dstfd refer to the same file */ /* Check if srcfd and dstfd refer to the same file */
if (src_statsbuf.st_dev == dst_statsbuf.st_dev && if (src_statsbuf.st_dev == dst_statsbuf.st_dev &&
src_statsbuf.st_ino == dst_statsbuf.st_ino) { src_statsbuf.st_ino == dst_statsbuf.st_ino) {
goto out; goto out;
}
/* Truncate the file in case the destination already existed. */
if (ftruncate(dstfd, 0) != 0) {
err = UV__ERR(errno);
goto out;
}
} }
if (fchmod(dstfd, src_statsbuf.st_mode) == -1) { if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {

View File

@ -125,6 +125,11 @@ TEST_IMPL(fs_copyfile) {
r = uv_fs_copyfile(NULL, &req, src, src, 0, NULL); r = uv_fs_copyfile(NULL, &req, src, src, 0, NULL);
ASSERT(r == 0); ASSERT(r == 0);
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);
/* Verify that the src file did not get truncated. */
r = uv_fs_stat(NULL, &req, src, NULL);
ASSERT_EQ(r, 0);
ASSERT_EQ(req.statbuf.st_size, 12);
uv_fs_req_cleanup(&req);
unlink(src); unlink(src);
/* Copies file synchronously. Creates new file. */ /* Copies file synchronously. Creates new file. */