From 5ee2f1be7a10a3644168d7f9e6281d4c5bcabe87 Mon Sep 17 00:00:00 2001 From: Colin Walters <walters@verbum.org> Date: Fri, 22 Sep 2017 11:34:14 -0400 Subject: [PATCH] fdio: Open target dirname for glnx_file_copy_at() Particularly if `AT_FDCWD` is used, we need to open in the target dir, otherwise we can get `EXDEV` when trying to do the final link. (Theoretically we can cross a mountpoint even with fd-relative though this is a lot less likely) --- glnx-fdio.c | 13 +++++++++---- tests/test-libglnx-fdio.c | 11 ++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/glnx-fdio.c b/glnx-fdio.c index 7113aebce..53f82e279 100644 --- a/glnx-fdio.c +++ b/glnx-fdio.c @@ -930,11 +930,16 @@ glnx_file_copy_at (int src_dfd, if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) return FALSE; - /* Open a tmpfile for dest */ + /* Open a tmpfile for dest. Particularly for AT_FDCWD calls, we really want to + * open in the target directory, otherwise we may not be able to link. + */ g_auto(GLnxTmpfile) tmp_dest = { 0, }; - if (!glnx_open_tmpfile_linkable_at (dest_dfd, ".", O_WRONLY | O_CLOEXEC, - &tmp_dest, error)) - return FALSE; + { char *dnbuf = strdupa (dest_subpath); + const char *dn = dirname (dnbuf); + if (!glnx_open_tmpfile_linkable_at (dest_dfd, dn, O_WRONLY | O_CLOEXEC, + &tmp_dest, error)) + return FALSE; + } if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0) return glnx_throw_errno_prefix (error, "regfile copy"); diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c index 9ec9be721..bf973b9a9 100644 --- a/tests/test-libglnx-fdio.c +++ b/tests/test-libglnx-fdio.c @@ -169,14 +169,24 @@ test_filecopy (void) _GLNX_TEST_DECLARE_ERROR(local_error, error); g_auto(GLnxTmpfile) tmpf = { 0, }; const char foo[] = "foo"; + struct stat stbuf; + + if (!glnx_ensure_dir (AT_FDCWD, "subdir", 0755, error)) + return; if (!glnx_file_replace_contents_at (AT_FDCWD, foo, (guint8*)foo, sizeof (foo), GLNX_FILE_REPLACE_NODATASYNC, NULL, error)) return; + /* Copy it into both the same dir and a subdir */ if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS, NULL, error)) return; + if (!glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "subdir/bar", + GLNX_FILE_COPY_NOXATTRS, NULL, error)) + return; + if (!glnx_fstatat (AT_FDCWD, "subdir/bar", &stbuf, 0, error)) + return; if (glnx_file_copy_at (AT_FDCWD, foo, NULL, AT_FDCWD, "bar", GLNX_FILE_COPY_NOXATTRS, NULL, error)) @@ -206,7 +216,6 @@ test_filecopy (void) NULL, error)) return; - struct stat stbuf; if (!glnx_fstatat_allow_noent (AT_FDCWD, "nosuchtarget", &stbuf, AT_SYMLINK_NOFOLLOW, error)) return; g_assert_cmpint (errno, ==, ENOENT); -- GitLab