diff options
-rw-r--r-- | Documentation/MANPAGE.md | 2 | ||||
-rw-r--r-- | README.md | 26 | ||||
-rw-r--r-- | go.mod | 6 | ||||
-rw-r--r-- | go.sum | 11 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/node_helpers.go | 2 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/virtualnode.go | 9 | ||||
-rwxr-xr-x | tests/issue893.sh | 27 | ||||
-rw-r--r-- | tests/plaintextnames/file_holes_test.go | 2 | ||||
-rw-r--r-- | tests/reverse/correctness_test.go | 97 |
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". @@ -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 @@ -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 ) @@ -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") + } +} |