Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: rfjakob/gocryptfs
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.4.0
Choose a base ref
...
head repository: rfjakob/gocryptfs
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Jun 17, 2023

  1. Added contrib/gocryptfssh

    zevv authored and rfjakob committed Jun 17, 2023
    Copy the full SHA
    0f11c77 View commit details

Commits on Sep 5, 2023

  1. Print errors to stderr

    scaiper authored and rfjakob committed Sep 5, 2023
    Copy the full SHA
    8b1c4b0 View commit details

Commits on Sep 15, 2023

  1. tests/fsck: add malleable_base64 test filesystem

    This filesystem contains filenames with non-canonical base64
    encodings of the same name "foo", leading to this mess:
    
    $ ls mnt/
    foo  foo  foo  foo
    rfjakob committed Sep 15, 2023
    Copy the full SHA
    30c0fbd View commit details
  2. nametransform: reject non-canonical base64

    The test added in the earlier commit passes with this
    change.
    rfjakob committed Sep 15, 2023
    Copy the full SHA
    7fff33a View commit details
  3. gocryptfs -speed: call testing.Init() to not panic

    Looks like I should have been calling testing.Init()
    all along. From https://pkg.go.dev/testing#Init :
    
    > Init is only needed when calling functions such as
    > Benchmark without using "go test".
    
    Panic only affected without_openssl builds and looks
    like this:
    
      $ ./gocryptfs -speed
      gocryptfs v2.4.0-2-g8b1c4b0-dirty without_openssl; go-fuse v2.3.0; 2023-09-15 go1.21.1 linux/amd64
      cpu: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz; with AES acceleration
      AES-GCM-256-OpenSSL             panic: runtime error: invalid memory address or nil pointer dereference
      [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x5a5d20]
    
      goroutine 7 [running]:
      testing.(*common).decorate(0x40d625?, {0xc00001c150, 0x2a}, 0x830601?)
              testing/testing.go:772 +0xa0
      [...]
    
    Fixes: #789
    Relates-to: golang/go#62666
    rfjakob committed Sep 15, 2023
    Copy the full SHA
    3a65627 View commit details

Commits on Sep 17, 2023

  1. fsck: print "Checking filesystem..."

    From #779 / @jroovy
    
    > When using `-fsck`, the command line output looks like this:
    >
    > ```
    > $ gocryptfs -fsck ENCRYPTED_DIRECTORY
    > Password:
    > Decrypting master key
    > ```
    >
    > However, the user might think it's stuck at decrypting the master
    > key. Adding extra text showing that fsck is working would be nice,
    > something like:
    >
    > ```
    > $ gocryptfs -fsck ENCRYPTED_DIRECTORY
    > Password:
    > Decrypting master key
    > Checking filesystem...
    > ```
    
    Fixes #779
    rfjakob committed Sep 17, 2023
    Copy the full SHA
    4ef110f View commit details

Commits on Oct 29, 2023

  1. .github: drop unsupported Go versions

    According to https://go.dev/doc/devel/release#policy each major Go release is
    supported until there are two newer major releases. For example, Go 1.5 was
    supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8
    release. Older releases are not receiving security updates.
    
    Upcoming dependency updates to golang exp packages use newer features like
    unsafe.Slice and therefore do not build correctly against Go < 1.19.x.
    
    Drop the older versions and add the newer versions to the ci.
    
    Signed-off-by: Christian Stewart <christian@aperture.us>
    paralin authored and rfjakob committed Oct 29, 2023
    Copy the full SHA
    770707e View commit details
  2. Update /x/crypto, /x/sys/, /x/term, jacobsa-crypto

    Updated jacobsa-crypto which also pulls in the latest versions of the
    golang.org/x/ packages.
    
    Signed-off-by: Christian Stewart <christian@aperture.us>
    paralin authored and rfjakob committed Oct 29, 2023
    Copy the full SHA
    08b6ed1 View commit details
  3. Update go-fuse

    Signed-off-by: Christian Stewart <christian@aperture.us>
    paralin authored and rfjakob committed Oct 29, 2023
    Copy the full SHA
    aa49343 View commit details
  4. Update xattr

    Signed-off-by: Christian Stewart <christian@aperture.us>
    paralin authored and rfjakob committed Oct 29, 2023
    Copy the full SHA
    5da40e7 View commit details
  5. github ci: add back go 1.18 & go 1.19

    Seems to build fine and has a big userbase due to
    Debian and Ubuntu.
    rfjakob committed Oct 29, 2023
    Copy the full SHA
    94c5828 View commit details

Commits on Nov 12, 2023

  1. Update MANPAGE.md

    Removed repeated "conflicts"
    RohanTalip authored and rfjakob committed Nov 12, 2023
    Copy the full SHA
    1766df8 View commit details

Commits on Dec 11, 2023

  1. test.bash: print fs info

    rfjakob committed Dec 11, 2023
    Copy the full SHA
    15118e6 View commit details
  2. tests: TestMountPasswordIncorrect: better error on wrong exit code

    Report that exit code is wrong when the
    exit code is wrong.
    rfjakob committed Dec 11, 2023
    Copy the full SHA
    6fbe61d View commit details

Commits on Jan 23, 2024

  1. go.mod: update all deps

    rfjakob committed Jan 23, 2024
    Copy the full SHA
    9958b63 View commit details

Commits on Mar 9, 2024

  1. Copy the full SHA
    1d7dd0a View commit details

Commits on Mar 12, 2024

  1. Copy the full SHA
    0dfa7f8 View commit details

Commits on Mar 13, 2024

  1. init_dir: use masterkey arg

    pmazzini authored and rfjakob committed Mar 13, 2024
    Copy the full SHA
    8ced867 View commit details

Commits on Apr 18, 2024

  1. MANPAGE: emphasize that -reverse implies -aessiv, so you must specify…

    … it with -masterkey
    
    Fixes: #841
    rfjakob committed Apr 18, 2024
    Copy the full SHA
    7883d38 View commit details
  2. Build with v2 extensions

    Enables older CPUs (2008-2013) to take advantage of certain hardware accelerators.
    Closes #828
    zp authored and rfjakob committed Apr 18, 2024
    Copy the full SHA
    f5007b2 View commit details

Commits on Apr 19, 2024

  1. Add option to set FIDO2 verificatoin option

    Add an option to specify user verification options for `fido2-assert -t`
    
    Options will be saved to config file
    
    Provide same functionality to #705 with simpler implementation
    
    Resolve #702
    invis-z authored and rfjakob committed Apr 19, 2024
    Copy the full SHA
    4b6b955 View commit details
  2. Improve documentation

    invis-z authored and rfjakob committed Apr 19, 2024
    Copy the full SHA
    1b3fb29 View commit details

Commits on May 1, 2024

  1. tetss: reverse: add TestHardlinkedLongname

    Regression test for #802 .
    Fails at the moment.
    rfjakob committed May 1, 2024
    Copy the full SHA
    210c5c5 View commit details
  2. reverse: use unique generation number for all nodes

    We used to present gocryptfs.longname.*.name files for hardlinked
    files as hardlinked to the kernel (same Node ID) which is wrong.
    
    Fix this by using a unique generation number for all nodes, which
    also fixes possible issues with inode reuse.
    
    Basically what 1bc1db6 did
    for forward mode with -sharedstorage.
    
    Fixes #802
    rfjakob committed May 1, 2024
    Copy the full SHA
    ed0a12b View commit details

Commits on May 5, 2024

  1. inomap: incorporate spillBit into the spillNext start value

    This avoids the manual "| spillBit" logic.
    rfjakob committed May 5, 2024
    Copy the full SHA
    c85c092 View commit details
  2. inomap: export NextSpillIno()

    This will be used in reverse mode. Switch to atomic increment to avoid
    a "nextSpillInoUnlocked" helper.
    rfjakob committed May 5, 2024
    Copy the full SHA
    5a2d461 View commit details
  3. Copy the full SHA
    bbfbf37 View commit details
  4. reverse: use incrementing inode number for gocryptfs.longname.*.name …

    …files
    
    ed0a12b already fixed the kernel side,
    now we also want the .name files to NOT appear hardlinked when just
    looking at the inode number.
    
    Relates-to: #802
    rfjakob committed May 5, 2024
    Copy the full SHA
    a385079 View commit details

Commits on May 12, 2024

  1. Fix documentation error

    Fix error in the examples for `-fido2-assert-option`
    invis-z authored and rfjakob committed May 12, 2024
    Copy the full SHA
    f797960 View commit details
  2. Remove accidentially-committed generation_num package

    This package is a failed experiment and should not
    have been committed.
    
    Fixes: 9958b63
    rfjakob committed May 12, 2024
    Copy the full SHA
    67d52b3 View commit details

Commits on May 17, 2024

  1. Copy the full SHA
    72ec4b0 View commit details
  2. syscallcompat: Openat: always set O_CLOEXEC

    Let's not leak fds to logger.
    
    Before:
    
    	$ lsof -p $(pgrep logger)
    	COMMAND    PID  USER   FD   TYPE             DEVICE  SIZE/OFF    NODE NAME
    	logger  146410 jakob  cwd    DIR              253,0      4096       2 /
    	logger  146410 jakob  rtd    DIR              253,0      4096       2 /
    	logger  146410 jakob  txt    REG              253,0     41560 6293858 /usr/bin/logger
    	logger  146410 jakob  mem    REG              253,0 229754784 6292695 /usr/lib/locale/locale-archive
    	logger  146410 jakob  mem    REG              253,0    186480 6292031 /usr/lib64/libgcc_s-14-20240508.so.1
    	logger  146410 jakob  mem    REG              253,0    787128 6294119 /usr/lib64/libzstd.so.1.5.6
    	logger  146410 jakob  mem    REG              253,0    211424 6294587 /usr/lib64/liblzma.so.5.4.6
    	logger  146410 jakob  mem    REG              253,0    131128 6302636 /usr/lib64/liblz4.so.1.9.4
    	logger  146410 jakob  mem    REG              253,0     49184 6302330 /usr/lib64/libcap.so.2.69
    	logger  146410 jakob  mem    REG              253,0   2476880 6295299 /usr/lib64/libc.so.6
    	logger  146410 jakob  mem    REG              253,0    987256 6292058 /usr/lib64/libsystemd.so.0.38.0
    	logger  146410 jakob  mem    REG              253,0    906256 6295295 /usr/lib64/ld-linux-x86-64.so.2
    	logger  146410 jakob    0r  FIFO               0,14       0t0  607727 pipe
    	logger  146410 jakob    1w   CHR                1,3       0t0       4 /dev/null
    	logger  146410 jakob    2w   CHR                1,3       0t0       4 /dev/null
    	logger  146410 jakob    3u  unix 0x0000000046d9c96b       0t0  607729 type=DGRAM (CONNECTED)
    	logger  146410 jakob   10u   DIR               0,33        80    7758 /tmp/tmp.lbUiEw9P6W/a
    
    After:
    
    	$ lsof -p $(pgrep logger)
    	COMMAND    PID  USER   FD   TYPE             DEVICE  SIZE/OFF    NODE NAME
    	logger  147982 jakob  cwd    DIR              253,0      4096       2 /
    	logger  147982 jakob  rtd    DIR              253,0      4096       2 /
    	logger  147982 jakob  txt    REG              253,0     41560 6293858 /usr/bin/logger
    	logger  147982 jakob  mem    REG              253,0 229754784 6292695 /usr/lib/locale/locale-archive
    	logger  147982 jakob  mem    REG              253,0    186480 6292031 /usr/lib64/libgcc_s-14-20240508.so.1
    	logger  147982 jakob  mem    REG              253,0    787128 6294119 /usr/lib64/libzstd.so.1.5.6
    	logger  147982 jakob  mem    REG              253,0    211424 6294587 /usr/lib64/liblzma.so.5.4.6
    	logger  147982 jakob  mem    REG              253,0    131128 6302636 /usr/lib64/liblz4.so.1.9.4
    	logger  147982 jakob  mem    REG              253,0     49184 6302330 /usr/lib64/libcap.so.2.69
    	logger  147982 jakob  mem    REG              253,0   2476880 6295299 /usr/lib64/libc.so.6
    	logger  147982 jakob  mem    REG              253,0    987256 6292058 /usr/lib64/libsystemd.so.0.38.0
    	logger  147982 jakob  mem    REG              253,0    906256 6295295 /usr/lib64/ld-linux-x86-64.so.2
    	logger  147982 jakob    0r  FIFO               0,14       0t0  609636 pipe
    	logger  147982 jakob    1w   CHR                1,3       0t0       4 /dev/null
    	logger  147982 jakob    2w   CHR                1,3       0t0       4 /dev/null
    	logger  147982 jakob    3u  unix 0x00000000bc46d033       0t0  610344 type=DGRAM (CONNECTED)
    
    Fixes #846
    rfjakob committed May 17, 2024
    Copy the full SHA
    da87308 View commit details

