[libglnx] fdio: Open target dirname for glnx_file_copy_at()



commit 5ee2f1be7a10a3644168d7f9e6281d4c5bcabe87
Author: Colin Walters <walters verbum org>
Date:   Fri Sep 22 11:34:14 2017 -0400

    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 7113aeb..53f82e2 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 9ec9be7..bf973b9 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);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]