summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/fusefrontend_reverse/virtualnode.go9
-rw-r--r--tests/reverse/correctness_test.go65
2 files changed, 74 insertions, 0 deletions
diff --git a/internal/fusefrontend_reverse/virtualnode.go b/internal/fusefrontend_reverse/virtualnode.go
index 732564a..95e71ab 100644
--- a/internal/fusefrontend_reverse/virtualnode.go
+++ b/internal/fusefrontend_reverse/virtualnode.go
@@ -100,6 +100,15 @@ func (n *Node) newVirtualMemNode(content []byte, parentStat *syscall.Stat_t, ino
st.Nlink = 1
var a fuse.Attr
a.FromStat(st)
+ // With inode number reuse and hard links, we could have returned
+ // wrong data for gocryptfs.diriv and gocryptfs.xyz.longname files, respectively
+ // (https://github.com/rfjakob/gocryptfs/issues/802).
+ //
+ // Now that this is fixed, ensure that rsync and similar tools pick up the new
+ // correct files by advancing mtime and ctime by 10 seconds, which should be more
+ // than any filesytems' timestamp granularity (FAT32 has 2 seconds).
+ a.Mtime += 10
+ a.Ctime += 10
if rn.args.ForceOwner != nil {
a.Owner = *rn.args.ForceOwner
}
diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go
index 8f051d0..c93c32a 100644
--- a/tests/reverse/correctness_test.go
+++ b/tests/reverse/correctness_test.go
@@ -363,3 +363,68 @@ func TestHardlinkedLongname(t *testing.T) {
t.Errorf("Files %q have the same inode number - that's wrong!", matches)
}
}
+
+// With inode number reuse and hard links, we could have returned
+// wrong data for gocryptfs.diriv and gocryptfs.xyz.longname files, respectively
+// (https://github.com/rfjakob/gocryptfs/issues/802).
+//
+// Now that this is fixed, ensure that rsync and similar tools pick up the new
+// correct files by advancing mtime and ctime by 10 seconds, which should be more
+// than any filesytems' timestamp granularity (FAT32 has 2 seconds).
+func TestMtimePlus10(t *testing.T) {
+ if plaintextnames {
+ t.Skip("plaintextnames mode does not have virtual files")
+ }
+
+ workdirA, workdirB := newWorkdir(t)
+
+ long := workdirA + "/" + strings.Repeat("x", 200)
+ if err := os.WriteFile(long, nil, 0600); err != nil {
+ t.Fatal(err)
+ }
+ var long_stat syscall.Stat_t
+ if err := syscall.Stat(long, &long_stat); err != nil {
+ t.Fatal(err)
+ }
+
+ var workdirA_stat syscall.Stat_t
+ if err := syscall.Stat(workdirA, &workdirA_stat); err != nil {
+ t.Fatal(err)
+ }
+
+ // Find and check gocryptfs.longname.*.name
+ matches, err := filepath.Glob(workdirB + "/gocryptfs.longname.*.name")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(matches) != 1 {
+ t.Fatal(matches)
+ }
+ var name_stat syscall.Stat_t
+ if err := syscall.Stat(matches[0], &name_stat); err != nil {
+ t.Fatal(err)
+ }
+ if name_stat.Mtim.Sec != long_stat.Mtim.Sec+10 {
+ t.Errorf(".name file should show mtime+10")
+ }
+ if name_stat.Ctim.Sec != long_stat.Ctim.Sec+10 {
+ t.Errorf(".name file should show ctime+10")
+ }
+
+ if deterministic_names {
+ // No gocryptfs.diriv
+ return
+ }
+
+ // Check gocryptfs.diriv
+ var diriv_stat syscall.Stat_t
+ if err := syscall.Stat(workdirB+"/gocryptfs.diriv", &diriv_stat); err != nil {
+ t.Fatal(err)
+ }
+ if diriv_stat.Mtim.Sec != workdirA_stat.Mtim.Sec+10 {
+ t.Errorf("diriv file should show mtime+10")
+ }
+ if diriv_stat.Ctim.Sec != workdirA_stat.Ctim.Sec+10 {
+ t.Errorf("diriv file should show ctime+10")
+ }
+}