rofiles: Fix --copyup when creating a new file
authorColin Walters <walters@verbum.org>
Fri, 5 Jan 2018 21:02:58 +0000 (16:02 -0500)
committerAtomic Bot <atomic-devel@projectatomic.io>
Mon, 8 Jan 2018 15:21:29 +0000 (15:21 +0000)
This tripped up the `docbook-dtds` `%post` in my experiments
with doing rpm-ostree for buildroots.

I cloned and built [xfstests](https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git)
but haven't yet investigated actually running it.

In the meantime let's do the obvious fix here; we need to distinguish
between "copyup enabled" and "actually did a copyup" in the open path
at least, since if we didn't do a copyup we don't need to re-open.

Closes: #1396
Approved by: jlebon

src/rofiles-fuse/main.c
tests/test-rofiles-fuse.sh

index 2e1c13468678c76f2081007c8f950a79210aa283..77c2f3d7d5535101b660d90e94d5ed39f99207f7 100644 (file)
@@ -247,10 +247,14 @@ gioerror_to_errno (GIOErrorEnum e)
 }
 
 static int
-verify_write_or_copyup (const char *path, const struct stat *stbuf)
+verify_write_or_copyup (const char *path, const struct stat *stbuf,
+                        gboolean *out_did_copyup)
 {
   struct stat stbuf_local;
 
+  if (out_did_copyup)
+    *out_did_copyup = FALSE;
+
   /* If a stbuf wasn't provided, gather it now */
   if (!stbuf)
     {
@@ -272,6 +276,8 @@ verify_write_or_copyup (const char *path, const struct stat *stbuf)
           g_autoptr(GError) tmp_error = NULL;
           if (!ostree_break_hardlink (basefd, path, FALSE, NULL, &tmp_error))
             return -gioerror_to_errno ((GIOErrorEnum)tmp_error->code);
+          if (out_did_copyup)
+            *out_did_copyup = TRUE;
         }
       else
         return -EROFS;
@@ -286,7 +292,7 @@ verify_write_or_copyup (const char *path, const struct stat *stbuf)
  */
 #define PATH_WRITE_ENTRYPOINT(path) do {                     \
     path = ENSURE_RELPATH (path);                            \
-    int r = verify_write_or_copyup (path, NULL);             \
+    int r = verify_write_or_copyup (path, NULL, NULL);       \
     if (r != 0)                                              \
       return r;                                              \
   } while (0)
@@ -374,7 +380,8 @@ do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
           return -errno;
         }
 
-      int r = verify_write_or_copyup (path, &stbuf);
+      gboolean did_copyup;
+      int r = verify_write_or_copyup (path, &stbuf, &did_copyup);
       if (r != 0)
         {
           (void) close (fd);
@@ -382,7 +389,7 @@ do_open (const char *path, mode_t mode, struct fuse_file_info *finfo)
         }
 
       /* In the copyup case, we need to re-open */
-      if (opt_copyup)
+      if (did_copyup)
         {
           (void) close (fd);
           /* Note that unlike the initial open, we will pass through
index 805a718839433516c60bebb143143de2cad98494..1c91a5cda46b31c3403f6f1c370a2b7c9564f3e6 100755 (executable)
@@ -121,19 +121,54 @@ echo "ok flock"
 
 # And now with --copyup enabled
 
-fusermount -u ${test_tmpdir}/mnt
-assert_not_has_file mnt/firstfile
-rofiles-fuse --copyup checkout-test2 mnt
+copyup_reset() {
+    cd ${test_tmpdir}
+    fusermount -u mnt
+    rm checkout-test2 -rf
+    $OSTREE checkout -H test2 checkout-test2
+    rofiles-fuse --copyup checkout-test2 mnt
+}
+
+assert_test_file() {
+    t=$1
+    f=$2
+    if ! test ${t} "${f}"; then
+        ls -al "${f}"
+        fatal "Failed test ${t} ${f}"
+    fi
+}
+
+copyup_reset
 assert_file_has_content mnt/firstfile first
 echo "ok copyup mount"
 
+# Test O_TRUNC directly
 firstfile_orig_inode=$(stat -c %i checkout-test2/firstfile)
-for path in firstfile{,-link}; do
-    echo truncating > mnt/${path}
-    assert_file_has_content mnt/${path} truncating
-    assert_not_file_has_content mnt/${path} first
-done
+echo -n truncating > mnt/firstfile
+assert_streq "$(cat mnt/firstfile)" truncating
+firstfile_new_inode=$(stat -c %i checkout-test2/firstfile)
+assert_not_streq "${firstfile_orig_inode}" "${firstfile_new_inode}"
+assert_test_file -f checkout-test2/firstfile
+
+copyup_reset
+firstfile_link_orig_inode=$(stat -c %i checkout-test2/firstfile-link)
+firstfile_orig_inode=$(stat -c %i checkout-test2/firstfile)
+# Now write via the symlink
+echo -n truncating > mnt/firstfile-link
+assert_streq "$(cat mnt/firstfile)" truncating
 firstfile_new_inode=$(stat -c %i checkout-test2/firstfile)
+firstfile_link_new_inode=$(stat -c %i checkout-test2/firstfile-link)
 assert_not_streq "${firstfile_orig_inode}" "${firstfile_new_inode}"
+assert_streq "${firstfile_link_orig_inode}" "${firstfile_link_new_inode}"
+assert_test_file -f checkout-test2/firstfile
+# Verify we didn't replace the link with a regfile somehow
+assert_test_file -L checkout-test2/firstfile-link
+
+# These both end up creating new files; in the sed case we'll then do a rename()
+copyup_reset
+echo "hello new file" > mnt/a-new-non-copyup-file
+assert_file_has_content_literal mnt/a-new-non-copyup-file "hello new file"
+sed -i -e s,first,second, mnt/firstfile
+assert_file_has_content_literal mnt/firstfile "second"
 
 echo "ok copyup"