Commits on Jun 6, 2024

  1. stupidgcm: detect AES-GCM acceleration like crypto/tls

    Instead of just looking for AES, also look for PCLMULQDQ,
    like crypto/tls does.
    
    Fixes: #822
    rfjakob committed Jun 6, 2024
    Copy the full SHA
    f06f27e View commit details

Commits on Jul 27, 2024

  1. cli: deduplicate kernel options

    Merge stock kernel options with user-provided ones before passing to go-fuse.
    
    Before: `-ko volname=custom` would result in `-o volname=mountpoint,volname=custom` to macFUSE.
    
    After: `-ko volname=custom` would produce `-o volname=custom` with no duplicates.
    
    Fixes #854 and #557
    JokerQyou authored and rfjakob committed Jul 27, 2024
    Copy the full SHA
    533c9eb View commit details

Commits on Aug 23, 2024

  1. readpassword: show where stdin is connected

    Should make debugging situations like
    
    	#852
    	Empty stdin in mkinitcpio hook
    
    easier.
    
    Examples:
    
    $ echo -n "" | ./gocryptfs -init a
    Choose a password for protecting your files.
    Reading Password from stdin (connected to "pipe:[749878]")
    Got empty Password from stdin
    
    $ ./gocryptfs -init a < /dev/null
    Choose a password for protecting your files.
    Reading Password from stdin (connected to "/dev/null")
    Got empty Password from stdin
    
    $ ./gocryptfs -init a < /dev/zero
    Choose a password for protecting your files.
    Reading Password from stdin (connected to "/dev/zero")
    fatal: maximum password length of 2048 bytes exceeded
    
    $ ./gocryptfs -init a < /dev/full
    Choose a password for protecting your files.
    Reading Password from stdin (connected to "/dev/full")
    fatal: maximum password length of 2048 bytes exceeded
    
    $ jakob@brikett:~/go/src/github.com/rfjakob/gocryptfs$ ./gocryptfs -init a < /dev/urandom
    Choose a password for protecting your files.
    Reading Password from stdin (connected to "/dev/urandom")
    
    Your master key is:
    
        4e45a317-595d8a2d-46493a30-97de86ef-
        540c7364-f0acc297-dd6f2592-7d9a5c97
    
    If the gocryptfs.conf file becomes corrupted or you ever forget your password,
    there is only one hope for recovery: The master key. Print it to a piece of
    paper and store it in a drawer. This message is only printed once.
    The gocryptfs filesystem has been created successfully.
    You can now mount it using: gocryptfs a MOUNTPOINT
    rfjakob committed Aug 23, 2024
    Copy the full SHA
    b78e6a1 View commit details
  2. Copy the full SHA
    1db3808 View commit details
  3. reverse: fix force_owner

    Fixes #809
    rfjakob committed Aug 23, 2024
    Copy the full SHA
    f665be1 View commit details

Commits on Sep 2, 2024

  1. ctlsocksrv: move Listen() call here

    Prep for solving #776
    rfjakob committed Sep 2, 2024
    Copy the full SHA
    40abf96 View commit details

Commits on Sep 3, 2024

  1. ctlsock: delete colliding orphaned socket file

    Detect and delete an orphaned socket file that collides with
    the ctlsock we want to create.
    
    Fixes #776
    rfjakob committed Sep 3, 2024
    Copy the full SHA
    2c01b1a View commit details

Commits on Sep 19, 2024

  1. dl-linux-tarball.bash: drop wget's "--no-progress" flag

    Looks like wget does not support it anymore
    $ wget --version
    GNU Wget2 2.1.0 - multithreaded metalink/file/website downloader
    
    $ ./benchmark.bash
    Testing gocryptfs   at /tmp/benchmark.bash.bmt: gocryptfs v2.4.0-38-g40abf96-dirty; go-fuse v2.5.0; 2024-09-03 go1.21.4 linux/amd64
    /tmp/benchmark.bash.bmt.mnt is a mountpoint
    Downloading linux-3.0.tar.gz
    Unknown option 'show-progress'
    rfjakob committed Sep 19, 2024
    Copy the full SHA
    2ccea57 View commit details

Commits on Oct 30, 2024

  1. manpage: Fix reference to manual section

    The `--reverse` section of the manual has a reference to an `INIT FLAGS` section, but no such section exists. Change the reference to refer to the `INIT OPTIONS` section, which does exist.
    TheBicPen authored and rfjakob committed Oct 30, 2024
    Copy the full SHA
    94bd6a3 View commit details

Commits on Nov 11, 2024

  1. Copy the full SHA
    12c0f3a View commit details
  2. Report inode number for the root node

    Now that hanwen/go-fuse#399 has
    landed we can report an inode number for the root node.
    
    Fixes #580
    rfjakob committed Nov 11, 2024
    Copy the full SHA
    8689105 View commit details

Commits on Dec 4, 2024

  1. passfile: drop byte counter from trailing garbage warning

    We don't know the exact value as we only read 2kiB.
    
    Relates-to: #882
    rfjakob committed Dec 4, 2024
    Copy the full SHA
    11f338f View commit details
  2. Copy the full SHA
    634a450 View commit details
  3. gocryptfs -passwd: ignore -extpass and -passfile for new password

    Using the same "-extpass" or "-passfile" for both old
    and new password makes little sense, and it causes real
    problems as seen here: #882
    
    I hope nobody depends on this or I'll have to revert.
    
    Fixes #287
    Fixes #882
    rfjakob committed Dec 4, 2024
    Copy the full SHA
    d5bd98e View commit details
  4. gocryptfs -passwd: fix the tests I just broke

    Turns out at least the tests depended on the old
    behavoir.
    
    Fixes d5bd98e
    rfjakob committed Dec 4, 2024
    Copy the full SHA
    82dac42 View commit details
  5. tests: ignore pidfd in fd leak test

    This is not a real leak:
    
      fd leak in test process? before, after:
      [0r=/dev/null 3r=/proc/940141/fd 5rw=anon_inode:[eventfd] (filtered: pipe:[2454797], pipe:[2454797], anon_inode:[eventpoll])]
      [0r=/dev/null 3r=/proc/940141/fd 5rw=anon_inode:[eventfd] 12rw=anon_inode:[pidfd] (filtered: pipe:[2454797], pipe:[2454797], anon_inode:[eventpoll], pipe:[2460158])]
    
    Ignore pidfd.
    rfjakob committed Dec 4, 2024
    Copy the full SHA
    9529f5d View commit details

Commits on Dec 5, 2024

  1. Copy the full SHA
    0192dca View commit details

Commits on Dec 30, 2024

  1. manpage: fix typo in -passfile example

    Paul Pazderski authored and rfjakob committed Dec 30, 2024
    Copy the full SHA
    1464f9d View commit details
Showing with 750 additions and 156 deletions.
  1. +6 −2 .github/workflows/ci.yml
  2. +37 −9 Documentation/MANPAGE.md
  3. +3 −0 build.bash
  4. +6 −2 cli_args.go
  5. +86 −0 contrib/gocryptfssh
  6. +1 −1 daemonize.go
  7. +1 −0 fsck.go
  8. +8 −8 go.mod
  9. +16 −29 go.sum
  10. +1 −1 gocryptfs-xray/paths_ctlsock.go
  11. +3 −3 gocryptfs-xray/xray_main.go
  12. +5 −3 init_dir.go
  13. +11 −4 internal/configfile/config_file.go
  14. +44 −0 internal/ctlsocksrv/ctlsock_listen.go
  15. +13 −5 internal/fido2/fido2.go
  16. +1 −1 internal/fusefrontend/file.go
  17. +13 −2 internal/fusefrontend/root_node.go
  18. +23 −0 internal/fusefrontend_reverse/node.go
  19. +14 −15 internal/fusefrontend_reverse/node_helpers.go
  20. +39 −6 internal/fusefrontend_reverse/root_node.go
  21. +9 −0 internal/fusefrontend_reverse/virtualconf.go
  22. +12 −3 internal/fusefrontend_reverse/virtualnode.go
  23. +25 −16 internal/inomap/inomap.go
  24. +13 −0 internal/inomap/inomap_test.go
  25. +10 −0 internal/nametransform/names.go
  26. +1 −2 internal/readpassword/passfile.go
  27. +8 −1 internal/readpassword/read.go
  28. +4 −3 internal/speed/speed.go
  29. +28 −0 internal/stupidgcm/cipher_suites.go
  30. +6 −8 internal/stupidgcm/prefer.go
  31. +5 −0 internal/syscallcompat/sys_common.go
  32. +2 −2 main.go
  33. +38 −18 mount.go
  34. +6 −0 test.bash
  35. +82 −6 tests/cli/cli_test.go
  36. +1 −1 tests/dl-linux-tarball.bash
  37. +11 −0 tests/fsck/fsck_test.go
  38. 0 tests/fsck/malleable_base64/27AG8t-XZH7G9ou2OSD_z g
  39. 0 tests/fsck/malleable_base64/27AG8t-XZH7G9ou2OSD_z g
  40. 0 tests/fsck/malleable_base64/27AG8t-XZH7G9ou2OSD_zg
  41. 0 tests/fsck/malleable_base64/27AG8t-XZH7G9ou2OSD_zh
  42. +20 −0 tests/fsck/malleable_base64/gocryptfs.conf
  43. +1 −0 tests/fsck/malleable_base64/gocryptfs.diriv
  44. +10 −0 tests/matrix/matrix_test.go
  45. +10 −0 tests/plaintextnames/plaintextnames_test.go
  46. +59 −0 tests/reverse/correctness_test.go
  47. +50 −0 tests/reverse/force_owner_test.go
  48. +4 −2 tests/reverse/inomap_test.go
  49. +4 −3 tests/test_helpers/mount_unmount.go
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -3,17 +3,21 @@ name: CI
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: '0 12 * * *' # Every day noon UTC

