aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/MANPAGE.md2
-rw-r--r--README.md26
-rw-r--r--go.mod6
-rw-r--r--go.sum11
-rw-r--r--internal/fusefrontend_reverse/node_helpers.go2
-rw-r--r--internal/fusefrontend_reverse/virtualnode.go9
-rwxr-xr-xtests/issue893.sh27
-rw-r--r--tests/plaintextnames/file_holes_test.go2
-rw-r--r--tests/reverse/correctness_test.go97
9 files changed, 156 insertions, 26 deletions
diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md
index 1d3ab6c..c7a1c03 100644
--- a/Documentation/MANPAGE.md
+++ b/Documentation/MANPAGE.md
@@ -580,7 +580,7 @@ files. They are concatenated for the effective password.
Example:
echo hello > hello.txt
- echo word > world.txt
+ echo world > world.txt
gocryptfs -passfile hello.txt -passfile world.txt
The effective password will be "helloworld".
diff --git a/README.md b/README.md
index f5783a4..3776c01 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[![gocryptfs](Documentation/gocryptfs-logo.png)](https://nuetzlich.net/gocryptfs/)
[![CI](https://github.com/rfjakob/gocryptfs/actions/workflows/ci.yml/badge.svg)](https://github.com/rfjakob/gocryptfs/actions/workflows/ci.yml)
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
-[![Go Report Card](https://goreportcard.com/badge/github.com/rfjakob/gocryptfs)](https://goreportcard.com/report/github.com/rfjakob/gocryptfs)
+[![Go Report Card](https://goreportcard.com/badge/github.com/rfjakob/gocryptfs/v2)](https://goreportcard.com/report/github.com/rfjakob/gocryptfs/v2)
[![Latest release](https://img.shields.io/github/release/rfjakob/gocryptfs.svg)](https://github.com/rfjakob/gocryptfs/releases)
[![Homebrew version](https://img.shields.io/homebrew/v/gocryptfs.svg)](https://formulae.brew.sh/formula/gocryptfs#default)
@@ -195,6 +195,30 @@ RM: 2,367
Changelog
---------
+#### v2.5.1, 2025-01-23
+* **Downgrade `golang.org/x/sys` to unbreak `-allow_other`
+ ([6d342f3](https://github.com/rfjakob/gocryptfs/commit/6d342f3f4f1e9468da00b141b2abaf1e55f28665),
+ [#893](https://github.com/rfjakob/gocryptfs/issues/893), [#892](https://github.com/rfjakob/gocryptfs/issues/892))**
+
+#### v2.5.0, 2025-01-18
+* **Important fixes for `-reverse` mode affecting the virtual `gocryptfs.diriv` and
+ `gocryptfs.longname.*.name` files.** The bug can cause file *names* to become
+ undecryptable. To make sure that sync tools like rsync copy new, good copies,
+ gocryptfs v2.5.0 and later advance ctime and mtime for these files by 10 seconds.
+ * Fix `-reverse` mode sometimes (triggered by inode number reuse) returning stale
+ data for `gocryptfs.diriv` (#802)
+ * Fix `-reverse` mode hardlinking `gocryptfs.longname.*.name` files of hardlinked
+ files together (#802)
+* Fix `-reverse` mode ignoring `-force-owner` (#809)
+* Add workaround for excessive file fragementation on btrfs (#811)
+* `-ctlsock`: automatically delete orphaned colliding socket file (#776)
+* MacOS: Fix XTIMES panic on startup (#823)
+ * Fixed by updating the go-fuse library to v2.5.0
+* MacOS: merge kernel options before passing them on (#854, #557)
+* Add `-fido2-assert-option` (#807)
+* `-init` now accepts `-masterkey`
+* `-passwd` now ignores `-extpass` and `-passfile` for the *new* password (#287, #882)
+
#### v2.4.0, 2023-06-10
* Try the `mount(2)` syscall before falling back to `fusermount(1)`. This means we
don't need `fusermount(1)` at all if running as root or in a root-like namespace
diff --git a/go.mod b/go.mod
index 63ac969..0761a16 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
github.com/rfjakob/eme v1.1.2
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/spf13/pflag v1.0.5
- golang.org/x/crypto v0.18.0
- golang.org/x/sys v0.16.0
- golang.org/x/term v0.16.0
+ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
+ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
+ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
)
diff --git a/go.sum b/go.sum
index 37b5355..13baeb9 100644
--- a/go.sum
+++ b/go.sum
@@ -25,16 +25,15 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
+golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
-golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
+golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
+golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go
index 30361bc..898587b 100644
--- a/internal/fusefrontend_reverse/node_helpers.go
+++ b/internal/fusefrontend_reverse/node_helpers.go
@@ -175,7 +175,7 @@ func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inod
errno = fs.ToErrno(err)
return
}
- content := pathiv.Derive(d.cPath, pathiv.PurposeDirIV)
+ content := rn.deriveDirIV(d.cPath)
var vf *VirtualMemNode
vf, errno = n.newVirtualMemNode(content, st, inoTagDirIV)
if errno != 0 {
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/issue893.sh b/tests/issue893.sh
new file mode 100755
index 0000000..a1e7cdb
--- /dev/null
+++ b/tests/issue893.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Reproducer for https://github.com/rfjakob/gocryptfs/issues/893 .
+# Run this script as non-root against a root-mounted gocryptfs -allow_other.
+
+set -eu
+
+mountpoint $1
+cd $1
+
+work() {
+ for i in $(seq 100) ; do
+ D=mtest.$BASHPID.$i/foo/bar/baz
+ mkdir -p $D
+ touch $D/foo $D/bar
+ echo AAAAAAAAAAAAAAAAAAAAA > $D/foo
+ rm $D/foo
+ mkdir $D/baz
+ done
+}
+
+rm -Rf mtest.*
+echo .
+
+work &
+work &
+
+wait
diff --git a/tests/plaintextnames/file_holes_test.go b/tests/plaintextnames/file_holes_test.go
index a17597a..ea47113 100644
--- a/tests/plaintextnames/file_holes_test.go
+++ b/tests/plaintextnames/file_holes_test.go
@@ -129,6 +129,8 @@ func doTestFileHoleCopy(t *testing.T, name string, writeOffsets []int64) {
// The test runs with -plaintextnames because that makes it easier to manipulate
// cipherdir directly.
func TestFileHoleCopy(t *testing.T) {
+ t.Skip("TODO: find out why this fails on recent kernels")
+
// | hole | x | hole | x | hole |
// truncate -s 50000 foo && dd if=/dev/zero of=foo bs=1 seek=10000 count=1 conv=notrunc && dd if=/dev/zero of=foo bs=1 seek=30000 count=1 conv=notrunc
name := "c0"
diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go
index b335456..e4684df 100644
--- a/tests/reverse/correctness_test.go
+++ b/tests/reverse/correctness_test.go
@@ -295,6 +295,23 @@ func TestSeekData(t *testing.T) {
f.Close()
}
+// newWorkdir creates a new empty dir in dirA and returns the full path to it along
+// with the corresponding encrypted path in dirB
+func newWorkdir(t *testing.T) (workdirA, workdirB string) {
+ workdirA = dirA + "/" + t.Name()
+ if err := os.Mkdir(workdirA, 0700); err != nil {
+ t.Fatal(err)
+ }
+ // Find workdir in dirB (=encrypted view)
+ var st syscall.Stat_t
+ if err := syscall.Stat(workdirA, &st); err != nil {
+ t.Fatal(err)
+ }
+ workdirB = dirB + "/" + findIno(dirB, st.Ino)
+ t.Logf("newWorkdir: workdirA=%q workdirB=%q", workdirA, workdirB)
+ return
+}
+
// gocryptfs.longname.*.name of hardlinked files should not appear hardlinked (as the
// contents are different).
//
@@ -308,28 +325,22 @@ func TestHardlinkedLongname(t *testing.T) {
t.Skip()
}
- workdir := dirA + "/" + t.Name()
- if err := os.Mkdir(workdir, 0700); err != nil {
- t.Fatal(err)
- }
- long1 := workdir + "/" + strings.Repeat("x", 200)
+ workdirA, workdirB := newWorkdir(t)
+
+ long1 := workdirA + "/" + strings.Repeat("x", 200)
if err := ioutil.WriteFile(long1, []byte("hello"), 0600); err != nil {
t.Fatal(err)
}
- long2 := workdir + "/" + strings.Repeat("y", 220)
- if err := syscall.Link(long1, long2); err != nil {
+ var long1_stat syscall.Stat_t
+ if err := syscall.Stat(long1, &long1_stat); err != nil {
t.Fatal(err)
}
-
- // Find workdir in encrypted view
- var st syscall.Stat_t
- if err := syscall.Stat(workdir, &st); err != nil {
+ long2 := workdirA + "/" + strings.Repeat("y", 220)
+ if err := syscall.Link(long1, long2); err != nil {
t.Fatal(err)
}
- cWorkdir := dirB + "/" + findIno(dirB, st.Ino)
- t.Logf("workdir=%q cWorkdir=%q", workdir, cWorkdir)
- matches, err := filepath.Glob(cWorkdir + "/gocryptfs.longname.*.name")
+ matches, err := filepath.Glob(workdirB + "/gocryptfs.longname.*.name")
if err != nil {
t.Fatal(err)
}
@@ -352,3 +363,61 @@ 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)
+ }
+ long_stat, err := os.Stat(long)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ workdirA_stat, err := os.Stat(workdirA)
+ if 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)
+ }
+ name_stat, err := os.Stat(matches[0])
+ if err != nil {
+ t.Fatal(err)
+ }
+ if name_stat.ModTime().Unix() != long_stat.ModTime().Unix()+10 {
+ t.Errorf(".name file should show mtime+10")
+ }
+
+ // Check gocryptfs.diriv
+ if deterministic_names {
+ // No gocryptfs.diriv
+ return
+ }
+ diriv_stat, err := os.Stat(workdirB + "/gocryptfs.diriv")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if diriv_stat.ModTime().Unix() != workdirA_stat.ModTime().Unix()+10 {
+ t.Errorf("diriv file should show mtime+10")
+ }
+}