jobs:
build:
strategy:
matrix:
# Each major Go release is supported until there are two newer major releases.
# https://go.dev/doc/devel/release#policy
go:
- "1.13.x" # Ubuntu 20.04 LTS "focal"
- "1.15.x" # Debian 11 "Bullseye"
- "1.18.x" # Ubuntu 22.04 LTS "jammy"
- "1.19.x" # Debian bookworm, bullseye-backports
- "1.20.x"
- "1.21.x"
- "oldstable" # 2nd-latest Golang upstream stable
- "stable" # Latest Go upstream stable
# Don't cancel everything when one Go version fails
46 changes: 37 additions & 9 deletions Documentation/MANPAGE.md
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ Run `gocryptfs -speed` to find out if and how much slower.

#### -deterministic-names
Disable file name randomisation and creation of `gocryptfs.diriv` files.
This can prevent sync conflicts conflicts when synchronising files, but
This can prevent sync conflicts when synchronising files, but
leaks information about identical file names across directories
("Identical names leak" in https://nuetzlich.net/gocryptfs/comparison/#file-names ).

@@ -155,7 +155,10 @@ mounted using gocryptfs v1.2 and higher. Default true.

#### -reverse
Reverse mode shows a read-only encrypted view of a plaintext
directory. Implies "-aessiv".
directory. Implies `-aessiv`.

If you want to mount the encrypted view using `-masterkey`, you *must*
specify `-aessiv`.

#### -xchacha
Use XChaCha20-Poly1305 file content encryption. This should be much faster
@@ -378,7 +381,7 @@ Mount the filesystem read-write (`-rw`, default) or read-only (`-ro`).
If both are specified, `-ro` takes precedence.

#### -reverse
See the `-reverse` section in INIT FLAGS. You need to specify the
See the `-reverse` section in INIT OPTIONS. You need to specify the
`-reverse` option both at `-init` and at mount.

#### -serialize_reads
@@ -479,11 +482,32 @@ for details.

#### -fido2 DEVICE_PATH
Use a FIDO2 token to initialize and unlock the filesystem.
Use "fido2-token -L" to obtain the FIDO2 token device path.
For linux, "fido2-tools" package is needed.
Use `fido2-token -L` to obtain the FIDO2 token device path.
For linux, **fido2-tools** package is needed.

Applies to: all actions that ask for a password.

#### -fido2-assert-option OPTION
Options passed to `fido2-assert` with `-t` option.
This option may be specified multiple times, each time it will add two
arguements `-t` `OPTION` to `fido2-assert`.
See `man fido2-assert` to check supported options.

Examples:

Creating a filesystem with no pin verification:

gocryptfs -init -fido2 DEVICE_PATH -fido2-assert-option pin=false CIPHERDIR

Creating a filesystem with both user verification and pin verification:

gocryptfs -init -fido2 DEVICE_PATH -fido2-assert-option uv=true -fido2-assert-option pin=true CIPHERDIR

Creating a filesystem with both user presence and user verification:

gocryptfs -init -fido2 DEVICE_PATH -fido2-assert-option up=true -fido2-assert-option uv=true CIPHERDIR


#### -masterkey string
Use an explicit master key specified on the command line or, if the special
value "stdin" is used, read the masterkey from stdin, instead of reading
@@ -501,10 +525,14 @@ settings have to be passed on the command line: `-aessiv` when you
mount a filesystem that was created using reverse mode, or
`-plaintextnames` for a filesystem that was created with that option.

Examples:
Example 1: Mount a filesystem that was created using default options:

gocryptfs -masterkey=6f717d8b-6b5f8e8a-fd0aa206-778ec093-62c5669b-abd229cd-241e00cd-b4d6713d cipher mnt
gocryptfs -masterkey=stdin cipher mnt

Example 2: Mount a `gocryptfs -reverse` filesystem (note that you *must* specify `-aessiv`):

-masterkey=6f717d8b-6b5f8e8a-fd0aa206-778ec093-62c5669b-abd229cd-241e00cd-b4d6713d
-masterkey=stdin
gocryptfs -masterkey=stdin -aessiv cipher mnt

Applies to: all actions that ask for a password.

@@ -552,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".
3 changes: 3 additions & 0 deletions build.bash
Original file line number Diff line number Diff line change
@@ -85,6 +85,9 @@ if [[ -n ${LDFLAGS:-} ]] ; then
GO_LDFLAGS="$GO_LDFLAGS \"-extldflags=$LDFLAGS\""
fi

# Set GOAMD64 version to v2
export GOAMD64=v2

# Actual "go build" call for gocryptfs
go build "-ldflags=$GO_LDFLAGS" "$@"
# Additional binaries
8 changes: 6 additions & 2 deletions cli_args.go
Original file line number Diff line number Diff line change
@@ -35,7 +35,10 @@ type argContainer struct {
// Mount options with opposites
dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool
masterkey, mountpoint, cipherdir, cpuprofile,
memprofile, ko, ctlsock, fsname, force_owner, trace, fido2 string
memprofile, ko, ctlsock, fsname, force_owner, trace string
// FIDO2
fido2 string
fido2_assert_options []string
// -extpass, -badname, -passfile can be passed multiple times
extpass, badname, passfile []string
// For reverse mode, several ways to specify exclusions. All can be specified multiple times.
@@ -208,6 +211,7 @@ func parseCliOpts(osArgs []string) (args argContainer) {
flagSet.StringVar(&args.force_owner, "force_owner", "", "uid:gid pair to coerce ownership")
flagSet.StringVar(&args.trace, "trace", "", "Write execution trace to file")
flagSet.StringVar(&args.fido2, "fido2", "", "Protect the masterkey using a FIDO2 token instead of a password")
flagSet.StringArrayVar(&args.fido2_assert_options, "fido2-assert-option", nil, "Options to be passed with `fido2-assert -t`")

// Exclusion options
flagSet.StringArrayVar(&args.exclude, "e", nil, "Alias for -exclude")
@@ -280,7 +284,7 @@ func parseCliOpts(osArgs []string) (args argContainer) {
tlog.Fatal.Printf("The options -passfile and -masterkey cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
if len(args.extpass) > 0 && args.masterkey != "" {
if len(args.extpass) > 0 && args.masterkey != "" && !args.init {
tlog.Fatal.Printf("The options -extpass and -masterkey cannot be used at the same time")
os.Exit(exitcodes.Usage)
}
86 changes: 86 additions & 0 deletions contrib/gocryptfssh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/bin/sh

# This script mounts an gocryptfs filesystem, starts a shell in the mounted
# directory, and then unmounts the filesystem when the shell exits. This is an
# equivalent of the encfssh script by by David Rosenstrauch.

canonicalize() {
cd "$1" || return
pwd
}


case $1 in "" | -h | --help)
echo "Usage: gocryptfssh encrypted_directory [unencrypted-directory [-p]]"
echo " -p mount the unencrypted directory as public"
exit 1
;;
esac

enc_dir=$1
unenc_dir_given=false
mount_public=false
if [ ! -z "$2" ]; then
unenc_dir_given=true
unenc_dir=$2
for arg in "$@" ; do
if [ "$arg" = "-p" ]; then
mount_public=true
fi
done
[ -d "$unenc_dir" ] || mkdir -- "$unenc_dir"
else
unenc_dir=$(mktemp -d .XXXXXXXX)
fi

if [ ! -d "$enc_dir" ]; then
mkdir -- "$enc_dir"
fi

enc_dir=$(canonicalize "$enc_dir")
unenc_dir=$(canonicalize "$unenc_dir")

options=
if [ "$unenc_dir_given" = "true" ]; then
if [ "$mount_public" = "true" ]; then
options="-- -o allow_other"
fi
fi

# Attach the directory and change into it

if gocryptfs "$enc_dir" "$unenc_dir" $options; then :; else
echo "gocryptfs failed"
rmdir -- "$unenc_dir"
exit 1
fi
if ! [ "$unenc_dir_given" = "true" ]; then
chmod 700 "$unenc_dir"
fi
echo "Directory is $unenc_dir" >&2
cd "$unenc_dir" || exit

# Fall back to umount if fusermount is not available (e.g., on OS X)
fuse_umount() {
if command -v fusermount >/dev/null 2>&1; then
fusermount -u "$@"
else
umount "$@" # MacOS case
fi
}

# Honor the SHELL environment variable to select a shell to run
"$SHELL"; retval=$?

# ensure that this shell isn't itself holding the mounted directory open
# ...but avoid terminating on failure, *or* causing a shellcheck warning for
# failing to check exit status from cd.
cd / ||:

# if unmount fails, skip rmdir, always use exit status of failure
fuse_umount "$unenc_dir" || exit

if ! [ "$unenc_dir_given" = true ]; then
rmdir -- "$unenc_dir"
fi
exit "$retval"
2 changes: 1 addition & 1 deletion daemonize.go
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ func redirectStdFds() {
if err != nil {
tlog.Warn.Printf("redirectStdFds: stderr dup error: %v\n", err)
}
// Our stout and stderr point to "pw". We can close the extra copy.
// Our stdout and stderr point to "pw". We can close the original copy.
pw.Close()
// Redirect stdin to /dev/null
nullFd, err := os.Open("/dev/null")
1 change: 1 addition & 0 deletions fsck.go
Original file line number Diff line number Diff line change
@@ -305,6 +305,7 @@ func fsck(args *argContainer) (exitcode int) {
}
}()
// Recursively check the root dir
tlog.Info.Println(tlog.ColorGreen + "Checking filesystem..." + tlog.ColorReset)
ck.dir("")
// Report results
wipeKeys()
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
module github.com/rfjakob/gocryptfs/v2

go 1.16
go 1.19

require (
github.com/aperturerobotics/jacobsa-crypto v1.0.0
github.com/hanwen/go-fuse/v2 v2.3.0
github.com/aperturerobotics/jacobsa-crypto v1.0.2
github.com/hanwen/go-fuse/v2 v2.5.0
github.com/moby/sys/mountinfo v0.6.2
github.com/pkg/xattr v0.4.3
github.com/pkg/xattr v0.4.9
github.com/rfjakob/eme v1.1.2
github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/spf13/pflag v1.0.5
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
golang.org/x/crypto v0.18.0
golang.org/x/sys v0.16.0
golang.org/x/term v0.16.0
)
45 changes: 16 additions & 29 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,53 +1,40 @@
github.com/aperturerobotics/jacobsa-crypto v0.0.0-20190317225127-9f44e2d11115/go.mod h1:XKd7k7LIBmeR/WGENaSpUSjQbWBVKZFhMT7+zKM5KVU=
github.com/aperturerobotics/jacobsa-crypto v1.0.0 h1:ARfIuzgovK+5leAKbFHcicKEgMzD94tb/FTiWSHdGLU=
github.com/aperturerobotics/jacobsa-crypto v1.0.0/go.mod h1:xq0oOkHSPQ1E5ByqbwLhCJ1mygYHtXTMQnvHD4tz4Cc=
github.com/aperturerobotics/jacobsa-crypto v1.0.2 h1:tNvVy1rev9FagnOyBmTcI6d23FfNceG9IujZROTRtlc=
github.com/aperturerobotics/jacobsa-crypto v1.0.2/go.mod h1:buWU1iY+FjIcfpb1aYfFJZfl07WlS7O30lTyC2iwjv8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hanwen/go-fuse/v2 v2.3.0 h1:t5ivNIH2PK+zw4OBul/iJjsoG9K6kXo4nMDoBpciC8A=
github.com/hanwen/go-fuse/v2 v2.3.0/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/hanwen/go-fuse/v2 v2.5.0 h1:JSJcwHQ1V9EGRy6QsosoLDMX6HaLdzyLOJpKdPqDt9k=
github.com/hanwen/go-fuse/v2 v2.5.0/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs=
github.com/jacobsa/oglematchers v0.0.0-20150720000706-141901ea67cd h1:9GCSedGjMcLZCrusBZuo4tyKLpKUPenUUqi34AkuFmA=
github.com/jacobsa/oglematchers v0.0.0-20150720000706-141901ea67cd/go.mod h1:TlmyIZDpGmwRoTWiakdr+HA1Tukze6C6XbRVidYq02M=
github.com/jacobsa/oglemock v0.0.0-20150831005832-e94d794d06ff h1:2xRHTvkpJ5zJmglXLRqHiZQNjUoOkhUyhTAhEQvPAWw=
github.com/jacobsa/oglemock v0.0.0-20150831005832-e94d794d06ff/go.mod h1:gJWba/XXGl0UoOmBQKRWCJdHrr3nE0T65t6ioaj3mLI=
github.com/jacobsa/ogletest v0.0.0-20170503003838-80d50a735a11 h1:BMb8s3ENQLt5ulwVIHVDWFHp8eIXmbfSExkvdn9qMXI=
github.com/jacobsa/ogletest v0.0.0-20170503003838-80d50a735a11/go.mod h1:+DBdDyfoO2McrOyDemRBq0q9CMEByef7sYl7JH5Q3BI=
github.com/jacobsa/reqtrace v0.0.0-20150505043853-245c9e0234cb h1:uSWBjJdMf47kQlXMwWEfmc864bA1wAC+Kl3ApryuG9Y=
github.com/jacobsa/reqtrace v0.0.0-20150505043853-245c9e0234cb/go.mod h1:ivcmUvxXWjb27NsPEaiYK7AidlZXS7oQ5PowUS9z3I4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
github.com/pkg/xattr v0.4.3 h1:5Jx4GCg5ABtqWZH8WLzeI4fOtM1HyX4RBawuCoua1es=
github.com/pkg/xattr v0.4.3/go.mod h1:sBD3RAqlr8Q+RC3FutZcikpT8nyDrIEEBw2J744gVWs=
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rfjakob/eme v1.1.2 h1:SxziR8msSOElPayZNFfQw4Tjx/Sbaeeh3eRvrHVMUs4=
github.com/rfjakob/eme v1.1.2/go.mod h1:cVvpasglm/G3ngEfcfT/Wt0GwhkuO32pf/poW6Nyk1k=
github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f h1:8P2MkG70G76gnZBOPGwmMIgwBb/rESQuwsJ7K8ds4NE=
github.com/sabhiram/go-gitignore v0.0.0-20201211210132-54b8a0bf510f/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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.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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
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/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-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=
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=
2 changes: 1 addition & 1 deletion gocryptfs-xray/paths_ctlsock.go
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ func transformPaths(socketPath string, req *ctlsock.RequestStruct, in *string, s
errorCount := 0
c, err := ctlsock.New(socketPath)
if err != nil {
fmt.Printf("fatal: %v\n", err)
fmt.Fprintf(os.Stderr, "fatal: %v\n", err)
os.Exit(1)
}
line := 1
6 changes: 3 additions & 3 deletions gocryptfs-xray/xray_main.go
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ func main() {

s := sum(args.dumpmasterkey, args.decryptPaths, args.encryptPaths)
if s > 1 {
fmt.Printf("fatal: %d operations were requested\n", s)
fmt.Fprintf(os.Stderr, "fatal: %d operations were requested\n", s)
os.Exit(1)
}
if flag.NArg() != 1 {
@@ -146,7 +146,7 @@ func dumpMasterKey(fn string, fido2Path string) {
tlog.Fatal.Printf("Masterkey encrypted using FIDO2 token; need to use the --fido2 option.")
os.Exit(exitcodes.Usage)
}
pw = fido2.Secret(fido2Path, cf.FIDO2.CredentialID, cf.FIDO2.HMACSalt)
pw = fido2.Secret(fido2Path, cf.FIDO2.AssertOptions, cf.FIDO2.CredentialID, cf.FIDO2.HMACSalt)
} else {
pw, err = readpassword.Once(nil, nil, "")
if err != nil {
@@ -183,7 +183,7 @@ func inspectCiphertext(args *argContainer, fd *os.File) {
fmt.Println("empty file")
os.Exit(0)
} else if err == io.EOF {
fmt.Printf("incomplete file header: read %d bytes, want %d\n", n, contentenc.HeaderLen)
fmt.Fprintf(os.Stderr, "incomplete file header: read %d bytes, want %d\n", n, contentenc.HeaderLen)
os.Exit(1)
} else if err != nil {
errExit(err)
8 changes: 5 additions & 3 deletions init_dir.go
Original file line number Diff line number Diff line change
@@ -68,9 +68,9 @@ func initDir(args *argContainer) {
tlog.Fatal.Printf("Invalid cipherdir: %v", err)
os.Exit(exitcodes.CipherDir)
}
if !args.xchacha && !stupidgcm.CpuHasAES() {
if !args.xchacha && !stupidgcm.HasAESGCMHardwareSupport() {
tlog.Info.Printf(tlog.ColorYellow +
"Notice: Your CPU does not have AES acceleration. Consider using -xchacha for better performance." +
"Notice: Your CPU does not have AES-GCM acceleration. Consider using -xchacha for better performance." +
tlog.ColorReset)
}
}
@@ -84,7 +84,7 @@ func initDir(args *argContainer) {
if args.fido2 != "" {
fido2CredentialID = fido2.Register(args.fido2, filepath.Base(args.cipherdir))
fido2HmacSalt = cryptocore.RandBytes(32)
password = fido2.Secret(args.fido2, fido2CredentialID, fido2HmacSalt)
password = fido2.Secret(args.fido2, args.fido2_assert_options, fido2CredentialID, fido2HmacSalt)
} else {
// normal password entry
password, err = readpassword.Twice([]string(args.extpass), []string(args.passfile))
@@ -105,9 +105,11 @@ func initDir(args *argContainer) {
AESSIV: args.aessiv,
Fido2CredentialID: fido2CredentialID,
Fido2HmacSalt: fido2HmacSalt,
Fido2AssertOptions: args.fido2_assert_options,
DeterministicNames: args.deterministic_names,
XChaCha20Poly1305: args.xchacha,
LongNameMax: args.longnamemax,
Masterkey: handleArgsMasterkey(args),
})
if err != nil {
tlog.Fatal.Println(err)
15 changes: 11 additions & 4 deletions internal/configfile/config_file.go
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ type FIDO2Params struct {
CredentialID []byte
// FIDO2 hmac-secret salt
HMACSalt []byte
AssertOptions []string
}

// ConfFile is the content of a config file.
@@ -71,9 +72,11 @@ type CreateArgs struct {
AESSIV bool
Fido2CredentialID []byte
Fido2HmacSalt []byte
Fido2AssertOptions []string
DeterministicNames bool
XChaCha20Poly1305 bool
LongNameMax uint8
Masterkey []byte
}

// Create - create a new config with a random key encrypted with
@@ -116,8 +119,9 @@ func Create(args *CreateArgs) error {
if len(args.Fido2CredentialID) > 0 {
cf.setFeatureFlag(FlagFIDO2)
cf.FIDO2 = &FIDO2Params{
CredentialID: args.Fido2CredentialID,
HMACSalt: args.Fido2HmacSalt,
CredentialID: args.Fido2CredentialID,
HMACSalt: args.Fido2HmacSalt,
AssertOptions: args.Fido2AssertOptions,
}
}
// Catch bugs and invalid cli flag combinations early
@@ -126,8 +130,11 @@ func Create(args *CreateArgs) error {
return err
}
{
// Generate new random master key
key := cryptocore.RandBytes(cryptocore.KeyLen)
key := args.Masterkey
if key == nil {
// Generate new random master key
key = cryptocore.RandBytes(cryptocore.KeyLen)
}
tlog.PrintMasterkeyReminder(key)
// Encrypt it using the password
// This sets ScryptObject and EncryptedKey
44 changes: 44 additions & 0 deletions internal/ctlsocksrv/ctlsock_listen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ctlsocksrv

import (
"errors"
"io/fs"
"net"
"os"
"syscall"
"time"

"github.com/rfjakob/gocryptfs/v2/internal/tlog"
)

// cleanupOrphanedSocket deletes an orphaned socket file at `path`.
// The file at `path` will only be deleted if:
// 1) It is a socket file
// 2) Connecting to it results in ECONNREFUSED
func cleanupOrphanedSocket(path string) {
fi, err := os.Stat(path)
if err != nil {
return
}
if fi.Mode().Type() != fs.ModeSocket {
return
}
conn, err := net.DialTimeout("unix", path, time.Second)
if err == nil {
// This socket file is still active. Don't delete it.
conn.Close()
return
}
if errors.Is(err, syscall.ECONNREFUSED) {
tlog.Info.Printf("ctlsock: deleting orphaned socket file %q\n", path)
err = os.Remove(path)
if err != nil {
tlog.Warn.Printf("ctlsock: deleting socket file failed: %v", path)
}
}
}

func Listen(path string) (net.Listener, error) {
cleanupOrphanedSocket(path)
return net.Listen("unix", path)
}
18 changes: 13 additions & 5 deletions internal/fido2/fido2.go
Original file line number Diff line number Diff line change
@@ -35,13 +35,21 @@ func (fc fidoCommand) String() string {

const relyingPartyID = "gocryptfs"

func callFidoCommand(command fidoCommand, device string, stdin []string) ([]string, error) {
func callFidoCommand(command fidoCommand, assertOptions []string, device string, stdin []string) ([]string, error) {
var cmd *exec.Cmd
switch command {
case cred:
cmd = exec.Command("fido2-cred", "-M", "-h", device)
case assert:
cmd = exec.Command("fido2-assert", "-G", "-h", device)
var args []string
args = append(args, "-G")
args = append(args, "-h")
for i := range assertOptions{
args = append(args, "-t")
args = append(args, assertOptions[i])
}
args = append(args, device)
cmd = exec.Command("fido2-assert", args...)
}
tlog.Debug.Printf("callFidoCommand %s: executing %q with args %q", command, cmd.Path, cmd.Args)
cmd.Stderr = os.Stderr
@@ -67,7 +75,7 @@ func Register(device string, userName string) (credentialID []byte) {
cdh := base64.StdEncoding.EncodeToString(cryptocore.RandBytes(32))
userID := base64.StdEncoding.EncodeToString(cryptocore.RandBytes(32))
stdin := []string{cdh, relyingPartyID, userName, userID}
out, err := callFidoCommand(cred, device, stdin)
out, err := callFidoCommand(cred, nil, device, stdin)
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.FIDO2Error)
@@ -81,14 +89,14 @@ func Register(device string, userName string) (credentialID []byte) {
}

// Secret generates a HMAC secret using a FIDO2 token
func Secret(device string, credentialID []byte, salt []byte) (secret []byte) {
func Secret(device string, assertOptions []string, credentialID []byte, salt []byte) (secret []byte) {
tlog.Info.Printf("FIDO2 Secret: interact with your device ...")
cdh := base64.StdEncoding.EncodeToString(cryptocore.RandBytes(32))
crid := base64.StdEncoding.EncodeToString(credentialID)
hmacsalt := base64.StdEncoding.EncodeToString(salt)
stdin := []string{cdh, relyingPartyID, crid, hmacsalt}
// call fido2-assert
out, err := callFidoCommand(assert, device, stdin)
out, err := callFidoCommand(assert, assertOptions, device, stdin)
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.FIDO2Error)
2 changes: 1 addition & 1 deletion internal/fusefrontend/file.go
Original file line number Diff line number Diff line change
@@ -316,7 +316,7 @@ func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) {
if cOff > math.MaxInt64 {
return 0, syscall.EFBIG
}
if !f.rootNode.args.NoPrealloc {
if !f.rootNode.args.NoPrealloc && f.rootNode.quirks&syscallcompat.QuirkBrokenFalloc == 0 {
err = syscallcompat.EnospcPrealloc(f.intFd(), int64(cOff), int64(len(ciphertext)))
if err != nil {
if !syscallcompat.IsENOSPC(err) {
15 changes: 13 additions & 2 deletions internal/fusefrontend/root_node.go
Original file line number Diff line number Diff line change
@@ -59,13 +59,16 @@ type RootNode struct {
// quirks is a bitmap that enables workaround for quirks in the filesystem
// backing the cipherdir
quirks uint64
// rootIno is the inode number that we report for the root node on mount
rootIno uint64
}

func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode {
var rootDev uint64
var st syscall.Stat_t
if err := syscall.Stat(args.Cipherdir, &st); err != nil {
tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, err)
var statErr error
if statErr = syscall.Stat(args.Cipherdir, &st); statErr != nil {
tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, statErr)
} else {
rootDev = uint64(st.Dev)
}
@@ -87,6 +90,10 @@ func NewRootNode(args Args, c *contentenc.ContentEnc, n *nametransform.NameTrans
dirCache: dirCache{ivLen: ivLen},
quirks: syscallcompat.DetectQuirks(args.Cipherdir),
}
if statErr == nil {
rn.inoMap.TranslateStat(&st)
rn.rootIno = st.Ino
}
return rn
}

@@ -288,3 +295,7 @@ func (rn *RootNode) decryptXattrName(cAttr string) (attr string, err error) {
}
return attr, nil
}

func (rn *RootNode) RootIno() uint64 {
return rn.rootIno
}
23 changes: 23 additions & 0 deletions internal/fusefrontend_reverse/node.go
Original file line number Diff line number Diff line change
@@ -68,6 +68,29 @@ func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch
if t == typeReal {
n.translateSize(d.dirfd, cName, d.pName, &out.Attr)
}

if rn.args.ForceOwner != nil {
out.Owner = *rn.args.ForceOwner
}

// Usually we always create a new Node ID by always incrementing the generation
// number.
//
// If we already have a child node that matches what we found on disk*
// (as reflected in `ch`), return it here.
//
// This keeps the Node ID for each directory entry stable
// (until forgotten), preventing extra FORGETs from the kernel.
//
// *We compare `cName`, `Ino`, `Mode` (but not `Gen`!)
old := n.Inode.GetChild(cName)
if old != nil &&
old.StableAttr().Ino == ch.StableAttr().Ino &&
// `Mode` has already been masked with syscall.S_IFMT by n.newChild()
old.StableAttr().Mode == ch.StableAttr().Mode {
return old, 0
}

return ch, 0
}

29 changes: 14 additions & 15 deletions internal/fusefrontend_reverse/node_helpers.go
Original file line number Diff line number Diff line change
@@ -91,18 +91,17 @@ func (n *Node) prepareAtSyscall(child string) (d *dirfdPlus, errno syscall.Errno

// newChild attaches a new child inode to n.
// The passed-in `st` will be modified to get a unique inode number.
//
// This function is not used for virtual files. See lookupLongnameName(),
// lookupDiriv() instead.
func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.EntryOut) *fs.Inode {
isOtherFilesystem := (uint64(st.Dev) != n.rootNode().rootDev)
// Get unique inode number
rn := n.rootNode()
isOtherFilesystem := (uint64(st.Dev) != rn.rootDev)
// Get unique inode number
rn.inoMap.TranslateStat(st)
out.Attr.FromStat(st)
// Create child node
id := fs.StableAttr{
Mode: uint32(st.Mode),
Gen: 1,
Ino: st.Ino,
}
id := rn.uniqueStableAttr(uint32(st.Mode), st.Ino)
node := &Node{
isOtherFilesystem: isOtherFilesystem,
}
@@ -153,15 +152,16 @@ func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fus
}
out.Attr = vf.attr
// Create child node
id := fs.StableAttr{Mode: uint32(vf.attr.Mode), Gen: 1, Ino: vf.attr.Ino}
id := rn.uniqueStableAttr(uint32(vf.attr.Mode), vf.attr.Ino)
ch = n.NewInode(ctx, vf, id)
return

}

// lookupDiriv returns a new Inode for a gocryptfs.diriv file inside `n`.
func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) {
if rn := n.rootNode(); rn.args.DeterministicNames {
rn := n.rootNode()
if rn.args.DeterministicNames {
log.Panic("BUG: lookupDiriv called but DeterministicNames is set")
}

@@ -183,7 +183,7 @@ func (n *Node) lookupDiriv(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inod
}
out.Attr = vf.attr
// Create child node
id := fs.StableAttr{Mode: uint32(vf.attr.Mode), Gen: 1, Ino: vf.attr.Ino}
id := rn.uniqueStableAttr(uint32(vf.attr.Mode), vf.attr.Ino)
ch = n.NewInode(ctx, vf, id)
return
}
@@ -201,12 +201,11 @@ func (n *Node) lookupConf(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode
// Get unique inode number
rn.inoMap.TranslateStat(&st)
out.Attr.FromStat(&st)
// Create child node
id := fs.StableAttr{
Mode: uint32(st.Mode),
Gen: 1,
Ino: st.Ino,
if rn.args.ForceOwner != nil {
out.Owner = *rn.args.ForceOwner
}
// Create child node
id := rn.uniqueStableAttr(uint32(st.Mode), st.Ino)
node := &VirtualConfNode{path: p}
ch = n.NewInode(ctx, node, id)
return
45 changes: 39 additions & 6 deletions internal/fusefrontend_reverse/root_node.go
Original file line number Diff line number Diff line change
@@ -5,22 +5,21 @@ import (
"os"
"path/filepath"
"strings"
"sync/atomic"
"syscall"

"github.com/rfjakob/gocryptfs/v2/internal/exitcodes"

"github.com/rfjakob/gocryptfs/v2/internal/tlog"

"golang.org/x/sys/unix"

"github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse"

"github.com/rfjakob/gocryptfs/v2/internal/contentenc"
"github.com/rfjakob/gocryptfs/v2/internal/exitcodes"
"github.com/rfjakob/gocryptfs/v2/internal/fusefrontend"
"github.com/rfjakob/gocryptfs/v2/internal/inomap"
"github.com/rfjakob/gocryptfs/v2/internal/nametransform"
"github.com/rfjakob/gocryptfs/v2/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/v2/internal/tlog"

"github.com/sabhiram/go-gitignore"
)
@@ -45,6 +44,15 @@ type RootNode struct {
// If a file name length is shorter than shortNameMax, there is no need to
// hash it.
shortNameMax int
// gen is the node generation number. Normally, it is always set to 1,
// but reverse mode, like -sharestorage, uses an incrementing counter for new nodes.
// This makes each directory entry unique (even hard links),
// makes go-fuse hand out separate FUSE Node IDs for each, and prevents
// bizarre problems when inode numbers are reused behind our back,
// like this one: https://github.com/rfjakob/gocryptfs/issues/802
gen uint64
// rootIno is the inode number that we report for the root node on mount
rootIno uint64
}

// NewRootNode returns an encrypted FUSE overlay filesystem.
@@ -53,9 +61,10 @@ type RootNode struct {
func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransform.NameTransform) *RootNode {
var rootDev uint64
var st syscall.Stat_t
var statErr error
var shortNameMax int
if err := syscall.Stat(args.Cipherdir, &st); err != nil {
tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, err)
if statErr = syscall.Stat(args.Cipherdir, &st); statErr != nil {
tlog.Warn.Printf("Could not stat backing directory %q: %v", args.Cipherdir, statErr)
if args.OneFileSystem {
tlog.Fatal.Printf("This is a fatal error in combination with -one-file-system")
os.Exit(exitcodes.CipherDir)
@@ -75,6 +84,10 @@ func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n *nametransf
rootDev: rootDev,
shortNameMax: shortNameMax,
}
if statErr == nil {
rn.inoMap.TranslateStat(&st)
rn.rootIno = st.Ino
}
if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 {
rn.excluder = prepareExcluder(args)
}
@@ -149,3 +162,23 @@ func (rn *RootNode) excludeDirEntries(d *dirfdPlus, entries []fuse.DirEntry) (fi
}
return filtered
}

// uniqueStableAttr returns a fs.StableAttr struct with a unique generation number,
// preventing files to appear hard-linked, even when they have the same inode number.
//
// This is good because inode numbers can be reused behind our back, which could make
// unrelated files appear hard-linked.
// Example: https://github.com/rfjakob/gocryptfs/issues/802
func (rn *RootNode) uniqueStableAttr(mode uint32, ino uint64) fs.StableAttr {
return fs.StableAttr{
Mode: mode,
Ino: ino,
// Make each directory entry a unique node by using a unique generation
// value. Also see the comment at RootNode.gen for details.
Gen: atomic.AddUint64(&rn.gen, 1),
}
}

func (rn *RootNode) RootIno() uint64 {
return rn.rootIno
}
9 changes: 9 additions & 0 deletions internal/fusefrontend_reverse/virtualconf.go
Original file line number Diff line number Diff line change
@@ -18,6 +18,11 @@ type VirtualConfNode struct {
path string
}

// rootNode returns the Root Node of the filesystem.
func (n *VirtualConfNode) rootNode() *RootNode {
return n.Root().Operations().(*RootNode)
}

func (n *VirtualConfNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
fd, err := syscall.Open(n.path, syscall.O_RDONLY, 0)
if err != nil {
@@ -35,6 +40,10 @@ func (n *VirtualConfNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fu
return fs.ToErrno(err)
}
out.FromStat(&st)
rn := n.rootNode()
if rn.args.ForceOwner != nil {
out.Owner = *rn.args.ForceOwner
}
return 0
}

15 changes: 12 additions & 3 deletions internal/fusefrontend_reverse/virtualnode.go
Original file line number Diff line number Diff line change
@@ -86,14 +86,23 @@ func (n *Node) newVirtualMemNode(content []byte, parentStat *syscall.Stat_t, ino
// Adjust inode number and size
rn := n.rootNode()
st := parentStat
q := inomap.NewQIno(uint64(st.Dev), inoTag, uint64(st.Ino))
st.Ino = rn.inoMap.Translate(q)
if inoTag == inoTagNameFile {
// No stable mapping for gocryptfs.longname.*.name files, instead use an
// incrementing counter. We don't want two of those files to ever have the
// same inode number, even for hard-linked files.
st.Ino = rn.inoMap.NextSpillIno()
} else {
q := inomap.NewQIno(uint64(st.Dev), inoTag, uint64(st.Ino))
st.Ino = rn.inoMap.Translate(q)
}
st.Size = int64(len(content))
st.Mode = virtualFileMode
st.Nlink = 1
var a fuse.Attr
a.FromStat(st)

if rn.args.ForceOwner != nil {
a.Owner = *rn.args.ForceOwner
}
vf = &VirtualMemNode{content: content, attr: a}
return
}
41 changes: 25 additions & 16 deletions internal/inomap/inomap.go
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@
//
// Format of the returned inode numbers:
//
// [spill bit = 0][15 bit namespace id][48 bit passthru inode number]
// [spill bit = 1][63 bit spill inode number ]
// [spill bit = 0][15 bit namespace id][48 bit passthru inode number] = 64 bit translated inode number
// [spill bit = 1][63 bit counter ] = 64 bit spill inode number
//
// Each (Dev, Tag) tuple gets a namespace id assigned. The original inode
// number is then passed through in the lower 48 bits.
@@ -16,7 +16,9 @@ package inomap

import (
"log"
"math"
"sync"
"sync/atomic"
"syscall"

"github.com/rfjakob/gocryptfs/v2/internal/tlog"
@@ -27,10 +29,8 @@ const (
maxNamespaceId = 1<<15 - 1
// max value of 48 bit passthru inode number
maxPassthruIno = 1<<48 - 1
// max value of 63 bit spill inode number
maxSpillIno = 1<<63 - 1
// bit 63 is used as the spill bit
spillBit = 1 << 63
// the spill inode number space starts at 0b10000...0.
spillSpaceStart = 1 << 63
)

// InoMap stores the maps using for inode number translation.
@@ -57,7 +57,7 @@ func New(rootDev uint64) *InoMap {
namespaceMap: make(map[namespaceData]uint16),
namespaceNext: 0,
spillMap: make(map[QIno]uint64),
spillNext: 0,
spillNext: spillSpaceStart,
}
if rootDev > 0 {
// Reserve namespace 0 for rootDev
@@ -69,23 +69,32 @@ func New(rootDev uint64) *InoMap {

var spillWarn sync.Once

// NextSpillIno returns a fresh inode number from the spill pool without adding it to
// spillMap.
// Reverse mode NextSpillIno() for gocryptfs.longname.*.name files where a stable
// mapping is not needed.
func (m *InoMap) NextSpillIno() (out uint64) {
if m.spillNext == math.MaxUint64 {
log.Panicf("spillMap overflow: spillNext = 0x%x", m.spillNext)
}
return atomic.AddUint64(&m.spillNext, 1) - 1
}

func (m *InoMap) spill(in QIno) (out uint64) {
spillWarn.Do(func() { tlog.Warn.Printf("InoMap: opening spillMap for %v", in) })
spillWarn.Do(func() { tlog.Warn.Printf("InoMap: opening spillMap for %#v", in) })

out, found := m.spillMap[in]
if found {
return out | spillBit
}
if m.spillNext >= maxSpillIno {
log.Panicf("spillMap overflow: spillNext = 0x%x", m.spillNext)
return out
}
out = m.spillNext
m.spillNext++

out = m.NextSpillIno()
m.spillMap[in] = out
return out | spillBit

return out
}

// Translate maps the passed-in (device, inode) pair to a unique inode number.
// Translate maps the passed-in (device, tag, inode) tuple to a unique inode number.
func (m *InoMap) Translate(in QIno) (out uint64) {
m.Lock()
defer m.Unlock()
13 changes: 13 additions & 0 deletions internal/inomap/inomap_test.go
Original file line number Diff line number Diff line change
@@ -5,6 +5,11 @@ import (
"testing"
)

const (
// bit 63 is used as the spill bit
spillBit = 1 << 63
)

func TestTranslate(t *testing.T) {
m := New(0)
q := QIno{Ino: 1}
@@ -102,13 +107,21 @@ func TestSpill(t *testing.T) {
if out1&spillBit == 0 {
t.Error("spill bit not set")
}
if out1 != spillSpaceStart {
t.Errorf("unexpected first spill inode number %d", out1)
}
out2 := m.Translate(q)
if out2&spillBit == 0 {
t.Error("spill bit not set")
}
if out1 != out2 {
t.Errorf("unstable mapping: %d vs %d", out1, out2)
}
q.Ino = maxPassthruIno + 2
out3 := m.Translate(q)
if out3 != out1+1 {
t.Errorf("unexpected 2nd spill inode number %d", out1)
}
}

// TestUniqueness checks that unique (Dev, Flags, Ino) tuples get unique inode
10 changes: 10 additions & 0 deletions internal/nametransform/names.go
Original file line number Diff line number Diff line change
@@ -4,8 +4,10 @@ package nametransform
import (
"crypto/aes"
"encoding/base64"
"errors"
"math"
"path/filepath"
"strings"
"syscall"

"github.com/rfjakob/eme"
@@ -44,6 +46,7 @@ func New(e *eme.EMECipher, longNames bool, longNameMax uint8, raw64 bool, badnam
if raw64 {
b64 = base64.RawURLEncoding
}
b64 = b64.Strict() // Reject non-zero padding bits
var effectiveLongNameMax int = math.MaxInt32
if longNames {
if longNameMax == 0 {
@@ -81,6 +84,13 @@ func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error
// decryptName decrypts a base64-encoded encrypted filename "cipherName" using the
// initialization vector "iv".
func (n *NameTransform) decryptName(cipherName string, iv []byte) (string, error) {
// From https://pkg.go.dev/encoding/base64#Encoding.Strict :
// > Note that the input is still malleable, as new line characters
// > (CR and LF) are still ignored.
// Check for CR and LF ourselves.
if strings.ContainsAny(cipherName, "\r\n") {
return "", errors.New("characters CR or LF in base64")
}
bin, err := n.B64.DecodeString(cipherName)
if err != nil {
return "", err
3 changes: 1 addition & 2 deletions internal/readpassword/passfile.go
Original file line number Diff line number Diff line change
@@ -46,8 +46,7 @@ func readPassFile(passfile string) ([]byte, error) {
return nil, fmt.Errorf("fatal: passfile: max password length (%d bytes) exceeded", maxPasswordLen)
}
if len(lines) > 1 && len(lines[1]) > 0 {
tlog.Warn.Printf("warning: passfile: ignoring trailing garbage (%d bytes) after first line",
len(lines[1]))
tlog.Warn.Printf("warning: passfile: ignoring trailing garbage after first line")
}
return lines[0], nil
}
9 changes: 8 additions & 1 deletion internal/readpassword/read.go
Original file line number Diff line number Diff line change
@@ -87,7 +87,14 @@ func readPasswordTerminal(prompt string) ([]byte, error) {
// readPasswordStdin reads a line from stdin.
// It exits with a fatal error on read error or empty result.
func readPasswordStdin(prompt string) ([]byte, error) {
tlog.Info.Printf("Reading %s from stdin", prompt)
// This should make debugging situations like
// https://github.com/rfjakob/gocryptfs/issues/852
// easier. Only works on Linux, otherwise shows "?".
target, err := os.Readlink("/proc/self/fd/0")
if err != nil {
target = "?"
}
tlog.Info.Printf("Reading %s from stdin (connected to %q)", prompt, target)
p, err := readLineUnbuffered(os.Stdin)
if err != nil {
return nil, err
7 changes: 4 additions & 3 deletions internal/speed/speed.go
Original file line number Diff line number Diff line change
@@ -31,9 +31,9 @@ func Run() {
if cpu == "" {
cpu = "unknown"
}
aes := "; no AES acceleration"
if stupidgcm.CpuHasAES() {
aes = "; with AES acceleration"
aes := "; no AES-GCM acceleration"
if stupidgcm.HasAESGCMHardwareSupport() {
aes = "; with AES-GCM acceleration"
}
fmt.Printf("cpu: %s%s\n", cpu, aes)

@@ -48,6 +48,7 @@ func Run() {
{name: cryptocore.BackendXChaCha20Poly1305OpenSSL.String(), f: bStupidXchacha, preferred: stupidgcm.PreferOpenSSLXchacha20poly1305()},
{name: cryptocore.BackendXChaCha20Poly1305.String(), f: bXchacha20poly1305, preferred: !stupidgcm.PreferOpenSSLXchacha20poly1305()},
}
testing.Init()
for _, b := range bTable {
fmt.Printf("%-26s\t", b.name)
mbs := mbPerSec(testing.Benchmark(b.f))
28 changes: 28 additions & 0 deletions internal/stupidgcm/cipher_suites.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package stupidgcm

import (
"runtime"

"golang.org/x/sys/cpu"
)

// ********
// Carbon-copied from Go Stdlib
// https://github.com/golang/go/blob/45967bb18e04fa6dc62c2786c87ce120443c64f6/src/crypto/tls/cipher_suites.go#L367
// ********

var (
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
// Keep in sync with crypto/aes/cipher_s390x.go.
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)

hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
)

// ********
// End carbon-copy
// ********
14 changes: 6 additions & 8 deletions internal/stupidgcm/prefer.go
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@ package stupidgcm

import (
"runtime"

"golang.org/x/sys/cpu"
)

// PreferOpenSSLAES256GCM tells us if OpenSSL AES-256-GCM is faster than Go stdlib
@@ -22,7 +20,7 @@ func PreferOpenSSLAES256GCM() bool {
return false
}
// If the CPU has AES acceleration, Go stdlib is faster
if CpuHasAES() {
if HasAESGCMHardwareSupport() {
return false
}
// Otherwise OpenSSL is probably faster
@@ -44,13 +42,13 @@ func PreferOpenSSLXchacha20poly1305() bool {
return true
}

// CpuHasAES tells you if the CPU we are running has AES acceleration that is
// usable by the Go crypto library.
func CpuHasAES() bool {
// Safe to call on other architectures - will just read false.
if cpu.X86.HasAES || cpu.ARM64.HasAES {
// HasAESGCMHardwareSupport tells you if the CPU we are running has AES-GCM
// acceleration that is usable by the Go crypto library.
func HasAESGCMHardwareSupport() bool {
if hasAESGCMHardwareSupport {
return true
}

// On the Apple M1, the CPU has AES acceleration, despite cpu.ARM64.HasAES
// reading false: https://github.com/rfjakob/gocryptfs/issues/556#issuecomment-848079309
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
5 changes: 5 additions & 0 deletions internal/syscallcompat/sys_common.go
Original file line number Diff line number Diff line change
@@ -60,6 +60,11 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
flags |= syscall.O_NOFOLLOW
}
}

// os/exec expects all fds to have O_CLOEXEC or it will leak fds to subprocesses.
// In our case, that would be logger(1), and we did leak fds to it.
flags |= syscall.O_CLOEXEC

fd, err = retryEINTR2(func() (int, error) {
return unix.Openat(dirfd, path, flags, mode)
})
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ func loadConfig(args *argContainer) (masterkey []byte, cf *configfile.ConfFile,
tlog.Fatal.Printf("Masterkey encrypted using FIDO2 token; need to use the --fido2 option.")
return nil, nil, exitcodes.NewErr("", exitcodes.Usage)
}
pw = fido2.Secret(args.fido2, cf.FIDO2.CredentialID, cf.FIDO2.HMACSalt)
pw = fido2.Secret(args.fido2, cf.FIDO2.AssertOptions, cf.FIDO2.CredentialID, cf.FIDO2.HMACSalt)
} else {
pw, err = readpassword.Once([]string(args.extpass), []string(args.passfile), "")
if err != nil {
@@ -83,7 +83,7 @@ func changePassword(args *argContainer) {
os.Exit(exitcodes.Usage)
}
tlog.Info.Println("Please enter your new password.")
newPw, err := readpassword.Twice([]string(args.extpass), []string(args.passfile))
newPw, err := readpassword.Twice(nil, nil)
if err != nil {
tlog.Fatal.Println(err)
os.Exit(exitcodes.ReadPassword)
56 changes: 38 additions & 18 deletions mount.go
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import (
"log"
"log/syslog"
"math"
"net"
"os"
"os/exec"
"os/signal"
@@ -91,16 +90,15 @@ func doMount(args *argContainer) {
// We must use an absolute path because we cd to / when daemonizing.
// This messes up the delete-on-close logic in the unix socket object.
args.ctlsock, _ = filepath.Abs(args.ctlsock)
var sock net.Listener
sock, err = net.Listen("unix", args.ctlsock)

args._ctlsockFd, err = ctlsocksrv.Listen(args.ctlsock)
if err != nil {
tlog.Fatal.Printf("ctlsock: %v", err)
os.Exit(exitcodes.CtlSock)
}
args._ctlsockFd = sock
// Close also deletes the socket file
defer func() {
err = sock.Close()
err = args._ctlsockFd.Close()
if err != nil {
tlog.Warn.Printf("ctlsock close: %v", err)
}
@@ -353,6 +351,10 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
return rootNode, func() { cCore.Wipe() }
}

type RootInoer interface {
RootIno() uint64
}

// initGoFuse calls into go-fuse to mount `rootNode` on `args.mountpoint`.
// The mountpoint is ready to use when the functions returns.
// On error, it calls os.Exit and does not return.
@@ -377,6 +379,9 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server {
}
}
fuseOpts.NullPermissions = true
// The inode number for the root node must be manually set on mount
// https://github.com/hanwen/go-fuse/issues/399
fuseOpts.RootStableAttr = &fs.StableAttr{Ino: rootNode.(RootInoer).RootIno()}
// Enable go-fuse warnings
fuseOpts.Logger = log.New(os.Stderr, "go-fuse: ", log.Lmicroseconds)
fuseOpts.MountOptions = fuse.MountOptions{
@@ -402,20 +407,21 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server {
}

mOpts := &fuseOpts.MountOptions
opts := make(map[string]string)
if args.allow_other {
tlog.Info.Printf(tlog.ColorYellow + "The option \"-allow_other\" is set. Make sure the file " +
"permissions protect your data from unwanted access." + tlog.ColorReset)
mOpts.AllowOther = true
// Make the kernel check the file permissions for us
mOpts.Options = append(mOpts.Options, "default_permissions")
opts["default_permissions"] = ""
}
if args.acl {
mOpts.EnableAcl = true
}
// fusermount from libfuse 3.x removed the "nonempty" option and exits
// with an error if it sees it. Only add it to the options on libfuse 2.x.
if args.nonempty && haveFusermount2() {
mOpts.Options = append(mOpts.Options, "nonempty")
opts["nonempty"] = ""
}
// Set values shown in "df -T" and friends
// First column, "Filesystem"
@@ -437,40 +443,54 @@ func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server {
// Add a volume name if running osxfuse. Otherwise the Finder will show it as
// something like "osxfuse Volume 0 (gocryptfs)".
if runtime.GOOS == "darwin" {
volname := strings.Replace(path.Base(args.mountpoint), ",", "_", -1)
mOpts.Options = append(mOpts.Options, "volname="+volname)
opts["volname"] = strings.Replace(path.Base(args.mountpoint), ",", "_", -1)
}
// The kernel enforces read-only operation, we just have to pass "ro".
// Reverse mounts are always read-only.
if args.ro || args.reverse {
mOpts.Options = append(mOpts.Options, "ro")
opts["ro"] = ""
} else if args.rw {
mOpts.Options = append(mOpts.Options, "rw")
opts["rw"] = ""
}
// If both "nosuid" & "suid", "nodev" & "dev", etc were passed, the safer
// option wins.
if args.nosuid {
mOpts.Options = append(mOpts.Options, "nosuid")
opts["nosuid"] = ""
} else if args.suid {
mOpts.Options = append(mOpts.Options, "suid")
opts["suid"] = ""
}
if args.nodev {
mOpts.Options = append(mOpts.Options, "nodev")
opts["nodev"] = ""
} else if args.dev {
mOpts.Options = append(mOpts.Options, "dev")
opts["dev"] = ""
}
if args.noexec {
mOpts.Options = append(mOpts.Options, "noexec")
opts["noexec"] = ""
} else if args.exec {
mOpts.Options = append(mOpts.Options, "exec")
opts["exec"] = ""
}
// Add additional mount options (if any) after the stock ones, so the user has
// a chance to override them.
if args.ko != "" {
parts := strings.Split(args.ko, ",")
tlog.Debug.Printf("Adding -ko mount options: %v", parts)
mOpts.Options = append(mOpts.Options, parts...)
for _, part := range parts {
kv := strings.SplitN(part, "=", 2)
if len(kv) == 2 {
opts[kv[0]] = kv[1]
} else {
opts[kv[0]] = ""
}
}
}
for k, v := range opts {
if v == "" {
mOpts.Options = append(mOpts.Options, k)
} else {
mOpts.Options = append(mOpts.Options, k+"="+v)
}
}

srv, err := fs.Mount(args.mountpoint, rootNode, fuseOpts)
if err != nil {
tlog.Fatal.Printf("fs.Mount failed: %s", strings.TrimSpace(err.Error()))
6 changes: 6 additions & 0 deletions test.bash
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#!/bin/bash
#
# test.bash runs the gocryptfs test suite against $TMPDIR,
# or, if unset, /var/tmp.

set -eu

@@ -72,6 +75,9 @@ else
echo "shellcheck not installed - skipping"
fi

echo -n "Testing on TMPDIR=$TMPDIR, filesystem: "
findmnt --noheadings --target "$TESTDIR" --output FSTYPE,OPTIONS || true

EXTRA_ARGS=""
if [[ $VERBOSE -eq 1 ]]; then
# Disabling parallelism disables per-package output buffering, hence enabling
88 changes: 82 additions & 6 deletions tests/cli/cli_test.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,9 @@
package cli

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -97,29 +100,54 @@ func TestInitReverse(t *testing.T) {
}
}

// Test -init with -masterkey
func TestInitMasterkey(t *testing.T) {
var testMk = make([]byte, 32)
dir := test_helpers.InitFS(t, fmt.Sprintf("-masterkey=%s", hex.EncodeToString(testMk)))
m, _, err := configfile.LoadAndDecrypt(dir+"/"+configfile.ConfDefaultName, testPw)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(testMk, m) {
t.Error("masterkey does not match")
}
}

// testPasswd changes the password from "test" to "test" using
// the -extpass method, then from "test" to "newpasswd" using the
// stdin method.
func testPasswd(t *testing.T, dir string, extraArgs ...string) {
// Change password using "-extpass"
// Change password #1: old passwd via "-extpass", new one via stdin
args := []string{"-q", "-passwd", "-extpass", "echo test"}
args = append(args, extraArgs...)
args = append(args, dir)
cmd := exec.Command(test_helpers.GocryptfsBinary, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
p, err := cmd.StdinPipe()
if err != nil {
t.Fatal(err)
}
err = cmd.Start()
if err != nil {
t.Error(err)
}
// Change password using stdin
// New password through stdin
p.Write([]byte("test\n"))
p.Close()
err = cmd.Wait()
if err != nil {
t.Error(err)
}

// Change password #2: using stdin
args = []string{"-q", "-passwd"}
args = append(args, extraArgs...)
args = append(args, dir)
cmd = exec.Command(test_helpers.GocryptfsBinary, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
p, err := cmd.StdinPipe()
p, err = cmd.StdinPipe()
if err != nil {
t.Fatal(err)
}
@@ -320,10 +348,22 @@ func TestInitConfig(t *testing.T) {
"-config", config, dir)
cmd2.Stdout = os.Stdout
cmd2.Stderr = os.Stderr
err = cmd2.Run()
p, err := cmd2.StdinPipe()
if err != nil {
t.Fatal(err)
}
err = cmd2.Start()
if err != nil {
t.Error(err)
}
// New password
p.Write([]byte("passwd\n"))
p.Close()
err = cmd2.Wait()
if err != nil {
t.Error(err)
}

}

// Test -ro
@@ -407,7 +447,7 @@ func TestMountPasswordIncorrect(t *testing.T) {
err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo WRONG", "-wpanic=false", "-ctlsock", ctlSock)
exitCode := test_helpers.ExtractCmdExitCode(err)
if exitCode != exitcodes.PasswordIncorrect {
t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode)
t.Errorf("wrong exit code: want=%d, have=%d", exitcodes.PasswordIncorrect, exitCode)
}
if _, err := os.Stat(ctlSock); err == nil {
t.Errorf("socket file %q left behind", ctlSock)
@@ -1018,3 +1058,39 @@ func TestMountCreat(t *testing.T) {
test_helpers.UnmountPanic(mnt)
}
}

// https://github.com/rfjakob/gocryptfs/issues/776
func TestOrphanedSocket(t *testing.T) {
cDir := test_helpers.InitFS(t)
ctlSock := cDir + ".sock"
mnt := cDir + ".mnt"
test_helpers.MountOrFatal(t, cDir, mnt, "-extpass", "echo test", "-wpanic=false", "-ctlsock", ctlSock)

mnt2 := cDir + ".mnt2"
err := test_helpers.Mount(cDir, mnt2, false, "-extpass", "echo test", "-wpanic=false", "-ctlsock", ctlSock)
exitCode := test_helpers.ExtractCmdExitCode(err)
if exitCode != exitcodes.CtlSock {
t.Errorf("wrong exit code: want=%d, have=%d", exitcodes.CtlSock, exitCode)
}
test_helpers.UnmountPanic(mnt)

// Unmount returns before the gocryptfs process has terminated and before the
// socket file has been deleted. Wait out the deletion.
for i := 0; i < 100; i++ {
_, err := os.Stat(ctlSock)
if errors.Is(err, os.ErrNotExist) {
break
}
time.Sleep(time.Millisecond)
}

// Create orphaned socket file
err = syscall.Mknod(ctlSock, syscall.S_IFSOCK|0666, 0)
if err != nil {
t.Fatal(err)
}

// Should delete the socket file automatically and the mount should work
test_helpers.MountOrFatal(t, cDir, mnt, "-extpass", "echo test", "-wpanic=false", "-ctlsock", ctlSock)
test_helpers.UnmountPanic(mnt)
}
2 changes: 1 addition & 1 deletion tests/dl-linux-tarball.bash
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ fi
if [[ $SIZE_ACTUAL -ne $SIZE_WANT ]]; then
echo "Downloading linux-3.0.tar.gz"
if command -v wget > /dev/null ; then
wget -nv --show-progress -c -O "$TGZ" "$URL"
wget -nv -c -O "$TGZ" "$URL"
else
curl -o "$TGZ" "$URL"
fi
11 changes: 11 additions & 0 deletions tests/fsck/fsck_test.go
Original file line number Diff line number Diff line change
@@ -49,6 +49,17 @@ func TestBrokenFsV14(t *testing.T) {
}
}

func TestMalleableBase64(t *testing.T) {
cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", "malleable_base64")
outBin, err := cmd.CombinedOutput()
out := string(outBin)
t.Log(out)
code := test_helpers.ExtractCmdExitCode(err)
if code != exitcodes.FsckErrors {
t.Errorf("wrong exit code, have=%d want=%d", code, exitcodes.FsckErrors)
}
}

func TestExampleFses(t *testing.T) {
dirfd, err := os.Open("../example_filesystems")
if err != nil {
Empty file.
Empty file.
Empty file.
Empty file.
20 changes: 20 additions & 0 deletions tests/fsck/malleable_base64/gocryptfs.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"Creator": "gocryptfs v2.4.0-dirty",
"EncryptedKey": "3RHFBE1hurYP6D4VsdqhQb3Bd/kd3vYsZohgpc2lDhwIM8lsk8FHOgZEtfG2HfEFsV374B6rktbXmrBBiperqw==",
"ScryptObject": {
"Salt": "9pQrdZWjpp1cTjuWNB/SqcLw+HOYeOTECga2yIpjjUU=",
"N": 1024,
"R": 8,
"P": 1,
"KeyLen": 32
},
"Version": 2,
"FeatureFlags": [
"HKDF",
"GCMIV128",
"DirIV",
"EMENames",
"LongNames",
"Raw64"
]
}
1 change: 1 addition & 0 deletions tests/fsck/malleable_base64/gocryptfs.diriv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cTh{½ÚEfDbúüÕ«Û
10 changes: 10 additions & 0 deletions tests/matrix/matrix_test.go
Original file line number Diff line number Diff line change
@@ -972,3 +972,13 @@ func TestPwd(t *testing.T) {
os.Mkdir(dir, 0700)
}
}

// TestRootIno checks that inode number of the root dir is set
// https://github.com/hanwen/go-fuse/issues/399
func TestRootIno(t *testing.T) {
var st syscall.Stat_t
syscall.Stat(test_helpers.DefaultPlainDir, &st)
if st.Ino == 0 {
t.Errorf("inode number of root dir is zero")
}
}
10 changes: 10 additions & 0 deletions tests/plaintextnames/plaintextnames_test.go
Original file line number Diff line number Diff line change
@@ -125,3 +125,13 @@ func TestInoReuseEvil(t *testing.T) {
t.Logf("file ino = %d", st.Ino)
}
}

// TestRootIno checks that inode number of the root dir is set
// https://github.com/hanwen/go-fuse/issues/399
func TestRootIno(t *testing.T) {
var st syscall.Stat_t
syscall.Stat(cDir, &st)
if st.Ino == 0 {
t.Errorf("inode number of root dir is zero")
}
}
59 changes: 59 additions & 0 deletions tests/reverse/correctness_test.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"syscall"
"testing"

@@ -293,3 +294,61 @@ func TestSeekData(t *testing.T) {
}
f.Close()
}

// gocryptfs.longname.*.name of hardlinked files should not appear hardlinked (as the
// contents are different).
//
// This means that
// 1) They have a different NodeID, hence the kernel knows it's different files
// 2) They have a different inode number, hence userspace knows they are not hard-linked.
//
// https://github.com/rfjakob/gocryptfs/issues/802
func TestHardlinkedLongname(t *testing.T) {
if plaintextnames {
t.Skip()
}

workdir := dirA + "/" + t.Name()
if err := os.Mkdir(workdir, 0700); err != nil {
t.Fatal(err)
}
long1 := workdir + "/" + 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 {
t.Fatal(err)
}

// Find workdir in encrypted view
var st syscall.Stat_t
if err := syscall.Stat(workdir, &st); 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")
if err != nil {
t.Fatal(err)
}
if len(matches) != 2 {
t.Fatalf("BUG: only %d matches, want 2", len(matches))
}
if test_helpers.Md5fn(matches[0]) == test_helpers.Md5fn(matches[1]) {
t.Errorf("Files %q are identical - that's wrong!", matches)
}

var st0 syscall.Stat_t
if err := syscall.Stat(matches[0], &st0); err != nil {
t.Fatal(err)
}
var st1 syscall.Stat_t
if err := syscall.Stat(matches[1], &st1); err != nil {
t.Fatal(err)
}
if st0.Ino == st1.Ino {
t.Errorf("Files %q have the same inode number - that's wrong!", matches)
}
}
50 changes: 50 additions & 0 deletions tests/reverse/force_owner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package reverse_test

import (
"io/ioutil"
"net/url"
"os"
"syscall"
"testing"

"github.com/rfjakob/gocryptfs/v2/tests/test_helpers"
)

func TestForceOwner(t *testing.T) {
// Let's not explode with "TempDir: pattern contains path separator"
myEscapedName := url.PathEscape(t.Name())
mnt, err := ioutil.TempDir(test_helpers.TmpDir, myEscapedName)
if err != nil {
t.Fatal(err)
}
cliArgs := []string{"-reverse", "-zerokey", "-force_owner=1234:1234"}
if plaintextnames {
cliArgs = append(cliArgs, "-plaintextnames")
} else if deterministic_names {
cliArgs = append(cliArgs, "-deterministic-names")
}
test_helpers.MountOrFatal(t, "/", mnt, cliArgs...)
defer test_helpers.UnmountErr(mnt)

entries, err := os.ReadDir(mnt)
if err != nil {
t.Fatal(err)
}

// Check the mountpoint and everything inside it
toCheck := []string{mnt}
for _, e := range entries {
toCheck = append(toCheck, mnt+"/"+e.Name())
}

var st syscall.Stat_t
for _, path := range toCheck {
if err := syscall.Lstat(path, &st); err != nil {
t.Fatal(err)
}
if st.Uid != 1234 || st.Gid != 1234 {
t.Errorf("file %q: uid or gid != 1234: %#v", path, st)
}
}

}
6 changes: 4 additions & 2 deletions tests/reverse/inomap_test.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package reverse_test

import (
"bytes"
"log"
"os"
"strings"
"syscall"
@@ -30,6 +31,7 @@ func findIno(dir string, ino uint64) string {
return entry
}
}
log.Panicf("ino %d not found", ino)
return ""
}

@@ -141,7 +143,7 @@ func TestVirtualFileIno(t *testing.T) {
if origInos.child == cipherInos.name {
t.Errorf("name ino collision: %d == %d", origInos.child, cipherInos.name)
}
if origInos.child&mask != cipherInos.name&mask {
t.Errorf("name ino mismatch: %#x vs %#x", origInos.child, cipherInos.name)
if cipherInos.name < 1<<63 {
t.Errorf("name ino should be in spill space, but is actually %#x", cipherInos.name)
}
}
7 changes: 4 additions & 3 deletions tests/test_helpers/mount_unmount.go
Original file line number Diff line number Diff line change
@@ -242,11 +242,12 @@ func ListFds(pid int, prefix string) []string {
// fd was closed in the meantime
continue
}
if strings.HasPrefix(target, "pipe:") || strings.HasPrefix(target, "anon_inode:[eventpoll]") {
if strings.HasPrefix(target, "pipe:") || strings.HasPrefix(target, "anon_inode:[eventpoll]") ||
strings.HasPrefix(target, "anon_inode:[pidfd]") {
// The Go runtime creates pipes on demand for splice(), which
// creates spurious test failures. Ignore all pipes.
// Also get rid of the "eventpoll" fd that is always there and not
// interesting.
// Also get rid of the "eventpoll" and "pidfd" fds that are always there
// and not interesting.
filtered = append(filtered, target)
continue
}