From 847d4fa7817c84fe1b78726f031172ea508dab19 Mon Sep 17 00:00:00 2001 From: Hardcore Sushi Date: Tue, 8 Jun 2021 21:25:50 +0200 Subject: [PATCH] Genesis patch --- .gitignore | 20 +- .travis.yml | 43 - Documentation/.gitignore | 2 - Documentation/CLI_ABI.md | 102 - Documentation/MANPAGE-STATFS.md | 82 - Documentation/MANPAGE-XRAY.md | 64 - Documentation/MANPAGE-render.bash | 18 - Documentation/MANPAGE.md | 619 - Documentation/SECURITY.md | 1 - Documentation/XFSTESTS.md | 699 - Documentation/duplicate-inodes.txt | 36 - Documentation/extractloop.md | 56 - Documentation/extractloop_plot_csv.png | Bin 46953 -> 0 bytes Documentation/file-format.md | 39 - Documentation/folders-side-by-side.gif | Bin 53966 -> 0 bytes Documentation/gocryptfs-logo.png | Bin 7332 -> 0 bytes Documentation/performance-reverse.txt | 10 - Documentation/performance.txt | 86 - Makefile | 25 - README.md | 704 +- allocator/allocator32.go | 13 + allocator/allocator64.go | 12 + benchmark-reverse.bash | 50 - benchmark.bash | 103 - build-without-openssl.bash | 10 - build.bash | 104 - build.sh | 72 + cli_args.go | 332 - cli_args_test.go | 90 - codelingo.yaml | 4 - common_ops.go | 88 + contrib/atomicrename/.gitignore | 1 - contrib/atomicrename/main.go | 101 - contrib/cleanup-tmp-mounts.sh | 15 - contrib/findholes/.gitignore | 1 - contrib/findholes/holes/holes.go | 199 - contrib/findholes/main.go | 57 - contrib/getdents-debug/getdents/.gitignore | 1 - contrib/getdents-debug/getdents/getdents.go | 88 - contrib/getdents-debug/getdents_c/.gitignore | 1 - contrib/getdents-debug/getdents_c/Makefile | 2 - contrib/getdents-debug/getdents_c/getdents.c | 53 - .../getdents-debug/readdirnames/.gitignore | 1 - .../readdirnames/readdirnames.go | 56 - contrib/gocryptfs-maybe.bash | 28 - contrib/mount-ext4-ramdisk.sh | 20 - contrib/statfs/.gitignore | 1 - contrib/statfs/statfs.go | 35 - contrib/statvsfstat/.gitignore | 1 - contrib/statvsfstat/statvsfstat.go | 53 - crossbuild.bash | 31 - ctlsock/ctlsock.go | 61 - ctlsock/json_abi.go | 26 - daemonize.go | 106 - directory.go | 242 + file.go | 488 + fsck.go | 346 - go.mod | 19 - go.sum | 48 - gocryptfs-xray/.gitignore | 1 - gocryptfs-xray/paths_ctlsock.go | 62 - gocryptfs-xray/xray_main.go | 205 - .../xray_tests/aesgcm_fs.masterkey.txt | 4 - gocryptfs-xray/xray_tests/aesgcm_fs.xray.txt | 3 - .../aesgcm_fs/VnvoeSetPaOFjZDaZAh0lA | Bin 5082 -> 0 bytes .../xray_tests/aesgcm_fs/gocryptfs.conf | 20 - .../xray_tests/aesgcm_fs/gocryptfs.diriv | 1 - .../xray_tests/aessiv_fs.masterkey.txt | 4 - gocryptfs-xray/xray_tests/aessiv_fs.xray.txt | 3 - .../xray_tests/aessiv_fs/gocryptfs.conf | 21 - .../xray_tests/aessiv_fs/gocryptfs.diriv | 1 - .../aessiv_fs/klepPXQJIaEDaIx-yurAqQ | Bin 5082 -> 0 bytes gocryptfs-xray/xray_tests/xray_test.go | 110 - golint.bash | 17 - help.go | 56 - helpers.go | 183 + info.go | 44 - init_dir.go | 134 - internal/configfile/config_file.go | 89 +- internal/configfile/config_test.go | 147 - internal/configfile/config_test/.gitignore | 1 - .../config_test/PlaintextNames.conf | 16 - .../config_test/StrangeFeature.conf | 19 - internal/configfile/config_test/v1.conf | 11 - internal/configfile/config_test/v2.conf | 18 - internal/configfile/scrypt.go | 10 +- internal/configfile/scrypt_test.go | 60 - internal/contentenc/content.go | 28 +- internal/contentenc/content_test.go | 96 - internal/contentenc/file_header.go | 2 +- internal/contentenc/offsets.go | 6 - internal/contentenc/offsets_test.go | 53 - internal/cryptocore/cryptocore.go | 8 +- internal/cryptocore/cryptocore_test.go | 41 - internal/cryptocore/hkdf_test.go | 46 - internal/cryptocore/randprefetch_test.go | 48 - internal/cryptocore/randsize_test.go | 40 - internal/ctlsocksrv/ctlsock_serve.go | 163 - internal/ctlsocksrv/sanitize.go | 32 - internal/ctlsocksrv/sanitize_test.go | 30 - internal/ensurefds012/ensurefds012.go | 52 - internal/fido2/fido2.go | 110 - internal/fusefrontend/args.go | 52 - internal/fusefrontend/ctlsock_interface.go | 105 - internal/fusefrontend/dircache.go | 180 - internal/fusefrontend/file.go | 457 - .../fusefrontend/file_allocate_truncate.go | 218 - internal/fusefrontend/file_api_check.go | 22 - internal/fusefrontend/file_holes.go | 128 - internal/fusefrontend/file_setattr.go | 85 - internal/fusefrontend/node.go | 449 - internal/fusefrontend/node_api_check.go | 31 - internal/fusefrontend/node_dir_ops.go | 376 - internal/fusefrontend/node_helpers.go | 177 - internal/fusefrontend/node_open_create.go | 108 - internal/fusefrontend/node_xattr.go | 170 - internal/fusefrontend/node_xattr_darwin.go | 107 - internal/fusefrontend/node_xattr_linux.go | 70 - internal/fusefrontend/openbackingdir_test.go | 170 - internal/fusefrontend/root_node.go | 335 - internal/fusefrontend/xattr_unit_test.go | 45 - .../fusefrontend_reverse/ctlsock_interface.go | 42 - internal/fusefrontend_reverse/excluder.go | 63 - .../fusefrontend_reverse/excluder_test.go | 75 - internal/fusefrontend_reverse/file.go | 81 - .../fusefrontend_reverse/file_api_check.go | 25 - internal/fusefrontend_reverse/file_helpers.go | 62 - internal/fusefrontend_reverse/mocks_test.go | 32 - internal/fusefrontend_reverse/node.go | 188 - .../fusefrontend_reverse/node_api_check.go | 40 - internal/fusefrontend_reverse/node_dir_ops.go | 114 - internal/fusefrontend_reverse/node_helpers.go | 231 - internal/fusefrontend_reverse/root_node.go | 120 - internal/fusefrontend_reverse/rpath.go | 123 - internal/fusefrontend_reverse/virtualconf.go | 55 - internal/fusefrontend_reverse/virtualnode.go | 117 - internal/inomap/inomap.go | 117 - internal/inomap/inomap_test.go | 160 - internal/inomap/qino.go | 43 - internal/nametransform/diriv.go | 10 +- internal/nametransform/longnames.go | 13 +- internal/nametransform/longnames_test.go | 30 - internal/nametransform/names.go | 7 - internal/nametransform/names_test.go | 77 - internal/openfiletable/open_file_table.go | 103 - internal/pathiv/pathiv.go | 2 +- internal/readpassword/extpass_test.go | 85 - internal/readpassword/passfile.go | 53 - internal/readpassword/passfile_test.go | 102 - .../passfile_test_files/empty.txt | 0 .../passfile_test_files/empty_first_line.txt | 2 - .../passfile_test_files/file with spaces.txt | 1 - .../passfile_test_files/mypassword.txt | 1 - .../mypassword_garbage.txt | 2 - .../mypassword_missing_newline.txt | 1 - .../passfile_test_files/newline.txt | 1 - internal/readpassword/read.go | 159 - internal/readpassword/stdin_test.go | 100 - internal/serialize_reads/sr.go | 150 - internal/siv_aead/benchmark.bash | 7 - internal/siv_aead/correctness_test.go | 148 - internal/siv_aead/performance_test.go | 1 - internal/speed/benchmark.bash | 7 - internal/speed/speed.go | 148 - internal/speed/speed_test.go | 29 - internal/stupidgcm/stupidgcm_test.go | 195 - internal/stupidgcm/without_openssl.go | 2 +- internal/syscallcompat/emulate.go | 29 - internal/syscallcompat/emulate_test.go | 28 - internal/syscallcompat/getdents_linux.go | 37 +- internal/syscallcompat/getdents_other.go | 61 - internal/syscallcompat/getdents_test.go | 133 - internal/syscallcompat/helpers.go | 21 - internal/syscallcompat/main_test.go | 48 - internal/syscallcompat/open_nofollow.go | 4 - internal/syscallcompat/open_nofollow_test.go | 48 - internal/syscallcompat/sys_common.go | 13 +- internal/syscallcompat/sys_common_test.go | 327 - internal/syscallcompat/sys_darwin.go | 230 - internal/syscallcompat/sys_linux.go | 230 +- internal/syscallcompat/unix2syscall_darwin.go | 26 - internal/tlog/log.go | 201 - internal/tlog/tlog_test.go | 26 - main.go | 334 +- masterkey.go | 60 - mount.go | 531 - package-release-tarballs.bash | 97 - profiling.go | 95 - profiling/ls.bash | 39 - profiling/streaming-read.bash | 29 - profiling/streaming-write.bash | 22 - profiling/tar-extract.bash | 23 - profiling/write-trace.bash | 23 - race.go | 8 - sendusr1.go | 22 - test-without-openssl.bash | 5 - test.bash | 93 - tests/canonical-benchmarks.bash | 50 - tests/cli/cli_test.go | 888 - tests/cli/gocryptfs.conf.b9e5ba23 | 18 - tests/defaults/1980.tar.gz | Bin 115 -> 0 bytes tests/defaults/acl_test.go | 202 - tests/defaults/ctlsock_test.go | 98 - tests/defaults/diriv_test.go | 74 - tests/defaults/main_test.go | 372 - tests/defaults/performance_test.go | 131 - tests/dl-linux-tarball.bash | 27 - tests/example_filesystems/content/abs | 1 - ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 1 - tests/example_filesystems/content/rel | 1 - tests/example_filesystems/content/status.txt | 1 - .../example_filesystems_test.go | 384 - .../example_test_helpers.go | 80 - .../v0.4/3-HZSwv99agoWgTErV0YFQ== | Bin 56 -> 0 bytes .../v0.4/6hL2fPVB2aMSh4-UoDn5Kw== | 1 - .../v0.4/TBIgdfhDKwkXVTnWLVzFSg== | 1 - tests/example_filesystems/v0.4/gocryptfs.conf | 12 - .../v0.5/AOtl_i4xQWDyG0_zUqntOw== | 1 - .../v0.5/Pf35wlWlf43N68EbhIgTcQ== | 1 - tests/example_filesystems/v0.5/gocryptfs.conf | 14 - .../example_filesystems/v0.5/gocryptfs.diriv | 1 - .../v0.5/j2BpGUT5kOtia20PWQ2rEA== | Bin 56 -> 0 bytes .../v0.6-plaintextnames/abs | 1 - .../v0.6-plaintextnames/gocryptfs.conf | 14 - .../v0.6-plaintextnames/rel | 1 - .../v0.6-plaintextnames/status.txt | Bin 56 -> 0 bytes .../v0.6/9pOs0yjJI5A67pv5CnqomQ== | 1 - .../v0.6/G79Zdu41H3bgwdaQlrz-dg== | 1 - .../v0.6/RuYvQG_raW_-H_LcyJC4LQ== | Bin 56 -> 0 bytes tests/example_filesystems/v0.6/gocryptfs.conf | 15 - .../example_filesystems/v0.6/gocryptfs.diriv | 1 - .../v0.7-plaintextnames/abs | 1 - .../v0.7-plaintextnames/gocryptfs.conf | 15 - .../v0.7-plaintextnames/rel | 1 - .../v0.7-plaintextnames/status.txt | Bin 60 -> 0 bytes .../v0.7/4tRF1LdULRFLiXwfze671Q== | 1 - .../v0.7/RWPXmXkRFrWw1aOpq7C-NQ== | Bin 60 -> 0 bytes .../v0.7/dwPcZNei4HN4qPA6FxoG_A== | 1 - tests/example_filesystems/v0.7/gocryptfs.conf | 16 - .../example_filesystems/v0.7/gocryptfs.diriv | 1 - .../v0.9/00msNUi5h5aKMX_f-4pBhA== | 1 - .../v0.9/R83PhW-BBA_q4rPYD7dEMg== | Bin 60 -> 0 bytes tests/example_filesystems/v0.9/gocryptfs.conf | 17 - .../example_filesystems/v0.9/gocryptfs.diriv | 1 - ...J-w3LBX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ= | Bin 60 -> 0 bytes ...BX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ=.name | 1 - .../v0.9/hwE1RKIXtF8hmQMvEXSTtg== | 1 - .../v1.1-aessiv/MA0FDdmnXpmPJtS_AcAbqQ== | Bin 60 -> 0 bytes .../v1.1-aessiv/Sjl6QXHm2IjuKwaKgJ5jig== | 1 - .../v1.1-aessiv/gocryptfs.conf | 19 - .../v1.1-aessiv/gocryptfs.diriv | 1 - ...hdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk= | Bin 60 -> 0 bytes ...5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk=.name | 1 - .../v1.1-aessiv/jCGXyJJqu4sdxRLsDQNEtA== | 1 - .../.gocryptfs.reverse.conf | 17 - .../v1.1-reverse-plaintextnames/abs | 1 - ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 1 - .../v1.1-reverse-plaintextnames/rel | 1 - .../v1.1-reverse-plaintextnames/status.txt | 1 - .../v1.1-reverse/.gocryptfs.reverse.conf | 19 - tests/example_filesystems/v1.1-reverse/abs | 1 - ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 1 - tests/example_filesystems/v1.1-reverse/rel | 1 - .../v1.1-reverse/status.txt | 1 - .../v1.3-reverse/.gocryptfs.reverse.conf | 21 - tests/example_filesystems/v1.3-reverse/abs | 1 - .../v1.3-reverse/dir1/dir2/file | Bin 10000 -> 0 bytes ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 1 - tests/example_filesystems/v1.3-reverse/rel | 1 - .../v1.3-reverse/status.txt | 1 - tests/example_filesystems/v1.3/gocryptfs.conf | 20 - .../example_filesystems/v1.3/gocryptfs.diriv | 2 - ...hUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs | Bin 60 -> 0 bytes ...9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs.name | 1 - .../v1.3/gv65k_g2NQyBsSv_5dzMVQ | 1 - .../v1.3/mGj2_hdnHe34Sp0iIQUwuw | Bin 60 -> 0 bytes .../v1.3/uD4PVrDBY5y2k_qLKNOFvA | 1 - .../broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q | 0 ...MyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A | 0 ...f-68icxbQ-TuvmnWHuItB1BeLB92dNCXMXiz2M-zPI | Bin 18 -> 0 bytes .../broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw | 1 - .../mWEr9JLch2FW40qhbnPgpg | 0 .../broken_fs_v1.4/OtrNpznB8aMTKPi6bopM2g | Bin 63 -> 0 bytes .../_y58usbKXq_YRPMKfC3TNw | 0 .../PnkpLqHimGudw4C3jFY-Yw/gocryptfs.diriv | 2 - ...5DjvW5BXlGl1yCIJn4lPgdjdMvW_LUfc7G-R8W1cZ0 | Bin 8 -> 0 bytes ...00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ | 0 ...jZ3FX0zlFTpSfv-FBJ2u3ojwSN1XSqpNpCHxa5VGWw | Bin 1 -> 0 bytes tests/fsck/broken_fs_v1.4/gocryptfs.conf | 20 - tests/fsck/broken_fs_v1.4/gocryptfs.diriv | 2 - ...I0MtUdzELPeOAZYwYZFee169hpGgd3l2PXQBcc9sl4 | 1 - tests/fsck/broken_fs_v1.4/invalid_file_name.3 | 0 tests/fsck/broken_fs_v1.4/invalid_file_name_2 | 0 .../broken_fs_v1.4/invalid_file_name____1 | 0 .../broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A | 1 - .../broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA | 1 - .../trqecbMNXdzLqzpk7fSfKw/gocryptfs.diriv | 1 - .../broken_fs_v1.4/vDKs8a7UtM3PmEKk9wlPcA | Bin 77 -> 0 bytes .../yrwcjj2qoC4IYvhw9sbfRg/gocryptfs.diriv | 1 - .../uC2yqKyQUXSJF-YF1Ya5nQ | 0 tests/fsck/fsck_test.go | 128 - tests/fsck/run_fsck.bash | 2 - tests/fuse-unmount.bash | 27 - .../hkdf_sanity/broken_content/gocryptfs.conf | 17 - tests/hkdf_sanity/broken_content/status.txt | Bin 60 -> 0 bytes .../broken_names/L3yg-cJYAInDGg4TcjXrnw | Bin 60 -> 0 bytes tests/hkdf_sanity/broken_names/gocryptfs.conf | 20 - .../hkdf_sanity/broken_names/gocryptfs.diriv | 1 - tests/hkdf_sanity/sanity_test.go | 34 - tests/len2elen.sh | 29 - tests/matrix/atime_darwin.go | 9 - tests/matrix/atime_linux.go | 9 - tests/matrix/concurrency_test.go | 186 - tests/matrix/dir_test.go | 72 - tests/matrix/fallocate_test.go | 175 - tests/matrix/matrix_test.go | 903 - tests/maxlen.bash | 18 - tests/plaintextnames/file_holes_test.go | 153 - tests/plaintextnames/plaintextnames_test.go | 127 - tests/reverse/correctness_test.go | 297 - .../.gocryptfs.reverse.conf | 19 - .../reverse/ctlsock_reverse_test_fs/a_symlink | 1 - .../ctlsock_reverse_test_fs/dir/dir/file | 0 .../reverse/ctlsock_reverse_test_fs/dir/file | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 tests/reverse/ctlsock_reverse_test_fs/file | 0 .../file | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 tests/reverse/ctlsock_test.go | 88 - tests/reverse/exclude_test.go | 157 - .../exclude_test_fs/.gocryptfs.reverse.conf | 21 - .../.gocryptfs.reverse.conf.plaintextnames | 18 - tests/reverse/exclude_test_fs/bkp1~ | 0 tests/reverse/exclude_test_fs/dir1/exclude | 0 tests/reverse/exclude_test_fs/dir1/file1 | 0 tests/reverse/exclude_test_fs/dir1/file2 | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx~ | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 .../exclude_test_fs/dir1/subdir1/exclude | 0 .../dir1/subdir1/subdir2/exclude | 0 tests/reverse/exclude_test_fs/dir2/file | 0 .../file | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 .../reverse/exclude_test_fs/dir2/subdir/file | 0 tests/reverse/exclude_test_fs/file1 | 0 tests/reverse/exclude_test_fs/file2 | 0 .../file1 | 0 .../bkp~ | 0 .../file | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 ...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | 0 tests/reverse/inomap_test.go | 145 - tests/reverse/linux-tarball-test.bash | 33 - tests/reverse/longname_perf_test.go | 50 - tests/reverse/main_test.go | 56 - tests/reverse/xattr_test.go | 66 - tests/root_test/root_test.go | 313 - tests/sharedstorage/sharedstorage_test.go | 142 - tests/sshfs-benchmark.bash | 91 - tests/stress_tests/extractloop.bash | 135 - tests/stress_tests/extractloop_plot_csv.m | 15 - tests/stress_tests/fsstress-encfs.bash | 1 - tests/stress_tests/fsstress-gocryptfs.bash | 112 - tests/stress_tests/fsstress-loopback.bash | 1 - .../stress_tests/fsstress.collect-crashes.sh | 21 - tests/stress_tests/linux-3.0.md5sums | 36782 ---------------- tests/stress_tests/parallel_cp.sh | 61 - tests/stress_tests/pingpong-rsync.bash | 1 - tests/stress_tests/pingpong.bash | 69 - tests/symlink_race/.gitignore | 3 - tests/symlink_race/main.go | 91 - tests/test_helpers/helpers.go | 407 - tests/test_helpers/mount_unmount.go | 258 - tests/xattr/xattr_fd_test.go | 70 - tests/xattr/xattr_integration_test.go | 371 - volume.go | 172 + 379 files changed, 1365 insertions(+), 60021 deletions(-) delete mode 100644 .travis.yml delete mode 100644 Documentation/.gitignore delete mode 100644 Documentation/CLI_ABI.md delete mode 100644 Documentation/MANPAGE-STATFS.md delete mode 100644 Documentation/MANPAGE-XRAY.md delete mode 100755 Documentation/MANPAGE-render.bash delete mode 100644 Documentation/MANPAGE.md delete mode 100644 Documentation/SECURITY.md delete mode 100644 Documentation/XFSTESTS.md delete mode 100644 Documentation/duplicate-inodes.txt delete mode 100644 Documentation/extractloop.md delete mode 100644 Documentation/extractloop_plot_csv.png delete mode 100644 Documentation/file-format.md delete mode 100644 Documentation/folders-side-by-side.gif delete mode 100644 Documentation/gocryptfs-logo.png delete mode 100644 Documentation/performance-reverse.txt delete mode 100644 Documentation/performance.txt delete mode 100644 Makefile create mode 100644 allocator/allocator32.go create mode 100644 allocator/allocator64.go delete mode 100755 benchmark-reverse.bash delete mode 100755 benchmark.bash delete mode 100755 build-without-openssl.bash delete mode 100755 build.bash create mode 100755 build.sh delete mode 100644 cli_args.go delete mode 100644 cli_args_test.go delete mode 100644 codelingo.yaml create mode 100644 common_ops.go delete mode 100644 contrib/atomicrename/.gitignore delete mode 100644 contrib/atomicrename/main.go delete mode 100755 contrib/cleanup-tmp-mounts.sh delete mode 100644 contrib/findholes/.gitignore delete mode 100644 contrib/findholes/holes/holes.go delete mode 100644 contrib/findholes/main.go delete mode 100644 contrib/getdents-debug/getdents/.gitignore delete mode 100644 contrib/getdents-debug/getdents/getdents.go delete mode 100644 contrib/getdents-debug/getdents_c/.gitignore delete mode 100644 contrib/getdents-debug/getdents_c/Makefile delete mode 100644 contrib/getdents-debug/getdents_c/getdents.c delete mode 100644 contrib/getdents-debug/readdirnames/.gitignore delete mode 100644 contrib/getdents-debug/readdirnames/readdirnames.go delete mode 100755 contrib/gocryptfs-maybe.bash delete mode 100755 contrib/mount-ext4-ramdisk.sh delete mode 100644 contrib/statfs/.gitignore delete mode 100644 contrib/statfs/statfs.go delete mode 100644 contrib/statvsfstat/.gitignore delete mode 100644 contrib/statvsfstat/statvsfstat.go delete mode 100755 crossbuild.bash delete mode 100644 ctlsock/ctlsock.go delete mode 100644 ctlsock/json_abi.go delete mode 100644 daemonize.go create mode 100644 directory.go create mode 100644 file.go delete mode 100644 fsck.go delete mode 100644 go.mod delete mode 100644 go.sum delete mode 100644 gocryptfs-xray/.gitignore delete mode 100644 gocryptfs-xray/paths_ctlsock.go delete mode 100644 gocryptfs-xray/xray_main.go delete mode 100644 gocryptfs-xray/xray_tests/aesgcm_fs.masterkey.txt delete mode 100644 gocryptfs-xray/xray_tests/aesgcm_fs.xray.txt delete mode 100644 gocryptfs-xray/xray_tests/aesgcm_fs/VnvoeSetPaOFjZDaZAh0lA delete mode 100644 gocryptfs-xray/xray_tests/aesgcm_fs/gocryptfs.conf delete mode 100644 gocryptfs-xray/xray_tests/aesgcm_fs/gocryptfs.diriv delete mode 100644 gocryptfs-xray/xray_tests/aessiv_fs.masterkey.txt delete mode 100644 gocryptfs-xray/xray_tests/aessiv_fs.xray.txt delete mode 100644 gocryptfs-xray/xray_tests/aessiv_fs/gocryptfs.conf delete mode 100644 gocryptfs-xray/xray_tests/aessiv_fs/gocryptfs.diriv delete mode 100644 gocryptfs-xray/xray_tests/aessiv_fs/klepPXQJIaEDaIx-yurAqQ delete mode 100644 gocryptfs-xray/xray_tests/xray_test.go delete mode 100755 golint.bash delete mode 100644 help.go create mode 100644 helpers.go delete mode 100644 info.go delete mode 100644 init_dir.go delete mode 100644 internal/configfile/config_test.go delete mode 100644 internal/configfile/config_test/.gitignore delete mode 100644 internal/configfile/config_test/PlaintextNames.conf delete mode 100644 internal/configfile/config_test/StrangeFeature.conf delete mode 100644 internal/configfile/config_test/v1.conf delete mode 100644 internal/configfile/config_test/v2.conf delete mode 100644 internal/configfile/scrypt_test.go delete mode 100644 internal/contentenc/content_test.go delete mode 100644 internal/contentenc/offsets_test.go delete mode 100644 internal/cryptocore/cryptocore_test.go delete mode 100644 internal/cryptocore/hkdf_test.go delete mode 100644 internal/cryptocore/randprefetch_test.go delete mode 100644 internal/cryptocore/randsize_test.go delete mode 100644 internal/ctlsocksrv/ctlsock_serve.go delete mode 100644 internal/ctlsocksrv/sanitize.go delete mode 100644 internal/ctlsocksrv/sanitize_test.go delete mode 100644 internal/ensurefds012/ensurefds012.go delete mode 100644 internal/fido2/fido2.go delete mode 100644 internal/fusefrontend/args.go delete mode 100644 internal/fusefrontend/ctlsock_interface.go delete mode 100644 internal/fusefrontend/dircache.go delete mode 100644 internal/fusefrontend/file.go delete mode 100644 internal/fusefrontend/file_allocate_truncate.go delete mode 100644 internal/fusefrontend/file_api_check.go delete mode 100644 internal/fusefrontend/file_holes.go delete mode 100644 internal/fusefrontend/file_setattr.go delete mode 100644 internal/fusefrontend/node.go delete mode 100644 internal/fusefrontend/node_api_check.go delete mode 100644 internal/fusefrontend/node_dir_ops.go delete mode 100644 internal/fusefrontend/node_helpers.go delete mode 100644 internal/fusefrontend/node_open_create.go delete mode 100644 internal/fusefrontend/node_xattr.go delete mode 100644 internal/fusefrontend/node_xattr_darwin.go delete mode 100644 internal/fusefrontend/node_xattr_linux.go delete mode 100644 internal/fusefrontend/openbackingdir_test.go delete mode 100644 internal/fusefrontend/root_node.go delete mode 100644 internal/fusefrontend/xattr_unit_test.go delete mode 100644 internal/fusefrontend_reverse/ctlsock_interface.go delete mode 100644 internal/fusefrontend_reverse/excluder.go delete mode 100644 internal/fusefrontend_reverse/excluder_test.go delete mode 100644 internal/fusefrontend_reverse/file.go delete mode 100644 internal/fusefrontend_reverse/file_api_check.go delete mode 100644 internal/fusefrontend_reverse/file_helpers.go delete mode 100644 internal/fusefrontend_reverse/mocks_test.go delete mode 100644 internal/fusefrontend_reverse/node.go delete mode 100644 internal/fusefrontend_reverse/node_api_check.go delete mode 100644 internal/fusefrontend_reverse/node_dir_ops.go delete mode 100644 internal/fusefrontend_reverse/node_helpers.go delete mode 100644 internal/fusefrontend_reverse/root_node.go delete mode 100644 internal/fusefrontend_reverse/rpath.go delete mode 100644 internal/fusefrontend_reverse/virtualconf.go delete mode 100644 internal/fusefrontend_reverse/virtualnode.go delete mode 100644 internal/inomap/inomap.go delete mode 100644 internal/inomap/inomap_test.go delete mode 100644 internal/inomap/qino.go delete mode 100644 internal/nametransform/longnames_test.go delete mode 100644 internal/nametransform/names_test.go delete mode 100644 internal/openfiletable/open_file_table.go delete mode 100644 internal/readpassword/extpass_test.go delete mode 100644 internal/readpassword/passfile.go delete mode 100644 internal/readpassword/passfile_test.go delete mode 100644 internal/readpassword/passfile_test_files/empty.txt delete mode 100644 internal/readpassword/passfile_test_files/empty_first_line.txt delete mode 100644 internal/readpassword/passfile_test_files/file with spaces.txt delete mode 100644 internal/readpassword/passfile_test_files/mypassword.txt delete mode 100644 internal/readpassword/passfile_test_files/mypassword_garbage.txt delete mode 100644 internal/readpassword/passfile_test_files/mypassword_missing_newline.txt delete mode 100644 internal/readpassword/passfile_test_files/newline.txt delete mode 100644 internal/readpassword/read.go delete mode 100644 internal/readpassword/stdin_test.go delete mode 100644 internal/serialize_reads/sr.go delete mode 100755 internal/siv_aead/benchmark.bash delete mode 100644 internal/siv_aead/correctness_test.go delete mode 100644 internal/siv_aead/performance_test.go delete mode 100755 internal/speed/benchmark.bash delete mode 100644 internal/speed/speed.go delete mode 100644 internal/speed/speed_test.go delete mode 100644 internal/stupidgcm/stupidgcm_test.go delete mode 100644 internal/syscallcompat/emulate.go delete mode 100644 internal/syscallcompat/emulate_test.go delete mode 100644 internal/syscallcompat/getdents_other.go delete mode 100644 internal/syscallcompat/getdents_test.go delete mode 100644 internal/syscallcompat/helpers.go delete mode 100644 internal/syscallcompat/main_test.go delete mode 100644 internal/syscallcompat/open_nofollow_test.go delete mode 100644 internal/syscallcompat/sys_common_test.go delete mode 100644 internal/syscallcompat/sys_darwin.go delete mode 100644 internal/syscallcompat/unix2syscall_darwin.go delete mode 100644 internal/tlog/log.go delete mode 100644 internal/tlog/tlog_test.go delete mode 100644 masterkey.go delete mode 100644 mount.go delete mode 100755 package-release-tarballs.bash delete mode 100644 profiling.go delete mode 100755 profiling/ls.bash delete mode 100755 profiling/streaming-read.bash delete mode 100755 profiling/streaming-write.bash delete mode 100755 profiling/tar-extract.bash delete mode 100755 profiling/write-trace.bash delete mode 100644 race.go delete mode 100644 sendusr1.go delete mode 100755 test-without-openssl.bash delete mode 100755 test.bash delete mode 100755 tests/canonical-benchmarks.bash delete mode 100644 tests/cli/cli_test.go delete mode 100644 tests/cli/gocryptfs.conf.b9e5ba23 delete mode 100644 tests/defaults/1980.tar.gz delete mode 100644 tests/defaults/acl_test.go delete mode 100644 tests/defaults/ctlsock_test.go delete mode 100644 tests/defaults/diriv_test.go delete mode 100644 tests/defaults/main_test.go delete mode 100644 tests/defaults/performance_test.go delete mode 100755 tests/dl-linux-tarball.bash delete mode 120000 tests/example_filesystems/content/abs delete mode 100644 tests/example_filesystems/content/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 120000 tests/example_filesystems/content/rel delete mode 100644 tests/example_filesystems/content/status.txt delete mode 100644 tests/example_filesystems/example_filesystems_test.go delete mode 100644 tests/example_filesystems/example_test_helpers.go delete mode 100644 tests/example_filesystems/v0.4/3-HZSwv99agoWgTErV0YFQ== delete mode 120000 tests/example_filesystems/v0.4/6hL2fPVB2aMSh4-UoDn5Kw== delete mode 120000 tests/example_filesystems/v0.4/TBIgdfhDKwkXVTnWLVzFSg== delete mode 100644 tests/example_filesystems/v0.4/gocryptfs.conf delete mode 120000 tests/example_filesystems/v0.5/AOtl_i4xQWDyG0_zUqntOw== delete mode 120000 tests/example_filesystems/v0.5/Pf35wlWlf43N68EbhIgTcQ== delete mode 100644 tests/example_filesystems/v0.5/gocryptfs.conf delete mode 100644 tests/example_filesystems/v0.5/gocryptfs.diriv delete mode 100644 tests/example_filesystems/v0.5/j2BpGUT5kOtia20PWQ2rEA== delete mode 120000 tests/example_filesystems/v0.6-plaintextnames/abs delete mode 100644 tests/example_filesystems/v0.6-plaintextnames/gocryptfs.conf delete mode 120000 tests/example_filesystems/v0.6-plaintextnames/rel delete mode 100644 tests/example_filesystems/v0.6-plaintextnames/status.txt delete mode 120000 tests/example_filesystems/v0.6/9pOs0yjJI5A67pv5CnqomQ== delete mode 120000 tests/example_filesystems/v0.6/G79Zdu41H3bgwdaQlrz-dg== delete mode 100644 tests/example_filesystems/v0.6/RuYvQG_raW_-H_LcyJC4LQ== delete mode 100644 tests/example_filesystems/v0.6/gocryptfs.conf delete mode 100644 tests/example_filesystems/v0.6/gocryptfs.diriv delete mode 120000 tests/example_filesystems/v0.7-plaintextnames/abs delete mode 100644 tests/example_filesystems/v0.7-plaintextnames/gocryptfs.conf delete mode 120000 tests/example_filesystems/v0.7-plaintextnames/rel delete mode 100644 tests/example_filesystems/v0.7-plaintextnames/status.txt delete mode 120000 tests/example_filesystems/v0.7/4tRF1LdULRFLiXwfze671Q== delete mode 100644 tests/example_filesystems/v0.7/RWPXmXkRFrWw1aOpq7C-NQ== delete mode 120000 tests/example_filesystems/v0.7/dwPcZNei4HN4qPA6FxoG_A== delete mode 100644 tests/example_filesystems/v0.7/gocryptfs.conf delete mode 100644 tests/example_filesystems/v0.7/gocryptfs.diriv delete mode 120000 tests/example_filesystems/v0.9/00msNUi5h5aKMX_f-4pBhA== delete mode 100644 tests/example_filesystems/v0.9/R83PhW-BBA_q4rPYD7dEMg== delete mode 100644 tests/example_filesystems/v0.9/gocryptfs.conf delete mode 100644 tests/example_filesystems/v0.9/gocryptfs.diriv delete mode 100644 tests/example_filesystems/v0.9/gocryptfs.longname.y4J-w3LBX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ= delete mode 100644 tests/example_filesystems/v0.9/gocryptfs.longname.y4J-w3LBX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ=.name delete mode 120000 tests/example_filesystems/v0.9/hwE1RKIXtF8hmQMvEXSTtg== delete mode 100644 tests/example_filesystems/v1.1-aessiv/MA0FDdmnXpmPJtS_AcAbqQ== delete mode 120000 tests/example_filesystems/v1.1-aessiv/Sjl6QXHm2IjuKwaKgJ5jig== delete mode 100644 tests/example_filesystems/v1.1-aessiv/gocryptfs.conf delete mode 100644 tests/example_filesystems/v1.1-aessiv/gocryptfs.diriv delete mode 100644 tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk= delete mode 100644 tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk=.name delete mode 120000 tests/example_filesystems/v1.1-aessiv/jCGXyJJqu4sdxRLsDQNEtA== delete mode 100644 tests/example_filesystems/v1.1-reverse-plaintextnames/.gocryptfs.reverse.conf delete mode 120000 tests/example_filesystems/v1.1-reverse-plaintextnames/abs delete mode 100644 tests/example_filesystems/v1.1-reverse-plaintextnames/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 120000 tests/example_filesystems/v1.1-reverse-plaintextnames/rel delete mode 100644 tests/example_filesystems/v1.1-reverse-plaintextnames/status.txt delete mode 100644 tests/example_filesystems/v1.1-reverse/.gocryptfs.reverse.conf delete mode 120000 tests/example_filesystems/v1.1-reverse/abs delete mode 100644 tests/example_filesystems/v1.1-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 120000 tests/example_filesystems/v1.1-reverse/rel delete mode 100644 tests/example_filesystems/v1.1-reverse/status.txt delete mode 100644 tests/example_filesystems/v1.3-reverse/.gocryptfs.reverse.conf delete mode 120000 tests/example_filesystems/v1.3-reverse/abs delete mode 100644 tests/example_filesystems/v1.3-reverse/dir1/dir2/file delete mode 100644 tests/example_filesystems/v1.3-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 120000 tests/example_filesystems/v1.3-reverse/rel delete mode 100644 tests/example_filesystems/v1.3-reverse/status.txt delete mode 100644 tests/example_filesystems/v1.3/gocryptfs.conf delete mode 100644 tests/example_filesystems/v1.3/gocryptfs.diriv delete mode 100644 tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs delete mode 100644 tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs.name delete mode 120000 tests/example_filesystems/v1.3/gv65k_g2NQyBsSv_5dzMVQ delete mode 100644 tests/example_filesystems/v1.3/mGj2_hdnHe34Sp0iIQUwuw delete mode 120000 tests/example_filesystems/v1.3/uD4PVrDBY5y2k_qLKNOFvA delete mode 100644 tests/fsck/broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q delete mode 100644 tests/fsck/broken_fs_v1.4/CMyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A delete mode 100644 tests/fsck/broken_fs_v1.4/Ef-68icxbQ-TuvmnWHuItB1BeLB92dNCXMXiz2M-zPI delete mode 120000 tests/fsck/broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw delete mode 100644 tests/fsck/broken_fs_v1.4/K2m0E6qzIfoLkVZJanoUiQ/mWEr9JLch2FW40qhbnPgpg delete mode 100644 tests/fsck/broken_fs_v1.4/OtrNpznB8aMTKPi6bopM2g delete mode 100644 tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/_y58usbKXq_YRPMKfC3TNw delete mode 100644 tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/gocryptfs.diriv delete mode 100644 tests/fsck/broken_fs_v1.4/V5DjvW5BXlGl1yCIJn4lPgdjdMvW_LUfc7G-R8W1cZ0 delete mode 100644 tests/fsck/broken_fs_v1.4/b00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ delete mode 100644 tests/fsck/broken_fs_v1.4/ejZ3FX0zlFTpSfv-FBJ2u3ojwSN1XSqpNpCHxa5VGWw delete mode 100644 tests/fsck/broken_fs_v1.4/gocryptfs.conf delete mode 100644 tests/fsck/broken_fs_v1.4/gocryptfs.diriv delete mode 120000 tests/fsck/broken_fs_v1.4/iI0MtUdzELPeOAZYwYZFee169hpGgd3l2PXQBcc9sl4 delete mode 100644 tests/fsck/broken_fs_v1.4/invalid_file_name.3 delete mode 100644 tests/fsck/broken_fs_v1.4/invalid_file_name_2 delete mode 100644 tests/fsck/broken_fs_v1.4/invalid_file_name____1 delete mode 100644 tests/fsck/broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A delete mode 120000 tests/fsck/broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA delete mode 100644 tests/fsck/broken_fs_v1.4/trqecbMNXdzLqzpk7fSfKw/gocryptfs.diriv delete mode 100644 tests/fsck/broken_fs_v1.4/vDKs8a7UtM3PmEKk9wlPcA delete mode 100644 tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/gocryptfs.diriv delete mode 100644 tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/uC2yqKyQUXSJF-YF1Ya5nQ delete mode 100644 tests/fsck/fsck_test.go delete mode 100755 tests/fsck/run_fsck.bash delete mode 100755 tests/fuse-unmount.bash delete mode 100644 tests/hkdf_sanity/broken_content/gocryptfs.conf delete mode 100644 tests/hkdf_sanity/broken_content/status.txt delete mode 100644 tests/hkdf_sanity/broken_names/L3yg-cJYAInDGg4TcjXrnw delete mode 100644 tests/hkdf_sanity/broken_names/gocryptfs.conf delete mode 100644 tests/hkdf_sanity/broken_names/gocryptfs.diriv delete mode 100644 tests/hkdf_sanity/sanity_test.go delete mode 100755 tests/len2elen.sh delete mode 100644 tests/matrix/atime_darwin.go delete mode 100644 tests/matrix/atime_linux.go delete mode 100644 tests/matrix/concurrency_test.go delete mode 100644 tests/matrix/dir_test.go delete mode 100644 tests/matrix/fallocate_test.go delete mode 100644 tests/matrix/matrix_test.go delete mode 100755 tests/maxlen.bash delete mode 100644 tests/plaintextnames/file_holes_test.go delete mode 100644 tests/plaintextnames/plaintextnames_test.go delete mode 100644 tests/reverse/correctness_test.go delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/.gocryptfs.reverse.conf delete mode 120000 tests/reverse/ctlsock_reverse_test_fs/a_symlink delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/dir/dir/file delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/dir/file delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/dir/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/file delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/longdir.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file delete mode 100644 tests/reverse/ctlsock_reverse_test_fs/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/ctlsock_test.go delete mode 100644 tests/reverse/exclude_test.go delete mode 100644 tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf delete mode 100644 tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf.plaintextnames delete mode 100644 tests/reverse/exclude_test_fs/bkp1~ delete mode 100644 tests/reverse/exclude_test_fs/dir1/exclude delete mode 100644 tests/reverse/exclude_test_fs/dir1/file1 delete mode 100644 tests/reverse/exclude_test_fs/dir1/file2 delete mode 100644 tests/reverse/exclude_test_fs/dir1/longbkp1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx~ delete mode 100644 tests/reverse/exclude_test_fs/dir1/longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/exclude_test_fs/dir1/longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/exclude_test_fs/dir1/longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/exclude_test_fs/dir1/subdir1/exclude delete mode 100644 tests/reverse/exclude_test_fs/dir1/subdir1/subdir2/exclude delete mode 100644 tests/reverse/exclude_test_fs/dir2/file delete mode 100644 tests/reverse/exclude_test_fs/dir2/longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file delete mode 100644 tests/reverse/exclude_test_fs/dir2/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/exclude_test_fs/dir2/subdir/file delete mode 100644 tests/reverse/exclude_test_fs/file1 delete mode 100644 tests/reverse/exclude_test_fs/file2 delete mode 100644 tests/reverse/exclude_test_fs/longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file1 delete mode 100644 tests/reverse/exclude_test_fs/longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/bkp~ delete mode 100644 tests/reverse/exclude_test_fs/longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file delete mode 100644 tests/reverse/exclude_test_fs/longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/exclude_test_fs/longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/exclude_test_fs/longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx delete mode 100644 tests/reverse/inomap_test.go delete mode 100755 tests/reverse/linux-tarball-test.bash delete mode 100644 tests/reverse/longname_perf_test.go delete mode 100644 tests/reverse/main_test.go delete mode 100644 tests/reverse/xattr_test.go delete mode 100644 tests/root_test/root_test.go delete mode 100644 tests/sharedstorage/sharedstorage_test.go delete mode 100755 tests/sshfs-benchmark.bash delete mode 100755 tests/stress_tests/extractloop.bash delete mode 100755 tests/stress_tests/extractloop_plot_csv.m delete mode 120000 tests/stress_tests/fsstress-encfs.bash delete mode 100755 tests/stress_tests/fsstress-gocryptfs.bash delete mode 120000 tests/stress_tests/fsstress-loopback.bash delete mode 100755 tests/stress_tests/fsstress.collect-crashes.sh delete mode 100644 tests/stress_tests/linux-3.0.md5sums delete mode 100755 tests/stress_tests/parallel_cp.sh delete mode 120000 tests/stress_tests/pingpong-rsync.bash delete mode 100755 tests/stress_tests/pingpong.bash delete mode 100644 tests/symlink_race/.gitignore delete mode 100644 tests/symlink_race/main.go delete mode 100644 tests/test_helpers/helpers.go delete mode 100644 tests/test_helpers/mount_unmount.go delete mode 100644 tests/xattr/xattr_fd_test.go delete mode 100644 tests/xattr/xattr_integration_test.go create mode 100644 volume.go diff --git a/.gitignore b/.gitignore index 2e2743f..cf7f075 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,10 @@ -# the gocryptfs executable -/gocryptfs +/build +/include +/lib +/openssl* # temporary files created by the tests /tmp -# binary releases and signatiures -/*.tar.gz -/*.asc - # Binaries created for cpu profiling *.test - -# Rendered manpage -gocryptfs.1 - -# Dependencies copied by "dep" -/vendor -/_vendor-* - -# Source tarball version. Should never be committed to git. -/VERSION diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9cfc238..0000000 --- a/.travis.yml +++ /dev/null @@ -1,43 +0,0 @@ -language: go -os: linux - -# fuse on travis -sudo: required -dist: bionic # Ubuntu 18.04 "Bionic", https://docs.travis-ci.com/user/reference/bionic/ - -env: - - GO111MODULE=on - -git: - depth: 300 - -# Build with the lastest relevant Go versions -# Relevance is determined from: -# * https://golang.org/dl/ -# * https://packages.debian.org/search?keywords=golang&searchon=names&exact=1&suite=all§ion=all -# * https://packages.ubuntu.com/search?keywords=golang&searchon=names&exact=1&suite=all§ion=all -go: - - 1.11.x # Debian 10 "Buster" - - 1.12.x # Ubuntu 19.10 - - 1.13.x # Debian 11 "Bullseye" - - stable - -before_install: - - sudo apt-get install -qq fuse - - sudo modprobe fuse - - sudo chmod 666 /dev/fuse - - sudo chown root:$USER /etc/fuse.conf - -script: - - openssl version - - df -Th / /tmp - - env GO111MODULE=on go build - - ./build-without-openssl.bash - - ./build.bash - - ./gocryptfs -speed - - ./test.bash - - make root_test - - ./crossbuild.bash - - echo "rebuild with locked dependencies" - - go mod vendor - - ./build.bash -mod=vendor diff --git a/Documentation/.gitignore b/Documentation/.gitignore deleted file mode 100644 index d2f316c..0000000 --- a/Documentation/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Generated man pages -*.1 diff --git a/Documentation/CLI_ABI.md b/Documentation/CLI_ABI.md deleted file mode 100644 index 9581556..0000000 --- a/Documentation/CLI_ABI.md +++ /dev/null @@ -1,102 +0,0 @@ -Stable CLI ABI -============== - -If you want to call gocryptfs from your script or app, this is the -stable ABI. - -General -------- - -1. A password is piped into gocryptfs with an optional terminating - newline. Any unexpected data after the final newline will - cause gocryptfs to abort. -2. Always pass "--" after the options. This prevents a CIPERDIR that - starts with a dash ("-") to wreak havoc. -3. Use "-q" to get rid of all informational messages. Only error - messages (if any) will be printed to stderr (capture it!). -4. Check the exit code of gocryptfs. 0 is success, anything else is an - error and details about that error will have been printed to stderr. - -Initialize Filesystem ---------------------- - -#### Bash example - - $ cat mypassword.txt | gocryptfs -init -q -- CIPHERDIR - -Content of "mypassword.txt": - - mypassword1234 - -#### What you have to pipe to gocryptfs - -1. Password -2. Optional newline - -#### Notes - -1. The CIPHERDIR directory must exist and be empty - -#### Exit Codes - -* 0 = success -* 6 = CIPHERDIR is invalid: not an empty directory -* 22 = password is empty -* 24 = could not create gocryptfs.conf -* other = please inspect the message - -Mount ------ - -#### Bash example - - $ cat mypassword.txt | gocryptfs -q -- CIPHERDIR MOUNTPOINT - -#### What you have to pipe to gocryptfs - -Same as for "Initialize Filesystem". - -#### Notes - -1. The MOUNTPOINT directory must exist and be empty. - -#### Exit Codes - -* 0 = success -* 10 = MOUNTPOINT is not an empty directory or contains CIPHERDIR -* 12 = password incorrect -* 23 = gocryptfs.conf could not be opened (does not exist, is unreadable, ...) -* other = please inspect the message - -Change Password ---------------- - -#### Bash example - - $ cat change.txt | gocryptfs -passwd -q -- CIPHERDIR - -Content of "change.txt": - - mypassword1234 - newpassword9876 - -#### What you have to pipe to gocryptfs - -1. Old password -2. Newline -3. New password -4. Optional newline - -#### Exit Codes - -* 0 = success -* 12 = password incorrect -* 23 = gocryptfs.conf could not be opened for reading -* 24 = could not write the updated gocryptfs.conf -* other = please inspect the message - -Further Reading ---------------- - -Additional exit codes that are unlikely to occur are defined in -[exitcodes.go](../internal/exitcodes/exitcodes.go). diff --git a/Documentation/MANPAGE-STATFS.md b/Documentation/MANPAGE-STATFS.md deleted file mode 100644 index c519f4b..0000000 --- a/Documentation/MANPAGE-STATFS.md +++ /dev/null @@ -1,82 +0,0 @@ -% STATFS(1) -% github.com/rfjakob -% Sep 2019 - -NAME -==== - -statfs - dump the statfs(2) information for PATH to console in JSON format. - -SYNOPSIS -======== - -statfs PATH - -DESCRIPTION -=========== - -The statfs(2) system call returns information about a mounted filesystem -in a `statfs_t` structure. This tool dumps this information in JSON format. -It is developed as part of gocryptfs and written in Go. - -The `statfs_t` structure is architecture-dependent. On amd64 it looks like this: - -``` -type Statfs_t struct { - Type int64 - Bsize int64 - Blocks uint64 - Bfree uint64 - Bavail uint64 - Files uint64 - Ffree uint64 - Fsid struct { - Val [2]int32 - } - Namelen int64 - Frsize int64 - Flags int64 - Spare [4]int64 -} -``` - -See the statfs(2) man page for the meaning of these fields, and note -that the field names here are acc. to the Go `golang.org/x/sys/unix` -naming convention, and slightly different than in C. - -EXAMPLES -======== - -Get the statfs(2) information for /tmp: - -``` -$ statfs /tmp -{ - "Type": 16914836, - "Bsize": 4096, - "Blocks": 3067428, - "Bfree": 3067411, - "Bavail": 3067411, - "Files": 3067428, - "Ffree": 3067381, - "Fsid": { - "Val": [ - 0, - 0 - ] - }, - "Namelen": 255, - "Frsize": 4096, - "Flags": 38, - "Spare": [ - 0, - 0, - 0, - 0 - ] -} -``` - -SEE ALSO -======== -statfs(2) gocryptfs(1) diff --git a/Documentation/MANPAGE-XRAY.md b/Documentation/MANPAGE-XRAY.md deleted file mode 100644 index 5a5162c..0000000 --- a/Documentation/MANPAGE-XRAY.md +++ /dev/null @@ -1,64 +0,0 @@ -% GOCRYPTFS-XRAY(1) -% github.com/rfjakob -% Jan 2018 - -NAME -==== - -gocryptfs-xray - examine gocryptfs-related data - -SYNOPSIS -======== - -#### Examine encrypted file/directory -gocryptfs-xray CIPHERDIR/ENCRYPTED-FILE-OR-DIR - -#### Decrypt and show master key -gocryptfs-xray -dumpmasterkey CIPHERDIR/gocryptfs.conf - -#### Encrypt paths -gocryptfs-xray -encrypt-paths SOCKET - -DESCRIPTION -=========== - -Available options are listed below. - -#### -0 -Use \\0 instead of \\n as separator for -decrypt-paths and -encrypt-paths. - -#### -aessiv -Assume AES-SIV mode instead of AES-GCM when examining an encrypted file. -Is not needed and has no effect in `-dumpmasterkey` mode. - -#### -decrypt-paths -Decrypt file paths using gocryptfs control socket. Reads from stdin. -See `-ctlsock` in gocryptfs(1). - -#### -dumpmasterkey -Decrypts and shows the master key. - -#### -encrypt-paths -Encrypt file paths using gocryptfs control socket. Reads from stdin. -See `-ctlsock` in gocryptfs(1). - -EXAMPLES -======== - -Examine an encrypted file: - - gocryptfs-xray myfs/mCXnISiv7nEmyc0glGuhTQ - -Print the master key: - - gocryptfs-xray -dumpmasterkey myfs/gocryptfs.conf - -Mount gocryptfs with control socket and use gocryptfs-xray to -encrypt some paths: - - gocryptfs -ctlsock myfs.sock myfs myfs.mnt - echo -e "foo\nbar" | gocryptfs-xray -encrypt-paths myfs.sock - -SEE ALSO -======== -gocryptfs(1) fuse(8) diff --git a/Documentation/MANPAGE-render.bash b/Documentation/MANPAGE-render.bash deleted file mode 100755 index 74028ad..0000000 --- a/Documentation/MANPAGE-render.bash +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -eu -cd $(dirname "$0") - -# Render Markdown to a proper man(1) manpage -function render { - IN=$1 - OUT=$2 - echo "Rendering $IN to $OUT" - echo ".\\\" This man page was generated from $IN. View it using 'man ./$OUT'" > $OUT - echo ".\\\"" >> $OUT - pandoc "$IN" -s -t man >> $OUT -} - -render MANPAGE.md gocryptfs.1 -render MANPAGE-XRAY.md gocryptfs-xray.1 -render MANPAGE-STATFS.md statfs.1 diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md deleted file mode 100644 index 9ddf674..0000000 --- a/Documentation/MANPAGE.md +++ /dev/null @@ -1,619 +0,0 @@ -% GOCRYPTFS(1) -% github.com/rfjakob -% Aug 2017 - -NAME -==== - -gocryptfs - create or mount an encrypted filesystem - -SYNOPSIS -======== - -#### Initialize new encrypted filesystem -`gocryptfs -init [OPTIONS] CIPHERDIR` - -#### Mount -`gocryptfs [OPTIONS] CIPHERDIR MOUNTPOINT [-o COMMA-SEPARATED-OPTIONS]` - -#### Unmount -`fusermount -u MOUNTPOINT` - -#### Change password -`gocryptfs -passwd [OPTIONS] CIPHERDIR` - -#### Check consistency -`gocryptfs -fsck [OPTIONS] CIPHERDIR` - -#### Show filesystem information -`gocryptfs -info [OPTIONS] CIPHERDIR` - -DESCRIPTION -=========== - -gocryptfs is an encrypted overlay filesystem written in Go. -Encrypted files are stored in CIPHERDIR, and a plain-text -view can be presented by mounting the filesystem at MOUNTPOINT. - -gocryptfs was inspired by encfs(1) and strives to fix its -security issues while providing good performance. - -ACTION FLAGS -============ - -Unless one of the following *action flags* is passed, the default -action is to mount a filesystem (see SYNOPSIS). - -#### -fsck -Check CIPHERDIR for consistency. If corruption is found, the -exit code is 26. - -#### -h, -help -Print a short help text that shows the more-often used options. - -#### -hh -Long help text, shows all available options. - -#### -info -Pretty-print the contents of the config file in CIPHERDIR for -human consumption, stripping out sensitive data. - -Example: - - $ gocryptfs -info my_cipherdir - Creator: gocryptfs v2.0-beta2 - FeatureFlags: GCMIV128 HKDF DirIV EMENames LongNames Raw64 - EncryptedKey: 64B - ScryptObject: Salt=32B N=65536 R=8 P=1 KeyLen=32 - -#### -init -Initialize encrypted directory. - -#### -passwd -Change the password. Will ask for the old password, check if it is -correct, and ask for a new one. - -This can be used together with `-masterkey` if -you forgot the password but know the master key. Note that without the -old password, gocryptfs cannot tell if the master key is correct and will -overwrite the old one without mercy. It will, however, create a backup copy -of the old config file as `gocryptfs.conf.bak`. Delete it after -you have verified that you can access your files with the -new password. - -#### -speed -Run crypto speed test. Benchmark Go's built-in GCM against OpenSSL -(if available). The library that will be selected on "-openssl=auto" -(the default) is marked as such. - -#### -version -Print version and exit. The output contains three fields separated by ";". -Example: "gocryptfs v1.1.1-5-g75b776c; go-fuse 6b801d3; 2016-11-01 go1.7.3". -Field 1 is the gocryptfs version, field 2 is the version of the go-fuse -library, field 3 is the compile date and the Go version that was -used. - -INIT OPTIONS -============ - -Available options for `-init` are listed below. Usually, you don't need any. -Defaults are fine. - -#### -aessiv -Use the AES-SIV encryption mode. This is slower than GCM but is -secure with deterministic nonces as used in "-reverse" mode. - -#### -devrandom -Use `/dev/random` for generating the master key instead of the default Go -implementation. This is especially useful on embedded systems with Go versions -prior to 1.9, which fall back to weak random data when the getrandom syscall -is blocking. Using this option can block indefinitely when the kernel cannot -harvest enough entropy. - -#### -hkdf -Use HKDF to derive separate keys for content and name encryption from -the master key. Default true. - -#### -nosyslog -Diagnostic messages are normally redirected to syslog once gocryptfs -daemonizes. This option disables the redirection and messages will -continue be printed to stdout and stderr. - -#### -plaintextnames -Do not encrypt file names and symlink targets. - -#### -raw64 -Use unpadded base64 encoding for file names. This gets rid of the -trailing "\\=\\=". A filesystem created with this option can only be -mounted using gocryptfs v1.2 and higher. Default true. - -#### -reverse -Reverse mode shows a read-only encrypted view of a plaintext -directory. Implies "-aessiv". - -#### -scryptn int -scrypt cost parameter expressed as scryptn=log2(N). Possible values are -10 to 28, representing N=2^10 to N=2^28. - -Setting this to a lower -value speeds up mounting and reduces its memory needs, but makes -the password susceptible to brute-force attacks. The default is 16. - -MOUNT OPTIONS -============= - -Available options for mounting are listed below. Usually, you don't need any. -Defaults are fine. - -#### -allow_other -By default, the Linux kernel prevents any other user (even root) to -access a mounted FUSE filesystem. Settings this option allows access for -other users, subject to file permission checking. Only works if -user_allow_other is set in /etc/fuse.conf. This option is equivalent to -"allow_other" plus "default_permissions" described in fuse(8). - -#### -ctlsock string -Create a control socket at the specified location. The socket can be -used to decrypt and encrypt paths inside the filesystem. When using -this option, make sure that the directory you place the socket in is -not world-accessible. For example, `/run/user/UID/my.socket` would -be suitable. - -#### -dev, -nodev -Enable (`-dev`) or disable (`-nodev`) device files in a gocryptfs mount -(default: `-nodev`). If both are specified, `-nodev` takes precedence. -You need root permissions to use `-dev`. - -#### -e PATH, -exclude PATH -Only for reverse mode: exclude relative plaintext path from the encrypted -view, matching only from root of mounted filesystem. Can be passed multiple -times. Example: - - gocryptfs -reverse -exclude Music -exclude Movies /home/user /mnt/user.encrypted - -See also `-exclude-wildcard`, `-exclude-from` and the [EXCLUDING FILES](#excluding-files) section. - -#### -ew PATH, -exclude-wildcard PATH -Only for reverse mode: exclude paths from the encrypted view, matching anywhere. -Wildcards supported. Can be passed multiple times. Example: - - gocryptfs -reverse -exclude-wildcard '*~' /home/user /mnt/user.encrypted - -See also `-exclude`, `-exclude-from` and the [EXCLUDING FILES](#excluding-files) section. - -#### -exclude-from FILE -Only for reverse mode: reads exclusion patters (using `-exclude-wildcard` syntax) -from a file. Can be passed multiple times. Example: - - gocryptfs -reverse -exclude-from ~/crypt-exclusions /home/user /mnt/user.encrypted - -See also `-exclude`, `-exclude-wildcard` and the [EXCLUDING FILES](#excluding-files) section. - -#### -exec, -noexec -Enable (`-exec`) or disable (`-noexec`) executables in a gocryptfs mount -(default: `-exec`). If both are specified, `-noexec` takes precedence. - -#### -fg, -f -Stay in the foreground instead of forking away. -For compatibility, "-f" is also accepted, but "-fg" is preferred. - -Unless `-notifypid` is also passed, the logs go to stdout and stderr -instead of syslog. - -#### -force_owner string -If given a string of the form "uid:gid" (where both "uid" and "gid" are -substituted with positive integers), presents all files as owned by the given -uid and gid, regardless of their actual ownership. Implies "allow_other". - -This is rarely desired behavior: One should *usually* run gocryptfs as the -account which owns the backing-store files, which should *usually* be one and -the same with the account intended to access the decrypted content. An example -of a case where this may be useful is a situation where content is stored on a -filesystem that doesn't properly support UNIX ownership and permissions. - -#### -forcedecode -Force decode of encrypted files even if the integrity check fails, instead of -failing with an IO error. Warning messages are still printed to syslog if corrupted -files are encountered. -It can be useful to recover files from disks with bad sectors or other corrupted -media. It shall not be used if the origin of corruption is unknown, specially -if you want to run executable files. - -For corrupted media, note that you probably want to use dd_rescue(1) -instead, which will recover all but the corrupted 4kB block. - -This option makes no sense in reverse mode. It requires gocryptfs to be compiled with openssl -support and implies -openssl true. Because of this, it is not compatible with -aessiv, -that uses built-in Go crypto. - -Setting this option forces the filesystem to read-only and noexec. - -#### -fsname string -Override the filesystem name (first column in df -T). Can also be -passed as "-o fsname=" and is equivalent to libfuse's option of the -same name. By default, CIPHERDIR is used. - -#### -fusedebug -Enable fuse library debug output. - -#### -i duration, -idle duration -Only for forward mode: automatically unmount the filesystem if it has been idle -for the specified duration. Durations can be specified like "500s" or "2h45m". -0 (the default) means stay mounted indefinitely. - -When a process has open files or its working directory in the mount, -this will keep it not idle indefinitely. - -#### -kernel_cache -Enable the kernel_cache option of the FUSE filesystem, see fuse(8) for details. - -#### -ko -Pass additional mount options to the kernel (comma-separated list). -FUSE filesystems are mounted with "nodev,nosuid" by default. If gocryptfs -runs as root, you can enable device files by passing the opposite mount option, -"dev", and if you want to enable suid-binaries, pass "suid". -"ro" (equivalent to passing the "-ro" option) and "noexec" may also be -interesting. For a complete list see the section -`FILESYSTEM-INDEPENDENT MOUNT OPTIONS` in mount(8). On MacOS, "local", -"noapplexattr", "noappledouble" may be interesting. - -Note that unlike "-o", "-ko" is a regular option and must be passed BEFORE -the directories. Example: - - gocryptfs -ko noexec /tmp/foo /tmp/bar - -#### -longnames -Store names longer than 176 bytes in extra files (default true) -This flag is useful when recovering old gocryptfs filesystems using -"-masterkey". It is ignored (stays at the default) otherwise. - -#### -nodev -See `-dev, -nodev`. - -#### -noexec -See `-exec, -noexec`. - -#### -nofail -Having the `nofail` option in `/etc/fstab` instructs `systemd` to continue -booting normally even if the mount fails (see `man systemd.fstab`). - -The option is ignored by `gocryptfs` itself and has no effect outside `/etc/fstab`. - -#### -nonempty -Allow mounting over non-empty directories. FUSE by default disallows -this to prevent accidental shadowing of files. - -#### -noprealloc -Disable preallocation before writing. By default, gocryptfs -preallocates the space the next write will take using fallocate(2) -in mode FALLOC_FL_KEEP_SIZE. The preallocation makes sure it cannot -run out of space in the middle of the write, which would cause the -last 4kB block to be corrupt and unreadable. - -On ext4, preallocation is fast and does not cause a -noticeable performance hit. Unfortunately, on Btrfs, preallocation -is very slow, especially on rotational HDDs. The "-noprealloc" -option gives users the choice to trade robustness against -out-of-space errors for a massive speedup. - -For benchmarks and more details of the issue see -https://github.com/rfjakob/gocryptfs/issues/63 . - -#### -nosuid -See `-suid, -nosuid`. - -#### -notifypid int -Send USR1 to the specified process after successful mount. This is -used internally for daemonization. - -#### -rw, -ro -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 specifiy the -`-reverse` option both at `-init` and at mount. - -#### -serialize_reads -The kernel usually submits multiple concurrent reads to service -userspace requests and kernel readahead. gocryptfs serves them -concurrently and in arbitrary order. On backing storage that performs -poorly for concurrent or out-of-order reads (like Amazon Cloud Drive), -this behavior can cause very slow read speeds. - -The `-serialize_reads` -option does two things: (1) reads will be submitted one-by-one (no -concurrency) and (2) gocryptfs tries to order the reads by file -offset order. - -The ordering requires gocryptfs to wait a certain time before -submitting a read. The serialization introduces extra locking. -These factors will limit throughput to below 70MB/s. - -For more details visit https://github.com/rfjakob/gocryptfs/issues/92 . - -#### -sharedstorage -Enable work-arounds so gocryptfs works better when the backing -storage directory is concurrently accessed by multiple gocryptfs -instances. - -At the moment, it does two things: - -1. Disable stat() caching so changes to the backing storage show up - immediately. -2. Disable hard link tracking, as the inode numbers on the backing - storage are not stable when files are deleted and re-created behind - our back. This would otherwise produce strange "file does not exist" - and other errors. - -When "-sharedstorage" is active, performance is reduced and hard -links cannot be created. - -Even with this flag set, you may hit occasional problems. Running -gocryptfs on shared storage does not receive as much testing as the -usual (exclusive) use-case. Please test your workload in advance -and report any problems you may hit. - -More info: https://github.com/rfjakob/gocryptfs/issues/156 - -#### -suid, -nosuid -Enable (`-suid`) or disable (`-nosuid`) suid and sgid executables in a gocryptfs -mount (default: `-nosuid`). If both are specified, `-nosuid` takes precedence. -You need root permissions to use `-suid`. - -#### -zerokey -Use all-zero dummy master key. This options is only intended for -automated testing as it does not provide any security. - -COMMON OPTIONS -============== - -Options that apply to more than one action are listed below. -Each options lists where it is applicable. Again, usually you -don't need any. - -#### -config string -Use specified config file instead of `CIPHERDIR/gocryptfs.conf`. - -Applies to: all actions that use a config file: mount, `-fsck`, `-passwd`, `-info`, `-init`. - -#### -cpuprofile string -Write cpu profile to specified file. - -Applies to: all actions. - -#### -d, -debug -Enable debug output. - -Applies to: all actions. - -#### -extpass CMD [-extpass ARG1 ...] -Use an external program (like ssh-askpass) for the password prompt. -The program should return the password on stdout, a trailing newline is -stripped by gocryptfs. If you just want to read from a password file, see `-passfile`. - -When `-extpass` is specified once, the string argument will be split on spaces. -For example, `-extpass "md5sum my password.txt"` will be executed as -`"md5sum" "my" "password.txt"`, which is NOT what you want. - -Specify `-extpass` twice or more to use the string arguments as-is. -For example, you DO want to call `md5sum` like this: -`-extpass "md5sum" -extpass "my password.txt"`. - -If you want to prevent splitting on spaces but don't want to pass arguments -to your program, use `"--"`, which is accepted by most programs: -`-extpass "my program" -extpass "--"` - -Applies to: all actions that ask for a password. - -#### -fido2 DEVICE_PATH -Use a FIDO2 token to initialize and unlock the filesystem. -Use "fido2-token -L" to obtain the FIDO2 token device path. - -Applies to: all actions that ask for a password. - -#### -masterkey string -Use a explicit master key specified on the command line or, if the special -value "stdin" is used, read the masterkey from stdin, instead of reading -the config file and asking for the decryption password. - -Note that the command line, and with it the master key, is visible to -anybody on the machine who can execute "ps -auxwww". Use "-masterkey=stdin" -to avoid that risk. - -The masterkey option is meant as a recovery option for emergencies, such as -if you have forgotten the password or lost the config file. - -Even if a config file exists, it will not be used. All non-standard -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: - - -masterkey=6f717d8b-6b5f8e8a-fd0aa206-778ec093-62c5669b-abd229cd-241e00cd-b4d6713d - -masterkey=stdin - -Applies to: all actions that ask for a password. - -#### -memprofile string -Write memory profile to the specified file. This is useful when debugging -memory usage of gocryptfs. - -Applies to: all actions. - -#### -o COMMA-SEPARATED-OPTIONS -For compatibility with mount(1), options are also accepted as -"-o COMMA-SEPARATED-OPTIONS" at the end of the command line. -For example, "-o q,zerokey" is equivalent to passing "-q -zerokey". - -Note that you can only use options that are understood by gocryptfs -with "-o". If you want to pass special flags to the kernel, you should -use "-ko" (*k*ernel *o*ption). This is different in libfuse-based -filesystems, that automatically pass any "-o" options they do not -understand along to the kernel. - -Example: - - gocryptfs /tmp/foo /tmp/bar -o q,zerokey - -Applies to: all actions. - -#### -openssl bool/"auto" -Use OpenSSL instead of built-in Go crypto (default "auto"). Using -built-in crypto is 4x slower unless your CPU has AES instructions and -you are using Go 1.6+. In mode "auto", gocrypts chooses the faster -option. - -Applies to: all actions. - -#### -passfile FILE [-passfile FILE2 ...] -Read password from the specified plain text file. The file should contain exactly -one line (do not use binary files!). -A warning will be printed if there is more than one line, and only -the first line will be used. A single -trailing newline is allowed and does not cause a warning. - -Pass this option multiple times to read the first line from multiple -files. They are concatenated for the effective password. - -Example: - - echo hello > hello.txt - echo word > world.txt - gocryptfs -passfile hello.txt -passfile world.txt - -The effective password will be "helloworld". - -Applies to: all actions that ask for a password. - -#### -q, -quiet -Quiet - silence informational messages. - -Applies to: all actions. - -#### -trace string -Write execution trace to file. View the trace using "go tool trace FILE". - -Applies to: all actions. - -#### -wpanic -When encountering a warning, panic and exit immediately. This is -useful in regression testing. - -Applies to: all actions. - -#### \-\- -Stop option parsing. Helpful when CIPHERDIR may start with a -dash "-". - -Applies to: all actions. - -EXCLUDING FILES -=============== - -In reverse mode, it is possible to exclude files from the encrypted view, using -the `-exclude`, `-exclude-wildcard` and `-exclude-from` options. - -`-exclude` matches complete paths, so `-exclude file.txt` only excludes a file -named `file.txt` in the root of the mounted filesystem; files named `file.txt` -in subdirectories are still visible. (This option is kept for compatibility -with the behavior up to version 1.6.x) - -`-exclude-wildcard` matches files anywhere, so `-exclude-wildcard file.txt` -excludes files named `file.txt` in any directory. If you want to match complete -paths, you can prefix the filename with a `/`: `-exclude-wildcard /file.txt` -excludes only `file.txt` in the root of the mounted filesystem. - -If there are many exclusions, you can use `-exclude-from` to read exclusion -patterns from a file. The syntax is that of `-exclude-wildcard`, so use a -leading `/` to match complete paths. - -The rules for exclusion are that of [gitignore](https://git-scm.com/docs/gitignore#_pattern_format). -In short: - -1. A blank line matches no files, so it can serve as a separator - for readability. -2. A line starting with `#` serves as a comment. Put a backslash (`\`) - in front of the first hash for patterns that begin with a hash. -3. Trailing spaces are ignored unless they are quoted with backslash (`\`). -4. An optional prefix `!` negates the pattern; any matching file - excluded by a previous pattern will become included again. It is not - possible to re-include a file if a parent directory of that file is - excluded. Put a backslash (`\`) in front of the first `!` for - patterns that begin with a literal `!`, for example, `\!important!.txt`. -5. If the pattern ends with a slash, it is removed for the purpose of the - following description, but it would only find a match with a directory. - In other words, `foo/` will match a directory foo and paths underneath it, - but will not match a regular file or a symbolic link foo. -6. If the pattern does not contain a slash `/`, it is treated as a shell glob - pattern and checked for a match against the pathname relative to the - root of the mounted filesystem. -7. Otherwise, the pattern is treated as a shell glob suitable for - consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the - pattern will not match a `/` in the pathname. For example, - `Documentation/*.html` matches `Documentation/git.html` but not - `Documentation/ppc/ppc.html` or `tools/perf/Documentation/perf.html`. -8. A leading slash matches the beginning of the pathname. For example, - `/*.c` matches `cat-file.c` but not `mozilla-sha1/sha1.c`. -9. Two consecutive asterisks (`**`) in patterns matched against full - pathname may have special meaning: - i. A leading `**` followed by a slash means match in all directories. - For example, `**/foo` matches file or directory `foo` anywhere, - the same as pattern `foo`. `**/foo/bar` matches file or directory - `bar` anywhere that is directly under directory `foo`. - ii. A trailing `/**` matches everything inside. For example, `abc/**` - matches all files inside directory `abc`, with infinite depth. - iii. A slash followed by two consecutive asterisks then a slash matches - zero or more directories. For example, `a/**/b` matches `a/b`, - `a/x/b`, `a/x/y/b` and so on. - iv. Other consecutive asterisks are considered invalid. - - -EXAMPLES -======== - -### Init - -Create an encrypted filesystem in directory "mydir.crypt", mount it on "mydir": - - mkdir mydir.crypt mydir - gocryptfs -init mydir.crypt - gocryptfs mydir.crypt mydir - -### Mount - -Mount an encrypted view of joe's home directory using reverse mode: - - mkdir /home/joe.crypt - gocryptfs -init -reverse /home/joe - gocryptfs -reverse /home/joe /home/joe.crypt - -### fstab - -Adding this line to `/etc/fstab` will mount `/tmp/cipher` to `/tmp/plain` on boot, using the -password in `/tmp/passfile`. Use `sudo mount -av` to test the line without having -to reboot. Adjust the gocryptfs path acc. to the output of the command `which gocryptfs`. -Do use the `nofail` option to prevent an unbootable system if the gocryptfs mount fails (see -the `-nofail` option for details). - - /tmp/cipher /tmp/plain fuse./usr/local/bin/gocryptfs nofail,allow_other,passfile=/tmp/password 0 0 - -EXIT CODES -========== - -0: success -6: CIPHERDIR is not an empty directory (on "-init") -10: MOUNTPOINT is not an empty directory -12: password incorrect -22: password is empty (on "-init") -23: could not read gocryptfs.conf -24: could not write gocryptfs.conf (on "-init" or "-password") -26: fsck found errors -other: please check the error message - -See also: https://github.com/rfjakob/gocryptfs/blob/master/internal/exitcodes/exitcodes.go - -SEE ALSO -======== -mount(2) fuse(8) fallocate(2) encfs(1) diff --git a/Documentation/SECURITY.md b/Documentation/SECURITY.md deleted file mode 100644 index 47edd1d..0000000 --- a/Documentation/SECURITY.md +++ /dev/null @@ -1 +0,0 @@ -This page has been moved to https://nuetzlich.net/gocryptfs/security/ . diff --git a/Documentation/XFSTESTS.md b/Documentation/XFSTESTS.md deleted file mode 100644 index bbf19e7..0000000 --- a/Documentation/XFSTESTS.md +++ /dev/null @@ -1,699 +0,0 @@ -# xfstests results - -Results of running [fuse-xfstests](https://github.com/rfjakob/fuse-xfstests) -against gocryptfs. - -## Failures - -### generic/035 - -Known [issue](https://github.com/hanwen/go-fuse/issues/55) in the -go-fuse library. Unlikely to have real-world impact. - -### generic/062 - -Only `user.\*` xattrs are supported, others are rejected. - -### generic/093 - -`security.\*` xattrs are not supported. - -### generic/097 - -`trusted.\*` xattrs are not supported. - -### generic/228 - -`ulimit -f` is not implemented in gocryptfs. - -### generic/273 - -Needs further analysis: -``` -_porter 28 not complete -cp: cannot create regular file '/var/tmp/check-gocryptfs/scratchdir/sub_28/origin/file_548': No such file or directory -``` - -### generic/403 - -`trusted.\*` xattrs are not supported. - -### generic/426, generic/467, generic/477 - -Needs further analysis. - -Failure related to the new system call open_by_handle_at(2) -([lwn article](https://lwn.net/Articles/375888/)). - -### generic/466 - -Harmless output caused by the fact that gocryptfs is not backed by -a block device. - -### generic/484 - -Needs further analysis: `record lock is not preserved across execve(2)` - -### generic/488 - -Needs further analysis: `Too many open files` - -## Full Test Output - -``` -0 jakob@brikett:~/code/fuse-xfstests$ sudo ./check-gocryptfs -gocryptfs v1.7.1; go-fuse v2.0.2-4-g8458b8a; 2019-10-10 go1.12.9 linux/amd64 -fuse-xfstests nlink0/dff383ab -Thu 10 Oct 2019 08:31:43 PM UTC - -FSTYP -- fuse.gocryptfs -PLATFORM -- Linux/x86_64 brikett 5.2.17-200.fc30.x86_64 -MKFS_OPTIONS -- /var/tmp/check-gocryptfs/scratchdev -MOUNT_OPTIONS -- -o context=system_u:object_r:root_t:s0 /var/tmp/check-gocryptfs/scratchdev /var/tmp/check-gocryptfs/scratchdir - -generic/001 6s ... 5s -generic/002 14s ... 1s -generic/003 [not run] atime related mount options have no effect on fuse.gocryptfs -generic/004 [not run] O_TMPFILE is not supported -generic/005 14s ... 0s -generic/006 16s ... 3s -generic/007 19s ... 7s -generic/008 [not run] xfs_io fzero failed (old kernel/wrong fs?) -generic/009 [not run] xfs_io fzero failed (old kernel/wrong fs?) -generic/010 15s ... 1s -generic/011 18s ... 4s -generic/012 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/013 97s ... 12s -generic/014 16s ... 2s -generic/015 2s ... 1s -generic/016 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/017 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/018 [not run] defragmentation not supported for fstype "fuse.gocryptfs" -generic/020 14s ... 1s -generic/021 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/022 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/023 15s ... 1s -generic/024 [not run] fs doesn't support RENAME_NOREPLACE -generic/025 [not run] fs doesn't support RENAME_EXCHANGE -generic/026 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/027 269s ... 101s -generic/028 19s ... 6s -generic/029 0s ... 0s -generic/030 2s ... 1s -generic/031 [not run] xfs_io fcollapse failed (old kernel/wrong fs?) -generic/032 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/033 [not run] xfs_io fzero failed (old kernel/wrong fs?) -generic/034 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/035 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/035.out.bad) - --- tests/generic/035.out 2018-01-20 14:29:39.062451937 +0100 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/035.out.bad 2019-10-10 22:34:13.622100130 +0200 - @@ -1,3 +1,7 @@ - QA output created by 035 - overwriting regular file: - +nlink is 1, should be 0 - +res=0 dev=54 ino=5770027 mode=100644 nlink=1 uid=0 - overwriting directory: - +t_rename_overwrite: fstat(3): No such file or directory - +res=-1 dev=0 ino=0 mode=0 nlink=0 uid=0 - ... - (Run 'diff -u tests/generic/035.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/035.out.bad' to see the entire diff) -generic/036 24s ... 10s -generic/037 5s ... 3s -generic/038 [not run] This test requires at least 10GB free on /var/tmp/check-gocryptfs/scratchdir to run -generic/039 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/040 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/041 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/043 [not run] fuse.gocryptfs does not support shutdown -generic/044 [not run] fuse.gocryptfs does not support shutdown -generic/045 [not run] fuse.gocryptfs does not support shutdown -generic/046 [not run] fuse.gocryptfs does not support shutdown -generic/047 [not run] fuse.gocryptfs does not support shutdown -generic/048 [not run] fuse.gocryptfs does not support shutdown -generic/049 [not run] fuse.gocryptfs does not support shutdown -generic/050 [not run] fuse.gocryptfs does not support shutdown -generic/051 [not run] fuse.gocryptfs does not support shutdown -generic/052 [not run] fuse.gocryptfs does not support shutdown -generic/053 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/054 [not run] fuse.gocryptfs does not support shutdown -generic/055 [not run] fuse.gocryptfs does not support shutdown -generic/056 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/057 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/058 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/059 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/060 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/061 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/062 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/062.out.bad) - --- tests/generic/062.out 2018-01-20 14:29:39.067451950 +0100 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/062.out.bad 2019-10-10 22:34:34.290196880 +0200 - @@ -13,7 +13,7 @@ - - *** set/get one initially empty attribute - # file: SCRATCH_MNT/reg - -user.name - +user.name="" - - *** overwrite empty, set several new attributes - ... - (Run 'diff -u tests/generic/062.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/062.out.bad' to see the entire diff) -generic/063 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/064 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/065 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/066 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/067 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/068 [not run] fuse.gocryptfs does not support freezing -generic/069 221s ... 216s -generic/070 22s ... 9s -generic/071 1s ... 1s -generic/072 [not run] xfs_io fcollapse failed (old kernel/wrong fs?) -generic/073 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/074 766s ... 328s -generic/075 69s ... 11s -generic/076 [not run] require /var/tmp/check-gocryptfs/scratchdev to be local device -generic/077 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/078 [not run] fs doesn't support RENAME_WHITEOUT -generic/079 [not run] file system doesn't support chattr +ia -generic/080 16s ... 2s -generic/081 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/082 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/083 15s ... 8s -generic/084 6s ... 5s -generic/085 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/086 15s ... 1s -generic/087 14s ... 0s -generic/088 14s ... 0s -generic/089 48s ... 53s -generic/090 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/091 19s ... 5s -generic/092 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/093 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/093.out.bad) - --- tests/generic/093.out 2018-06-27 21:12:13.629235005 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/093.out.bad 2019-10-10 22:45:22.194059446 +0200 - @@ -1,15 +1,22 @@ - QA output created by 093 - - **** Verifying that appending to file clears capabilities **** - -file = cap_chown+ep - +Failed to set capabilities on file '/var/tmp/check-gocryptfs/testdir/093.file' (Operation not supported) - +usage: setcap [-q] [-v] [-n ] (-r|-|) [ ... (-r|-|) ] - + - ... - (Run 'diff -u tests/generic/093.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/093.out.bad' to see the entire diff) -generic/094 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/095 [not run] fio utility required, skipped this test -generic/096 [not run] xfs_io fzero failed (old kernel/wrong fs?) -generic/097 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/097.out.bad) - --- tests/generic/097.out 2018-06-27 21:12:13.630235009 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/097.out.bad 2019-10-10 22:45:23.382064979 +0200 - @@ -110,18 +110,16 @@ - *** Test out the trusted namespace *** - - set EA : - +setfattr: TEST_DIR/foo: Operation not supported - - set EA : - - ... - (Run 'diff -u tests/generic/097.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/097.out.bad' to see the entire diff) -generic/098 1s ... 0s -generic/099 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/100 31s ... 15s -generic/101 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/102 32s ... 20s -generic/103 2s ... 2s -generic/104 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/105 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/106 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/107 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/108 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/109 3s ... 4s -generic/110 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/111 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/112 72s ... 14s -generic/113 155s ... 38s -generic/114 [not run] device block size: 4096 greater than 512 -generic/115 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/116 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/117 8s ... 10s -generic/118 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/119 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/120 [not run] atime related mount options have no effect on fuse.gocryptfs -generic/121 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/122 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/123 15s ... 1s -generic/124 19s ... 4s -generic/126 15s ... 1s -generic/127 540s ... 458s -generic/128 1s ... 0s -generic/129 43s ... 33s -generic/130 4s ... 5s -generic/131 16s ... 2s -generic/132 26s ... 17s -generic/133 79s ... 22s -generic/134 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/135 0s ... 1s -generic/136 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/137 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/138 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/139 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/140 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/141 1s ... 0s -generic/142 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/143 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/144 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/145 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/146 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/147 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/148 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/149 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/150 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/151 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/152 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/153 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/154 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/155 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/156 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/157 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/158 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/159 [not run] file system doesn't support chattr +i -generic/160 [not run] file system doesn't support chattr +i -generic/161 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/162 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/163 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/164 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/165 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/166 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/167 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/168 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/169 1s ... 1s -generic/170 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/171 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/172 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/173 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/174 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/175 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/176 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/177 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/178 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/179 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/180 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/181 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/182 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/183 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/184 16s ... 0s -generic/185 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/186 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/187 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/188 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/189 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/190 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/191 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/192 [not run] atime related mount options have no effect on fuse.gocryptfs -generic/193 16s ... 1s -generic/194 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/195 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/196 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/197 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/198 17s ... 1s -generic/199 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/200 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/201 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/202 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/203 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/204 20s ... 16s -generic/205 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/206 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/207 15s ... 1s -generic/208 216s ... 201s -generic/209 46s ... 31s -generic/210 15s ... 1s -generic/211 15s ... 1s -generic/212 15s ... 1s -generic/213 47s ... 20s -generic/214 15s ... 1s -generic/215 17s ... 4s -generic/216 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/217 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/218 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/219 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/220 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/221 17s ... 2s -generic/222 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/223 [not run] can't mkfs fuse.gocryptfs with geometry -generic/224 129s ... 34s -generic/225 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/226 14s ... 12s -generic/227 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/228 1s -generic/229 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/230 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/231 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/232 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/233 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/234 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/235 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/236 17s ... 2s -generic/237 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/238 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/239 21s ... 10s -generic/240 [not run] fs block size must be larger than the device block size. fs block size: 4096, device block size: 4096 -generic/241 87s ... 74s -generic/242 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/243 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/244 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/245 15s ... 1s -generic/246 16s ... 2s -generic/247 32s ... 13s -generic/248 12s ... 2s -generic/249 14s ... 4s -generic/250 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/252 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/253 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/254 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/255 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/256 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/257 12s ... 3s -generic/258 12s ... 2s -generic/259 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/260 [not run] FITRIM not supported on /var/tmp/check-gocryptfs/scratchdir -generic/261 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/262 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/263 30s ... 22s -generic/264 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/265 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/266 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/267 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/268 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/269 34s ... 29s -generic/270 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/271 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/272 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/273 56s ... 42s -generic/274 42s ... 50s -generic/275 46s ... 54s -generic/276 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/277 [not run] file system doesn't support chattr +A -generic/278 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/279 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/280 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/281 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/282 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/283 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/284 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/285 12s ... 3s -generic/286 35s ... 26s -generic/287 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/288 [not run] FITRIM not supported on /var/tmp/check-gocryptfs/scratchdir -generic/289 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/290 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/291 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/292 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/293 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/294 0s ... 1s -generic/295 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/296 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/297 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/298 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/299 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/300 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/301 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/302 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/303 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/304 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/305 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/306 12s ... 3s -generic/307 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/308 11s ... 2s -generic/309 13s ... 4s -generic/310 76s ... 69s -generic/311 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/312 2s ... 1s -generic/313 14s ... 7s -generic/314 11s ... 2s -generic/315 10s ... 3s -generic/316 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/317 0s ... 1s -generic/318 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/319 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/320 59s ... 48s -generic/321 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/322 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/323 131s ... 123s -generic/324 [not run] defragmentation not supported for fstype "fuse.gocryptfs" -generic/325 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/326 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/327 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/328 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/329 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/330 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/331 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/332 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/333 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/334 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/335 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/336 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/337 0s ... 0s -generic/338 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/339 13s ... 17s -generic/340 9s ... 4s -generic/341 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/342 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/343 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/344 36s ... 24s -generic/345 18s ... 8s -generic/346 29s ... 22s -generic/347 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/348 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/352 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/353 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/354 7s ... 5s -generic/355 11s ... 3s -generic/356 [not run] swapfiles are not supported -generic/357 [not run] swapfiles are not supported -generic/358 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/359 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/360 10s ... 2s -generic/361 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/362 [not run] this test requires richacl support on $SCRATCH_DEV -generic/363 [not run] this test requires richacl support on $SCRATCH_DEV -generic/364 [not run] this test requires richacl support on $SCRATCH_DEV -generic/365 [not run] this test requires richacl support on $SCRATCH_DEV -generic/366 [not run] this test requires richacl support on $SCRATCH_DEV -generic/367 [not run] this test requires richacl support on $SCRATCH_DEV -generic/368 [not run] this test requires richacl support on $SCRATCH_DEV -generic/369 [not run] this test requires richacl support on $SCRATCH_DEV -generic/370 [not run] this test requires richacl support on $SCRATCH_DEV -generic/371 159s ... 145s -generic/372 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/373 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/374 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/375 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/376 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/377 0s ... 0s -generic/378 11s ... 2s -generic/379 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/380 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/381 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/382 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/383 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/384 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/385 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/386 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/387 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/388 [not run] require /var/tmp/check-gocryptfs/scratchdev to be local device -generic/389 [not run] O_TMPFILE is not supported -generic/390 [not run] fuse.gocryptfs does not support freezing -generic/391 19s ... 9s -generic/392 [not run] fuse.gocryptfs does not support shutdown -generic/393 0s ... 1s -generic/394 16s ... 4s -generic/395 [not run] No encryption support for fuse.gocryptfs -generic/396 [not run] No encryption support for fuse.gocryptfs -generic/397 [not run] No encryption support for fuse.gocryptfs -generic/398 [not run] No encryption support for fuse.gocryptfs -generic/399 [not run] No encryption support for fuse.gocryptfs -generic/400 [not run] disk quotas not supported by this filesystem type: fuse.gocryptfs -generic/401 0s ... 0s -generic/402 [not run] no kernel support for y2038 sysfs switch -generic/403 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/403.out.bad) - --- tests/generic/403.out 2018-06-27 21:12:13.659235117 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/403.out.bad 2019-10-10 23:16:53.291612884 +0200 - @@ -1,2 +1,204 @@ - QA output created by 403 - +setfattr: /var/tmp/check-gocryptfs/scratchdir/file: Operation not supported - +/var/tmp/check-gocryptfs/scratchdir/file: trusted.small: Operation not supported - +setfattr: /var/tmp/check-gocryptfs/scratchdir/file: Operation not supported - +setfattr: /var/tmp/check-gocryptfs/scratchdir/file: Operation not supported - +setfattr: /var/tmp/check-gocryptfs/scratchdir/file: Operation not supported - +setfattr: /var/tmp/check-gocryptfs/scratchdir/file: Operation not supported - ... - (Run 'diff -u tests/generic/403.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/403.out.bad' to see the entire diff) -generic/404 [not run] xfs_io finsert failed (old kernel/wrong fs?) -generic/405 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/406 3s ... 2s -generic/407 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/408 [not run] Dedupe not supported by test filesystem type: fuse.gocryptfs -generic/409 [not run] require /var/tmp/check-gocryptfs/scratchdev to be local device -generic/410 [not run] require /var/tmp/check-gocryptfs/scratchdev to be local device -generic/411 [not run] require /var/tmp/check-gocryptfs/scratchdev to be local device -generic/412 2s ... 3s -generic/413 [not run] /var/tmp/check-gocryptfs/scratchdev fuse.gocryptfs does not support -o dax -generic/414 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/415 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/416 105s ... 102s -generic/417 [not run] fuse.gocryptfs does not support shutdown -generic/418 [not run] require /var/tmp/check-gocryptfs/testdev to be valid block disk -generic/419 [not run] No encryption support for fuse.gocryptfs -generic/420 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/421 [not run] No encryption support for fuse.gocryptfs -generic/422 15s ... 3s -generic/423 15s ... 4s -generic/424 [not run] file system doesn't support any of /usr/bin/chattr +a/+c/+d/+i -generic/425 [not run] xfs_io fiemap failed (old kernel/wrong fs?) -generic/426 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/426.out.bad) - --- tests/generic/426.out 2018-06-27 21:12:13.662235128 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/426.out.bad 2019-10-10 23:18:59.149149787 +0200 - @@ -1,5 +1,3077 @@ - QA output created by 426 - test_file_handles TEST_DIR/426-dir -d - test_file_handles TEST_DIR/426-dir - +open_by_handle(/var/tmp/check-gocryptfs/testdir/426-dir/file000000) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/426-dir/file000001) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/426-dir/file000002) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/426-dir/file000003) returned 116 incorrectly on a linked file! - ... - (Run 'diff -u tests/generic/426.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/426.out.bad' to see the entire diff) -generic/427 6s ... 4s -generic/428 15s ... 4s -generic/429 [not run] No encryption support for fuse.gocryptfs -generic/430 15s ... 4s -generic/431 15s ... 4s -generic/432 15s ... 4s -generic/433 15s ... 3s -generic/434 15s ... 4s -generic/435 [not run] No encryption support for fuse.gocryptfs -generic/436 15s ... 4s -generic/437 16s ... 4s -generic/438 57s ... 40s -generic/439 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/440 [not run] No encryption support for fuse.gocryptfs -generic/441 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/443 15s ... 4s -generic/444 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/445 15s ... 4s -generic/446 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/447 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/448 15s ... 4s -generic/449 [not run] ACLs not supported by this filesystem type: fuse.gocryptfs -generic/450 [not run] Only test on sector size < half of block size -generic/451 45s ... 34s -generic/452 0s ... 0s -generic/453 1s ... 1s -generic/454 1s ... 1s -generic/455 [not run] This test requires a valid $LOGWRITES_DEV -generic/456 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/457 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/458 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/459 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/460 [not run] This test requires at least 1GB free on /var/tmp/check-gocryptfs/scratchdir to run -generic/461 [not run] fuse.gocryptfs does not support shutdown -generic/462 [not run] /var/tmp/check-gocryptfs/scratchdev fuse.gocryptfs does not support -o dax -generic/463 [not run] Reflink not supported by test filesystem type: fuse.gocryptfs -generic/464 94s ... 63s -generic/465 5s ... 2s -generic/466 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/466.out.bad) - --- tests/generic/466.out 2018-06-27 21:12:13.667235146 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/466.out.bad 2019-10-10 23:22:14.813984485 +0200 - @@ -1,2 +1,3 @@ - QA output created by 466 - Silence is golden - +blockdev: ioctl error on BLKGETSIZE64: Inappropriate ioctl for device - ... - (Run 'diff -u tests/generic/466.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/466.out.bad' to see the entire diff) -generic/467 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/467.out.bad) - --- tests/generic/467.out 2018-06-27 21:12:13.667235146 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/467.out.bad 2019-10-10 23:22:22.324016522 +0200 - @@ -1,9 +1,82 @@ - QA output created by 467 - test_file_handles TEST_DIR/467-dir -dp - test_file_handles TEST_DIR/467-dir -rp - +open_by_handle(/var/tmp/check-gocryptfs/testdir/467-dir/file000000) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/467-dir/file000001) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/467-dir/file000002) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/467-dir/file000003) returned 116 incorrectly on a linked file! - ... - (Run 'diff -u tests/generic/467.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/467.out.bad' to see the entire diff) -generic/468 [not run] fuse.gocryptfs does not support shutdown -generic/469 16s ... 4s -generic/470 [not run] This test requires a valid $LOGWRITES_DEV -generic/471 [not run] xfs_io pwrite failed (old kernel/wrong fs?) -generic/472 [not run] swapfiles are not supported -generic/474 [not run] fuse.gocryptfs does not support shutdown -generic/475 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/476 979s ... 940s -generic/477 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/477.out.bad) - --- tests/generic/477.out 2018-06-27 21:12:13.669235154 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/477.out.bad 2019-10-10 23:38:15.586197915 +0200 - @@ -1,5 +1,48 @@ - QA output created by 477 - test_file_handles after cycle mount - +open_by_handle(/var/tmp/check-gocryptfs/testdir/file000000) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/file000001) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/file000002) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/file000003) returned 116 incorrectly on a linked file! - +open_by_handle(/var/tmp/check-gocryptfs/testdir/file000004) returned 116 incorrectly on a linked file! - ... - (Run 'diff -u tests/generic/477.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/477.out.bad' to see the entire diff) -generic/478 44s ... 35s -generic/479 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/480 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/481 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/482 [not run] This test requires a valid $LOGWRITES_DEV -generic/483 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/484 - output mismatch (see /home/jakob.donotbackup/code/fuse-xfstests/results//generic/484.out.bad) - --- tests/generic/484.out 2018-06-27 21:12:13.676235180 +0200 - +++ /home/jakob.donotbackup/code/fuse-xfstests/results//generic/484.out.bad 2019-10-10 23:38:54.996371174 +0200 - @@ -1,2 +1,3 @@ - QA output created by 484 - +record lock is not preserved across execve(2) - Silence is golden - ... - (Run 'diff -u tests/generic/484.out /home/jakob.donotbackup/code/fuse-xfstests/results//generic/484.out.bad' to see the entire diff) -generic/485 [not run] xfs_io finsert failed (old kernel/wrong fs?) -generic/486 0s ... 0s -generic/487 [not run] This test requires a valid $SCRATCH_LOGDEV -generic/488 1s ... 2s -generic/489 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/490 15s ... 3s -generic/491 [not run] fuse.gocryptfs does not support freezing -generic/492 [not run] xfs_io label support is missing (missing syscall?) -generic/493 [not run] swapfiles are not supported -generic/494 [not run] swapfiles are not supported -generic/495 [not run] swapfiles are not supported -generic/496 [not run] swapfiles are not supported -generic/497 [not run] swapfiles are not supported -generic/498 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/499 [not run] xfs_io fcollapse failed (old kernel/wrong fs?) -generic/500 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/501 [not run] Reflink not supported by scratch filesystem type: fuse.gocryptfs -generic/502 [not run] require /var/tmp/check-gocryptfs/scratchdev to be valid block disk -generic/503 [not run] xfs_io fpunch failed (old kernel/wrong fs?) -generic/504 15s ... 3s -shared/001 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/002 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/003 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/004 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/006 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/008 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/009 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/010 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/032 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/272 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/289 [not run] not suitable for this filesystem type: fuse.gocryptfs -shared/298 [not run] not suitable for this filesystem type: fuse.gocryptfs -Ran: generic/001 generic/002 generic/003 generic/004 generic/005 generic/006 generic/007 generic/008 generic/009 generic/010 generic/011 generic/012 generic/013 generic/014 generic/015 generic/016 generic/017 generic/018 generic/020 generic/021 generic/022 generic/023 generic/024 generic/025 generic/026 generic/027 generic/028 generic/029 generic/030 generic/031 generic/032 generic/033 generic/034 generic/035 generic/036 generic/037 generic/038 generic/039 generic/040 generic/041 generic/043 generic/044 generic/045 generic/046 generic/047 generic/048 generic/049 generic/050 generic/051 generic/052 generic/053 generic/054 generic/055 generic/056 generic/057 generic/058 generic/059 generic/060 generic/061 generic/062 generic/063 generic/064 generic/065 generic/066 generic/067 generic/068 generic/069 generic/070 generic/071 generic/072 generic/073 generic/074 generic/075 generic/076 generic/077 generic/078 generic/079 generic/080 generic/081 generic/082 generic/083 generic/084 generic/085 generic/086 generic/087 generic/088 generic/089 generic/090 generic/091 generic/092 generic/093 generic/094 generic/095 generic/096 generic/097 generic/098 generic/099 generic/100 generic/101 generic/102 generic/103 generic/104 generic/105 generic/106 generic/107 generic/108 generic/109 generic/110 generic/111 generic/112 generic/113 generic/114 generic/115 generic/116 generic/117 generic/118 generic/119 generic/120 generic/121 generic/122 generic/123 generic/124 generic/126 generic/127 generic/128 generic/129 generic/130 generic/131 generic/132 generic/133 generic/134 generic/135 generic/136 generic/137 generic/138 generic/139 generic/140 generic/141 generic/142 generic/143 generic/144 generic/145 generic/146 generic/147 generic/148 generic/149 generic/150 generic/151 generic/152 generic/153 generic/154 generic/155 generic/156 generic/157 generic/158 generic/159 generic/160 generic/161 generic/162 generic/163 generic/164 generic/165 generic/166 generic/167 generic/168 generic/169 generic/170 generic/171 generic/172 generic/173 generic/174 generic/175 generic/176 generic/177 generic/178 generic/179 generic/180 generic/181 generic/182 generic/183 generic/184 generic/185 generic/186 generic/187 generic/188 generic/189 generic/190 generic/191 generic/192 generic/193 generic/194 generic/195 generic/196 generic/197 generic/198 generic/199 generic/200 generic/201 generic/202 generic/203 generic/204 generic/205 generic/206 generic/207 generic/208 generic/209 generic/210 generic/211 generic/212 generic/213 generic/214 generic/215 generic/216 generic/217 generic/218 generic/219 generic/220 generic/221 generic/222 generic/223 generic/224 generic/225 generic/226 generic/227 generic/228 generic/229 generic/230 generic/231 generic/232 generic/233 generic/234 generic/235 generic/236 generic/237 generic/238 generic/239 generic/240 generic/241 generic/242 generic/243 generic/244 generic/245 generic/246 generic/247 generic/248 generic/249 generic/250 generic/252 generic/253 generic/254 generic/255 generic/256 generic/257 generic/258 generic/259 generic/260 generic/261 generic/262 generic/263 generic/264 generic/265 generic/266 generic/267 generic/268 generic/269 generic/270 generic/271 generic/272 generic/273 generic/274 generic/275 generic/276 generic/277 generic/278 generic/279 generic/280 generic/281 generic/282 generic/283 generic/284 generic/285 generic/286 generic/287 generic/288 generic/289 generic/290 generic/291 generic/292 generic/293 generic/294 generic/295 generic/296 generic/297 generic/298 generic/299 generic/300 generic/301 generic/302 generic/303 generic/304 generic/305 generic/306 generic/307 generic/308 generic/309 generic/310 generic/311 generic/312 generic/313 generic/314 generic/315 generic/316 generic/317 generic/318 generic/319 generic/320 generic/321 generic/322 generic/323 generic/324 generic/325 generic/326 generic/327 generic/328 generic/329 generic/330 generic/331 generic/332 generic/333 generic/334 generic/335 generic/336 generic/337 generic/338 generic/339 generic/340 generic/341 generic/342 generic/343 generic/344 generic/345 generic/346 generic/347 generic/348 generic/352 generic/353 generic/354 generic/355 generic/356 generic/357 generic/358 generic/359 generic/360 generic/361 generic/362 generic/363 generic/364 generic/365 generic/366 generic/367 generic/368 generic/369 generic/370 generic/371 generic/372 generic/373 generic/374 generic/375 generic/376 generic/377 generic/378 generic/379 generic/380 generic/381 generic/382 generic/383 generic/384 generic/385 generic/386 generic/387 generic/388 generic/389 generic/390 generic/391 generic/392 generic/393 generic/394 generic/395 generic/396 generic/397 generic/398 generic/399 generic/400 generic/401 generic/402 generic/403 generic/404 generic/405 generic/406 generic/407 generic/408 generic/409 generic/410 generic/411 generic/412 generic/413 generic/414 generic/415 generic/416 generic/417 generic/418 generic/419 generic/420 generic/421 generic/422 generic/423 generic/424 generic/425 generic/426 generic/427 generic/428 generic/429 generic/430 generic/431 generic/432 generic/433 generic/434 generic/435 generic/436 generic/437 generic/438 generic/439 generic/440 generic/441 generic/443 generic/444 generic/445 generic/446 generic/447 generic/448 generic/449 generic/450 generic/451 generic/452 generic/453 generic/454 generic/455 generic/456 generic/457 generic/458 generic/459 generic/460 generic/461 generic/462 generic/463 generic/464 generic/465 generic/466 generic/467 generic/468 generic/469 generic/470 generic/471 generic/472 generic/474 generic/475 generic/476 generic/477 generic/478 generic/479 generic/480 generic/481 generic/482 generic/483 generic/484 generic/485 generic/486 generic/487 generic/488 generic/489 generic/490 generic/491 generic/492 generic/493 generic/494 generic/495 generic/496 generic/497 generic/498 generic/499 generic/500 generic/501 generic/502 generic/503 generic/504 shared/001 shared/002 shared/003 shared/004 shared/006 shared/008 shared/009 shared/010 shared/032 shared/272 shared/289 shared/298 -Not run: generic/003 generic/004 generic/008 generic/009 generic/012 generic/016 generic/017 generic/018 generic/021 generic/022 generic/024 generic/025 generic/026 generic/031 generic/032 generic/033 generic/034 generic/038 generic/039 generic/040 generic/041 generic/043 generic/044 generic/045 generic/046 generic/047 generic/048 generic/049 generic/050 generic/051 generic/052 generic/053 generic/054 generic/055 generic/056 generic/057 generic/058 generic/059 generic/060 generic/061 generic/063 generic/064 generic/065 generic/066 generic/067 generic/068 generic/072 generic/073 generic/076 generic/077 generic/078 generic/079 generic/081 generic/082 generic/085 generic/090 generic/092 generic/094 generic/095 generic/096 generic/099 generic/101 generic/104 generic/105 generic/106 generic/107 generic/108 generic/110 generic/111 generic/114 generic/115 generic/116 generic/118 generic/119 generic/120 generic/121 generic/122 generic/134 generic/136 generic/137 generic/138 generic/139 generic/140 generic/142 generic/143 generic/144 generic/145 generic/146 generic/147 generic/148 generic/149 generic/150 generic/151 generic/152 generic/153 generic/154 generic/155 generic/156 generic/157 generic/158 generic/159 generic/160 generic/161 generic/162 generic/163 generic/164 generic/165 generic/166 generic/167 generic/168 generic/170 generic/171 generic/172 generic/173 generic/174 generic/175 generic/176 generic/177 generic/178 generic/179 generic/180 generic/181 generic/182 generic/183 generic/185 generic/186 generic/187 generic/188 generic/189 generic/190 generic/191 generic/192 generic/194 generic/195 generic/196 generic/197 generic/199 generic/200 generic/201 generic/202 generic/203 generic/205 generic/206 generic/216 generic/217 generic/218 generic/219 generic/220 generic/222 generic/223 generic/225 generic/227 generic/229 generic/230 generic/231 generic/232 generic/233 generic/234 generic/235 generic/237 generic/238 generic/240 generic/242 generic/243 generic/244 generic/250 generic/252 generic/253 generic/254 generic/255 generic/256 generic/259 generic/260 generic/261 generic/262 generic/264 generic/265 generic/266 generic/267 generic/268 generic/270 generic/271 generic/272 generic/276 generic/277 generic/278 generic/279 generic/280 generic/281 generic/282 generic/283 generic/284 generic/287 generic/288 generic/289 generic/290 generic/291 generic/292 generic/293 generic/295 generic/296 generic/297 generic/298 generic/299 generic/300 generic/301 generic/302 generic/303 generic/304 generic/305 generic/307 generic/311 generic/316 generic/318 generic/319 generic/321 generic/322 generic/324 generic/325 generic/326 generic/327 generic/328 generic/329 generic/330 generic/331 generic/332 generic/333 generic/334 generic/335 generic/336 generic/338 generic/341 generic/342 generic/343 generic/347 generic/348 generic/352 generic/353 generic/356 generic/357 generic/358 generic/359 generic/361 generic/362 generic/363 generic/364 generic/365 generic/366 generic/367 generic/368 generic/369 generic/370 generic/372 generic/373 generic/374 generic/375 generic/376 generic/379 generic/380 generic/381 generic/382 generic/383 generic/384 generic/385 generic/386 generic/387 generic/388 generic/389 generic/390 generic/392 generic/395 generic/396 generic/397 generic/398 generic/399 generic/400 generic/402 generic/404 generic/405 generic/407 generic/408 generic/409 generic/410 generic/411 generic/413 generic/414 generic/415 generic/417 generic/418 generic/419 generic/420 generic/421 generic/424 generic/425 generic/429 generic/435 generic/439 generic/440 generic/441 generic/444 generic/446 generic/447 generic/449 generic/450 generic/455 generic/456 generic/457 generic/458 generic/459 generic/460 generic/461 generic/462 generic/463 generic/468 generic/470 generic/471 generic/472 generic/474 generic/475 generic/479 generic/480 generic/481 generic/482 generic/483 generic/485 generic/487 generic/489 generic/491 generic/492 generic/493 generic/494 generic/495 generic/496 generic/497 generic/498 generic/499 generic/500 generic/501 generic/502 generic/503 shared/001 shared/002 shared/003 shared/004 shared/006 shared/008 shared/009 shared/010 shared/032 shared/272 shared/289 shared/298 -Failures: generic/035 generic/062 generic/093 generic/097 generic/403 generic/426 generic/466 generic/467 generic/477 generic/484 -Failed 10 of 507 tests - -Runtime was 4046 seconds -``` diff --git a/Documentation/duplicate-inodes.txt b/Documentation/duplicate-inodes.txt deleted file mode 100644 index 36fb89c..0000000 --- a/Documentation/duplicate-inodes.txt +++ /dev/null @@ -1,36 +0,0 @@ -ls: cannot access foo: No such file or directory -ls: cannot access foo: No such file or directory -ls: cannot access foo: No such file or directory -ls: cannot access foo: No such file or directory -36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo -36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo -36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo -36962337 -rwxrwxrwx 1 u1026 users 0 Nov 11 18:00 foo - - -u1026@d8min:/mnt/synology/public/tmp/g1$ strace -e lstat -p 8899 -f -Process 8899 attached with 10 threads -2017/11/11 18:12:21 Dispatch 238: LOOKUP, NodeId: 1. names: [foo] 4 bytes -[pid 10539] lstat("/mnt/synology/public/tmp/g1/a/4DZNVle_txclugO7n_FRIg", 0xc4241adbe8) = -1 ENOENT (No such file or directory) -2017/11/11 18:12:21 Serialize 238: LOOKUP code: OK value: {NodeId: 0 Generation=0 EntryValid=1.000 AttrValid=0.000 Attr={M00 SZ=0 L=0 0:0 B0*0 i0:0 A 0.000000000 M 0.000000000 C 0.000000000}} -2017/11/11 18:12:22 Dispatch 239: LOOKUP, NodeId: 1. names: [foo] 4 bytes -[pid 8903] lstat("/mnt/synology/public/tmp/g1/a/Xsy8mhdcIh0u9aiI7-iLiw", {st_mode=S_IFREG|0777, st_size=0, ...}) = 0 -2017/11/11 18:12:22 Serialize 239: LOOKUP code: OK value: {NodeId: 3 Generation=4 EntryValid=1.000 AttrValid=1.000 Attr={M0100777 SZ=0 L=1 1026:100 B0*16384 i0:36962337 A 1510419642.457639700 M 1510419642.457639700 C 1510419702.353712800}} - - -Call Trace: - -nodefs/fsops.go (c *rawBridge) Lookup - nodefs/fsops.go (c *FileSystemConnector) internalLookup - nodefs/inode.go (n *Inode) GetChild - pathfs/pathfs.go (n *pathInode) GetAttr - pathfs/pathfs.go (n *pathInode) GetPath - nodefs/inode.go (n *Inode) Parent() - pathfs/loopback.go (fs *loopbackFileSystem) GetAttr - -Call Trace 2 (new child): - -nodefs/fsops.go (c *rawBridge) Lookup - nodefs/fsops.go (c *FileSystemConnector) internalLookup - pathfs/pathfs.go (n *pathInode) Lookup - pathfs/pathfs.go (n *pathInode) findChild diff --git a/Documentation/extractloop.md b/Documentation/extractloop.md deleted file mode 100644 index eca66bb..0000000 --- a/Documentation/extractloop.md +++ /dev/null @@ -1,56 +0,0 @@ -# extractloop.bash results - -Memory usage stabilises at 141MiB, we do not run out of fds, -and the iteration time is stable around 38 seconds: - -![](extractloop_plot_csv.png) - -What the extractloop stress test does is (top comment in `tests/stress_tests/extractloop.bash`): - -``` -# Mount a gocryptfs filesystem somewhere on /tmp, then run two parallel -# infinite loops inside that do the following: -# 1) Extract linux-3.0.tar.gz -# 2) Verify the md5sums -# 3) Delete, go to (1) -# -# This test is good at discovering inode-related memory leaks because it creates -# huge numbers of files. -``` - -Test output (trimmed for brevity): -``` -~/go/src/github.com/rfjakob/gocryptfs/tests/stress_tests$ ./extractloop.bash - -20803 (process ID) old priority 0, new priority 19 -Testing gocryptfs -Test dir: /tmp/extractloop_tmpdir/SMc -'/tmp/extractloop.csv' -> '/tmp/extractloop_tmpdir/SMc.csv' -[looper 2] Starting -[looper 1] Starting -[looper 2] Iteration 1 done, 42 seconds, RSS 36020 kiB -[looper 1] Iteration 1 done, 42 seconds, RSS 36020 kiB -[looper 2] Iteration 2 done, 40 seconds, RSS 45400 kiB -[looper 1] Iteration 2 done, 40 seconds, RSS 45400 kiB -[looper 1] Iteration 3 done, 40 seconds, RSS 53396 kiB -[looper 2] Iteration 3 done, 40 seconds, RSS 53396 kiB -[looper 1] Iteration 4 done, 39 seconds, RSS 64588 kiB -[looper 2] Iteration 4 done, 40 seconds, RSS 64588 kiB -[looper 1] Iteration 5 done, 40 seconds, RSS 64588 kiB -[looper 2] Iteration 5 done, 39 seconds, RSS 64588 kiB -[looper 1] Iteration 6 done, 39 seconds, RSS 71628 kiB -[...] -[looper 1] Iteration 945 done, 38 seconds, RSS 140832 kiB -[looper 2] Iteration 946 done, 38 seconds, RSS 140832 kiB -[looper 1] Iteration 946 done, 38 seconds, RSS 140832 kiB -[looper 1] Iteration 947 done, 37 seconds, RSS 140832 kiB -[looper 2] Iteration 947 done, 37 seconds, RSS 140832 kiB -[looper 1] Iteration 948 done, 38 seconds, RSS 140832 kiB -[looper 2] Iteration 948 done, 38 seconds, RSS 140832 kiB -[looper 1] Iteration 949 done, 38 seconds, RSS 140832 kiB -[looper 2] Iteration 949 done, 38 seconds, RSS 140832 kiB -[looper 1] Iteration 950 done, 38 seconds, RSS 140832 kiB -[looper 2] Iteration 950 done, 38 seconds, RSS 140832 kiB -[looper 1] Iteration 951 done, 38 seconds, RSS 140832 kiB -[looper 2] Iteration 951 done, 38 seconds, RSS 140832 kiB -``` diff --git a/Documentation/extractloop_plot_csv.png b/Documentation/extractloop_plot_csv.png deleted file mode 100644 index 788fdb97302c13e379164a074e28caa1c178e224..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46953 zcmeEucT|(l_ht|jk*4xNkj_U11u0TOFQTG!=}1SUSLqO%(rkc@8mctuNN-8Ns7MRF zC4?%34ib7OJJIj&w|n-SJ$v^5&Cx?3d1vOGxpVJxpZm=F@}c&9`g3gOAP@+>+Jn1# z5XfmE2;>y<>>2Qv`#f?S;0KkrikiV$@CrWr0*ox8WAhtE1CN)ZC#fvDZRZ4i*Qf`=tv@ed)8bb7kIzr=o{`*8;L;vuf}%~L<@ zw`7{!p*#KOIG~=7XIj>Hn>yVY)I?V1dtW9+D(JqB!bd*&8RZ(|`o8MvDOd(;nnmzt zSnQB=Gfo`F8s#J z@_kHLNs!UW9wVx-Gpz9QJ4SpC>Uc-|eQpC&&qCvgPx;OTMvWP5fS)fY$QHK=i!IR5dTVbT3 zjFtN%DxLhB9>Zu<@aOr%%;s6MM!qll@rNDo>Yetwh5Kc&LL-dTAWR8no*6pgveg^o z(RYYu4w-J%Uz3fWci(nHQzgPY;A_Kcl;m3q7skPGK8~K3yC@)An8Kc5^CZ+h|M-Bz z$#j_0>fs~A($A#o6I-j^s_$|&o4rRClksjAs56}}3lg_`KZq5J;0Q^+&GRVMC_g`oO(g$UhU~xd%3X+cxrr8H@OHlDmiav`X$b zjlcEUeRd~E!oPTD0=oK(0ur>ePeOiP6$wl5i#xue;J@Oo){ohW#ik(p9`QVls=a1a zCIk~+KR|9tR0mf#7WIWRZGG?$*0|3ds;X3f;&s%nB$HKOC0#BkjGGqT-Ru4I)4z*okdpe-Kg8$m0XK5y7(r^`O-B-67{yH%TToAYPMFoF_9@5#y4O(ZRK*b!6!JDF|I2 zX{{;V_9n!Krp>tJ_Y^IAv6{I|sK-Lumm$>gn~bA=2j7|D;;o+|2n)fmU8w`i>LbF} z8cBuiHW>?Zi{Z}D17q1QIQs4C{>|uO;UUSF8due>T)_mIspi7VkedA3Ldy$iX1wPR z*U|Bpw(WP^T2AZBX)=P{Z#NKiS}_calekWH3do%WBx!A+TWWHA9S;f5{(kEcM8%}- z$9?ZQ=aFYpjRRj(PS)n4Zw{`w9bR@6-{y1pl0{-O8@6R;6>5O1eX|fBx)gN8ovBcUI63YN zn(8BL^>J&s?8cQg&MK4-k(74dmYochJISXH%DIp7x4Sgjs&wF~8JyPL1~fZp$?Mp1 z{w&5OQ0us?(c(MB)^Nuis{!w}wc$dgqh#&zpak-NOmG7UVc%%vv_Hg%RNOk`R=>72 zwOTirb~3!YyEMIw<{zVn;%{{aF7Pn?X>v*|bJ?F;`WMFLhKvj(`&zG3Q(2Q5Noh@D zW-MfuahbG!VA->qU4TZLT^JH_1s3dc@z0*J4j&S{J0q@C(ePcdx;kVze^>3j*b$!o zkHPiYyve1qUUu5`{PQaW;u!fqea?B)Q-A;Zf>}l5Kc9b5`9C=_WvPh(Ta-TqB>GaD z31opGuDr$b3h-(h(TtGB$XP*%4`Z8&b^V3^r$+zu`hWM1|8FDx|BV7q`+to%|9?*Z zWBdtjM-w1oS2X;i!8}JhS6ll|jX$9?U$*)_`3p1AiF`QRWe$6mca>SgkXeJd zTPN1nx}5xlZ}ez11w>gVxy8%Azwm8WF4Oy884MrP_{ZnECU*jvs$N@6xRAeiq)gsg zjl!-nAZ!e3e&Jqg}IBOU4a!Q1V{vW#UVd7)qS4<5UjnGztl0 z(HPpc6I`CQQ%UjZ=j0>14C{LG3Wk?eMSQpRTr{_2H^&m-zM9rfwN5AG&2Js>=ktt; z7(e*=ei@}{hLbW2{%Nx|lH4OAJNQD&1`ipOSR{4k2V0<5c_at3@=UH!wk3sIO%F`SJ-D8-D6S6v_rzY|uro$Qgp`MpE({AANLX7;AhK*v z^l*(7Om4F&Y)zX{?pcAG>v$67t6o0Qq#?DA8dq3< zb|2uaq}AbRmk*6I{hUE3*~UelGd|`1;L0s!leYD!4~9kjd?ip%%*rQ=W6BxVp$w1~ds~6?uk$bl%oxr5kHAkHkSJl;t*~D(&ab2v&T|oiikOqmSa{ zNJ>h+qKWtD=@_XYbQ}(QTc1?(a;Z12xO1&r&klGxFOV^Leu0efC{)>qK^Ob=#ec)?L#k=RTBnkNtGPjl2713$0YA zbUuAZ6%!5jaDSawMA8on5&4#uLX_HkIm>2!E4t&@1L@Zvm3b@`9g@Z$phO+ zJ9_BqmzaWN5?YZPbky=gC~d2VWwk|b^VE0u9^Zz(8<|~-`+_1zXk7Q|D_YGrMR4i9May#LPG!irDN zD~P0SlKIE8X-OqF_FnvHO4;*N-IJ;6UHW{d=2>$i-=5vChyde9Z26BRTpv6@Omo#* z2RJcG#ow`R@T}aHx;LBio4pnoTX=Wg#Mv7#SwV-fy9?_TojHm0X8h7ZMmfv0!VZiq zQklgw8;@FRODE;P#uM)f7Q{>*zd}DQECQbOn#^>bROXwo2fiFA1v(JkYW#jx3~0$e zd9e4RF3D3+mu&jgJ?vwKa{qR`*?a$qP$98=x7%r~OV;L7*+`lLb-LdzWsZWk|AL@H zN2PB=z+9-}(!_pH6-g^OnEyb-aD`i9M=HHEbXf*CZflw>m(RgxdUc8gi?RE3ZM<7C zm;ZHFbfJR(qLOs}$&{bq`>EFE#M09Dcp{`lMuBY3nJj;7g!hBCGp}11X(3Z0=&_&L z`x4Ad4Ss6|I!*yOWBA2D7x~bA5zPdFp!r699Nr!){gABqNWSovgTQsffT0!7<={Kk z{;T2z4t)AliI862)hXcoN zl#myt!PzA*$FA`Qq!AUb#)MpFfWMLbpzRpCG^O19bRiKK6>SJb7U%wRW3`J$2^S>C z!ZtQ526V$bp0Jme46Gq*o3s&bk&-40-;u*+CuMu7WB1+lm5 zNyo1J@V=UJsB_oRyWIW4FQW{;jz(V$o)S7K!b7Xr8Ak6+H`5#yuQglRaPK*@L-yEd zBhx)X?^|4|EeS#FBw56kN!4qMldwy&-Q$WRjmD`}mmS5IjT1p6wwm~z?b@nX_X!7^ zf~G)}f6yd5-hmx~SVg*H8$x1hb=Q%xjhyTVx6)>#9QOfLcQe7ZD*Fuo^yUdmf_B{?Wt!wcXk1lh} zfDnJZDJQ`5)Z)9v9QXUbZx6d);qA-isH!QEE-;c_nrP``hejnu&sM(NlGj$9B~-3U4M()?Lo~TWlW?W4Sier zL}KEYxUe!x4Ox&c|C&3)Zs{P!bC%)v8?5T!2D)Ov=FT;^g+tB=uGn{J)unFuC012@ zS%U2=?sZ!}Qypqx1ox8QU03G+a4n~TkhtHJf*V*1g%ZczcA&0Ltk}~hS4T9*3g*i%!tvtPqL+}?UfPROe+sg@5$;a58b*_mJwXLTXj(8HgTs8 zhA4Ht;*4;g=s|=kjx@!mhPdr6?<<`h!Icx-QEOFx!(%^*J%^lWeBZf3taXf;9U{c! z(?E-vuiW?jnN0J%;~xWOJsw6oi@W{dWto;!BxG% zwS!({RYr&+%h<@sxW?rt@}IBPqW63wF6uD|0AH_s6Cjowf_Btxr>jGvW{YLDn4pU1HzS55uJY=xa>P+Rxk%9l0MY zYpj1Bacrx$rr73xW&gu4vMPh1UQuRJRFD6hL&g83R`BGka8o?w50eOu5hCX@FDUhG zQa}PSFOvUCOj;-D(U0R>%YO;D9db{(FURtt4TH6nQ??9EFKHP1D>Q!oLk09+CO3}p z3IC85-tTkAcmAHlGrRAl-^d$aPCFoF!;Mn8{&C@_-+gyc{^SOg-`py=7OvGp_0I3C z)9BEL)_Hk=7mh8ZAK6XZ1D``6*t?E2)&rfnA5yOi?W;UEnWOQSVR8hW?mF0u<)z3t zD0YP!#@C1y*z=|52A(Hh(&7N9K!we)lXbSSMlF(3Xx-JqEfJ5wsHGbgUfsLPq|oBg z4@czWC6H*Cy3t3Kq_$U zq>^_HGWwi3L6r;?IFHf_iJ=dc%yw>BOvBPY_i9AKbd>zih6*@;iE@*o zoxJBPOTJz&Uw(S z!@1JgEA8+gLuwFL_DO=|K6yr*zW`CP53}?2Mf894q#Ip1gLsuN%L;*vy83ZT=K_99 z5^)IF(rkk9$$@y#jz;ECmDNEBY{K8YXUQ=0dNm@x6wG)5YqDU{oEzmgGZt*rrpC*s}YTpjB;4cx26{p(H>CVenO<7jB=Y(I@~ zI&L@Gcx`3rc;^mZ`rz00RtBqZ%XXFyck9b(q*~6^GSs`}TO*R<#yj1+ATtQxQP_OP zZM<*A-9L6;XW?6^ap5LQSnu}o)jxM3paI9z_y!L$?9UJ>>P?DF6S9w|CoGxsjP0Hj ziMja^h$JqnJOP@~+%%^v&C!+eGXk+g3aIKySAz zmmA^1hKXcrMV^$cHz>8&ooJ5iKI^^MlFxj}-58t=^S;$fmdcA9dll7F7D+dt-t zdv$&B7y@Lbwd?`(S)Pm{)+%6>9fA)nJz1O=0{wwsv(ioM1@ig-#;C>yZ&oHQq5R=K#f7AD8X;k;?30*M01 zB4gJ#OlK}k@T8r*PQ9yT?ezQ(NxFZ5l_|7~#){vWJ+{_u2hVV`J4teW5AzQhokzST z58qA-OOxU8i+1j(&HEz>CCPk%aMWsA?LJm89v&?PGw>iaJumO7TC{d(Gu8eG%%lL+ z!YiD^4;z4|tp_TLZ{*A?ihCozKi%6p9K5a7B`jcdz1+V;%W?Im4Y1Y0t|#h5eQJOw zJ-d6lo;V+E-a0Nk_0~YH1RItT7B^mDgVily7c%om|Lj(;Z(VP7J#gAtv_4k_kQmmU zZe*{mtOPPJ&~Oyp{gV209|yi!8FTp$+5+MU2FQng#lnA+ygk`WO7yp8VZ-Z!A}2}o zDL!I}&7pa~xr0m#M3e)BoU-v~xo#m;TAa|`l|a5V zCBLWEASwvu7O(kSj*T{s!LQeHGbQF80c;(pn%AwUW zb!|^U(am}Tc>9*~CLmeP?TOecwf|=ZbeRY86wLx$-^&4R+{B?L;tZWwL1TZ$h{$wt5uFhq@By(& z7Zqq>(2FW?+y!V@HlWV%0(wlCpyi9!Zi9P{Dk*yZ4&U~B2w-=9Opi^#tGo_n1!Lgc zrm|qt5bNu)@qFX^Yf$!ruVRbrvM&C*8JTRwg+_En0zbZgRr65N@^n(^dO3D)_r9rz zHnW)jzhD`{Tkn88t>t_>OY^I{OefmQO? zr&~4v`yKt}vYy*~_{ZVme+bRd(22vh%#&}I>MLV{0!+iG0;4$@^;&{evLW!UIU*-JWb zAw)?)h{F0ToyL{O$Ay&>ck`j=Gmsh6m{4eOL!S=k>cpjoOFhNp^ANdwCmw-o6k^ksc?!6rF`jLu5c5OyV0UC zQ99b^3EY>uW|_N@j9LMgOEdw|*!{RH2yTRl%tqMidw_Ovn^uByb zL;A;kCZ8m7aW$GQArTTj`ZJeFAhCH#z3F0amz$I9gFj#>Gn=n z2Cf0(`4AYT!jncz_(L?w;wfdPrmW5B_s8M=XU_!t<-JC9Tb_$9-G0tag31;gk=&Du zI?SO%KGur{Fk$B>2cNH&n+_b`B08K(zsPK&8nxFt60WahKHWful_fd-7Z)zV7?AcU z52MxL#|wT$8e(uoOf(E%S)6V7VxsZ*?SSIsTD_aLhW2c#r6J!ut)10^vvGV-o#f(# zHoEMRWI=ahX=E9IH*TZ0Rx#dq=JMy>n9H94)AV*ptGb(kz1zD!rI530bHXo;KnQqQ z^cXD4?TgR@`c<$pvL!<@M`^x@a}Ab^tM_0~Ha%*}4r@ERDqJ!AIq*S$wEz`iyD;s# zsa{Y0z{||_Si;!tc%pw^9!~65)33D%8m(@Z8wCd4!I^-5^5MOYd!D|!jIh~8tsz7e zK4=FF-1(=YLvvfC8sOPfehC2BUfdn%nII=bxAU<-xOP~!aiX1ArSEz*<=qCoX_~SC z_piA8GqBKh$+vOB`&s~&VL{VzG(P|AY)h-OzMVj&ZI%=h^0dQ z`{4}n4Ka)QeD+DsiuFMhZ^9f) z6mCl2d?S!p)WOn0#~d#sS8`S{ZW^71M$?f3&kvRG?Tynw*J+N|>`rIz=l9WluO-w; z6hxq{(Wwk6=emueV_(nnH_7a!5wG~SN7%j&+b(%|l0Y}BoJ&@>8rW@~rk4$#Ff@aBlV#e>vbrbI(Eg*ohiQWu9bj~_~Mi9jpd5$-?~X- zyE?@r4}adCTqZVdEk`oH@ig(bO7>I|4J#L41)U-iFl-WZx+^#2yPQ1K-{yJ<4=+ql z9t${=_SO$GA4GFqk^N4!Yg67jwfxR~9*k;1DmsReQ%o^+Bc#4cFzl=1Kh$zUxX%@D zl;n~bF#KaLIG;pH{&LFu3bjT6_o8h) ze*Z-@pC7YN+IA)7Va4yb@@+yd9ckuziI{YKTp%7^$oi@(;iqqo3XB-WW1@hM72*73 z`9b&Ob+G>Re;TWF$kU_{2^MP*KA>~1xZ<};*_3%?>ypB^c+RU)zKzgls)+MfN8JF9 zWzZVgGaqg^;)`^B0toFoS3w(4yV>fFa#{-$mnzxUYo$#qT0=gr|3y39rav&nci8!9 zLwMJQ*o`gsV0wemiFJDHcS|Dc%|)XSkNigqR~@VnGFB1v5%zrg+Tx`uW3t-ULfR^R z>z$zf0fyW%59-A}Z+CCk?%a7g`p{Vj>s%)u;<&RF6}AoG$47g_Md$7P>2D zuwr^z_jy79l|V#+rD17Epy%hp+Yj)Mv~52=(fWCjBRX#SiOvY`kddlt_p~Eh-=080 zX^WUEdjyYbh6%|pwb%CvAP#-5Fr_W^@O>nB4p>8?6f^p5F?EXUduk>|@=#m?Zu4@V9YX0;}-X!$GAu!^EeO zic9OR?gBciGBVdrHhxce@Mz|!0`vu01=0QSS08SNJ^U0+v|gk^L@^)~IDh7CFs$FU zNoqhlj}Qi~Ca)m?5gz3;WX#*}PUN4J0o?(4x(*loIE&x*XHJRJXuP#y{Dxce>vrgLfvc7fPwC%te2ji(Srsqkd^!RiN>{ThW${};klip(ZQ7eVNK5Bpc0wwv zr6zM9w~s7hz>` zf9c!r*5ckKs;A$y+RWPY@Op=?awMzCitdhO86K0_8L(yt0;79qdu<$d^{VpKrE1P_ zEa84SgL_VbZ|az;IEgEN0C%>jOKI2I=e`AIux)bP~s4p_VDkq59mi`+HGD0rJ`zs<>ScU)K z;ci=PjjZ_R-HOk%Z9PNfPa6BWm6y`3JjOmxZr3tNCRxdi;5wktN;kB7*~RhbMMye1 z3vrfLVngd3HIDr%;M`_A5Avo`PMH$058r>y6yLt9&<4B_RbpbOywuD=@Cr*D*``^k zfQQVa65Ip043{^WMfCJPE%@jtNm7Z?=8J*N_5JgYon?vuJ4D$oD zlI}gs;Q<@UCYQvONq?rpPJg-E5|Bfm=A-F~ktI^&a`hP1S{v;V_8**np@@*8I&Jph{Puk1ed+s`L9 zVXqIq03lRzVQR^(Gm%|>_`7ypY-GtTKWYl%HR|G{! zd$a9C7d!UVwfi|COY_M4tM@Bt-j19v)T`_F0%1$+BL)N+UL^NK8o}I7VZqmZy<@4! zZ*_dEZ~4~lQ;zircihd%S}b!cyWvekIaz`O$mPGSyeBo%$@HMsgXcVDCnR#hy12NpLDscNS%joqUF_)eTPW%j(S1ySDr_uVw%D!2%LbPc zDW;BMEDOKB9EeP04FWdMp`P+#XnQFW(`_}TSy9*LR^7zi?Or}GV&uIX zgJKp_fER$_#12u{0d^Izvn_!;N7dNh%dYuF-3&-gCJy4RJ-0JA!C8kXlp5tq1NUk@ zE9`Qr<@ffDOR~HxwA7@~F2G|fhsy{*i=~NEJ{u}C+l;&|M7H}_w^KkS*F0~(Cz$x!96{qoQ( zdqmyQv{XpJ$)?p&_*&SnH|#-xKiN(S^Ea*2Sy}&i#JVFr3XYJmC1J(>vojmJP0YA*a)gh;|x#jB+nUejzJ@w zlbeR-AW6fY{7l(aHP8;PacR3WS6P^vZ-62VwaI!ZMJs4;ui&c?i*{}vh|u!QC@+m= zKWFlc;&F8c^P%0jOb6c%uOJ7Y`p=wt@NhMLFuCJ|mb#1pWa-KJ&T{{F8L(D={O_#! z0)|E$v~3>D-ez-R_qYc;!m4STHD+vK+4`*yal5~0s1y^EoGv$8W5v$AgXCKSdx{Pv zM5qqcjt5Q>{d!P~OPxM5gOzcL z=6(7-nFH(GF!QmI^H}Q&x9I&9XfdcrXwBx5OB1?b55>hX@tfnMRqjh%u5S|5_?`1xHu4|6D4TM- z2XM23aoWRixl8c;+uLk;0PjK;P{6s7@8nQgLZeA)Jl^JdBtbiJ}j#M;?c*q{k3CY_nB zl(t!hqt!!%hfO~*+)2xxKZr04Dm_P=wv5lWIi6{ipmlZ#k*kKr7tm0m=gTElMxtrt zabqkVnayn@K9Y=z(U?%ark&9$sH0S=nd2zHhOoL?uyY;%m8RC{Ag^s5)>)#A z8Lh%Tn>A5FI05FGhsM%CiJnF0mZdTm^8gNf+5cXo_lw|_2qI%`s6wmuinjF=#w!_h zrzWk@mzX|dV-22L;4d)Fe>n$ROD2S~ticSE=j+0f13u+9+?ONGnLB(ht)-LGaYhs$ z*)P&DMy6!Seg@QDF)Uz6p-kiV!B6pP=k@F&$gG}P8(X*5|f{m>1tyjZyrL&@qIRKghmV|VGVd(%_ zq!9nND&eY?hkq1!+FKIk-{65Oi-8O zjVa9XDY*DKC8LE#aMfPE^k+FLbR1U^ipzPU$-VkO`PKYr55-pWx};P=t(7e>!rPIF z@Hb``qBEu=mN&$?`)3Yqgvg=?i&Cc-1E2e}tDf5!RHWz#!#D9tJID3Q%+1yVbWC;h z9WFl=G1?MmYg2V^8sevF2X=2GUbWg&!Ed@fVKAr)ZF*+6p$NS*pcSxaL{WF)c%F8y zw`^g1l-R`3;qCu$jFPk4Z3$%DbY zQ25ASqAx0YWrZG@f);YW6Ww?6WOJG<#U~sE{a3D#X6%exFix&2_HCznxU_$lF!wR>T}cbPlg6F};YQf9CPc z+~XtC7{Oh8jD_?M21bKE4D)Q54ZqsUqqY;`#u zAn*Amu&$0Q*>ik0Qru_HB(>VJ?s(O3r=mg4YM|IH@-zi|!-Tdg+LC_>`T#R4s>%J& zt$;3~$ew(Ofq*(6%!>Czr|h#_sjXq9UvhME>wRyZBk zu8(NS>gxM~M68#FoNz2ChMj*~jVX`Du#7l{HmUMv7W$1YKo1Qs_JRBW8HD@nk7#wP zZd`9AMsUk(g#N(| zAvUDPv>B6KZWWZ{?#}Z_(Yl6o(Q7xqF`P;C3uiKq;VEcJ8%aOeo{4S~irgxdw~ST6 z*w^A$pjgC&J>^&hvHO?w*xAb9}#yGj8bgh&CjA1}u$Z{M%BTdlAn z9emcS-+!{tu$j#0n#~c?2`6Ijd+WVdbY)wK2$AReVP_!_*$6OFR&K3912!rpjyiFJ z^BEN#TI?S4F)<1vS%vm%hQ60)_-{(nXy58{_3U4e?q51~^Qm`5h`2u2a(~_l3ZRV# zK@|}XV0*hj1WTrh!n)qB8lnG@3#g7k1)*&asPs9ewaV7>u;7m;Rrt4^2i!@YA&Mpc zj#}~sJR00@T~&3vQjcM`Y^a)seiHVmm2DjIg?b@$b9|lyfC`o#Y`jPdikbB(_-7eT z^YV#(n*ymyZE=rgYA5#j?q;dG9mS6SmT+)^9B9?V)z}6gD;i;2Vb`*D?Von~*|#%7 zN?sIcRwmJM-G80{PnV8M4j+<3{3hkp7KyT2`k_9_vzkO(l;gW6sD#+l83)IWcQ!_* zwy}mKep$960~lL^kDArWs18VOzP2wt<1zt1{#tx@9sy#(0tee~*HZvQ2cCvgj2b2# z^BO|sg1u|!!fZJXr?4HeHp~x>MpZ{;+3~8QF^1(G1s6>d!dn3qe7!xer0qV_ntAkm z1Y7z{%pJiy$2lrYa-)?x_fMbJ%}(+TbZL9{C#LBwoKT2x(3f8<^@pK%MvxeHG5WL> z62H}TLGc8ilUhNeJnU5c*zyg`0sGykqseN*tr|+ss#qf`IfVcFBNO_d2(WK%NxgA1 zG&t{6Oiy4hA9%TpUx3Q`BKn4g6IzaXkG(;_}63Ql-%3VQY-X92&omI z`YLpbG*qf>T5p|Wb3-!-xa^9w!}lo&Q9Q?Ol_o`w!4h947GaOu{y6=^Z?6z64WF;GqhTQfU5 z*TRtNl1a4CY?`r|g=4hN?ox6c$Vo|~YSRNj(pJ~2jrW13JXgh=ITx73Y|hKtvH_}> ztPIAW=k3&I(Cg zfSoN?V7$=i!m4=otF4OAdNpxc$w6qKtJn0~Ynm5b6~|Fq2eYYKrPO_|^=g0L691sx z$ZN{)yChu+Mg)Yi>P08_bOwo1LG_>gLz||Z1--@$bdXmU=2p)cRh%g}cUIf(ap-+| zn=&b(XHIEPqK5&+mGwnacC5cz&yfp6ohS2zrMq)96)7@kea6NxjS2G?k%x0j-s!V((P;EYgYODuiV4D>#@_b996(sV3|1H z#bB~7FhV45Bx$v0phbQ<+3P(`IvsAGoPF<@sM)f>*uEOAtQ+g6`GJ#Ihril>%sEaj z$nlUhzFBHJ%@v@=g5<3Q49bW_8mO3pu;#4dIBUK48heJ@`jRd&T=t_VaRdK==bT%U-&P)X2gar@ zkH-3d@_l0i4D_ZvdBN0O#ef$w`evmF3Nt}N0?#l7 zx(m0tN50H7|5=>Hn0>NMgP-TGC^(IO%2bfD+>iT+iZ3QbB}&wY-f;s1Y{9kt=(lo7 z*9uPoMqq)7xZjc@KDiIywNEEgx2(=!HT>6FDmuSv!>4|hxl+ouM*h-{^5~aJva8rM z-YgPaEz%Irl`p~_*-I*#qTT^{h%&!smoSjctBHGK8{`+FO9re4~3f{^226hfTck;29@QIXUi>>sG6ip|f zBbx8Dd)|p5*RAPF&7n1lPteX^t1Q3RtME8nKH+#a-yN+`&9U%LlS+T6`)T-Eq#=Fk z+jN0W$KXCNnT@Rm2C2Jjts?JM?K1&EHC0JTUj93R()aUhj7_z>gc~}~2##rbs-1nV z4bNorE)LB(rQQxJ!zT6U#BM*~2E&>9LTG1X-+nwVES1Th%z_HR0aOK!!RQOa`@gmq zd8{el2+B~0>AAt!l?#CMq>ior5!C|d9FYXdVDC)*tRLMp%zaKA`$%h^3H|5^oBioWQWgk3XA8?97c&zoey9c$iF!;Q^TcQ+2hXRA_%;bDH|_69G%6?^hENIh7q{ zsM6Z41?jEMXV0}$B|)GUoEH#b2Avd%o%!SHofRGGYDk+&KfAok>h8h?*gtwo2;^yNS|a65a@;B) z)+wcJ_2@bsLn#VUM*3;xKXiTn+f4Wf1yJyS zAJ%iUQ|;zZ=Ab}ukmrj*_6)c~YvsSqPe~B%+=Stu$anqUfBQ$n(5}Dz?`ibL zCuS&rNCs%jsI3zm2fZ&+O~HgQ&{kRaPqiXb?y z!!vyiq=lw{76b|O8Y%rWvJ0#Yt4U;{#d{IyX&bpTsoCz+DLa)ZKH#kH4Z(@epyli* z^Tl17_fR8%e|{Gm1UfkH(d9Ho5&j?&tm&WU0-W}zS}0dbet*guA3RSl)<6E`B&77?EpTmMvi4?5%3F`OSp9II3LV8~u-E&Ku z5G*SzQ-ANbHaFjZDd25D<>hJ#?oD2*$rMpOyEI3un$GVHTYSw0z%8jVH7cyPV{%)1N5-BL=UQ zEZ*vsEO0K(-?26~b#1!!e4ircCDmbeMR$>P?pGPr(U}9c$VD*lif{l6Zp}K1|C+a9 zgXg#G#|96?hEkV0RO!8dFo-Jt@0Uia&kC7UdsZ$%5x0nH>gT%5Qc5n@3SCeq zn-bqSsIU1o?*%FN;J>() zet$f(&1?PrFQuQQ94O<0b@~2;kn4#8?o3<+pCPx#DlkYh;SbvN&+_)^vtW(;SZc^t zQeY1?gjJDamV%1@u46;+VKVzeHqhku5p;Skr@_o+n6V5O_xC)>ZneH$if2qnPgjEE zt)>NGha!RQil+mgsg((7NaBM9sjBqgRZ7Ugl@&z#)wp$tDAf|@Y+*Ci0a-~MLO^>A zT;bk-y4%cxY{=TA4t>CXGPW(d&Ci`MJe)e(n~G5kQt43=Eg*33y$A0;iE%~iTknrO%@Ms3EZhVo;rhfzBg2|~CPto;Rblh*y z+tP?T1$^Sa9t*KG{-x=fnhU{b8!>qtZuvXxC_{McgiT@RcUTeOBtJ{TPOwpB2) zr~{ulTty7a7fM-FJ3Q<&owB|-vs7vMzCo*<`{a=uqU3n%CFKQE_2pJ{dd-~{ zf6jm$@bracuHG`zp+DZkh5RT6@K^%>o7ENUi_%(tX_tl2xEr$PoEPEP->-~a zc9$ovs;M6?z_!0i&0o3G*`|2lj(9RtuoZIu*wjp9a=B^cyLit+gRTGamy-=H#1d1O zp}&>t@G$gY(inAXhnhHtd{-HzN690MqR#tu_O#faENQRQn9PBxTAc@{r9Ok90-@6K zKZ>AM-oH)ZsO|Yct-y)|zn7<{E7qPQ2${*5fGLw$QMx3PDXxwxNyCXPx+^CIzH*y?5SxQ(ZeZmCq6NA2s z9KXA|xR1KAp!wUI{F}|f&WL4m*1J;rrK=*y>uzJF{_jSOO@X19HpR(NmqzIsF210) zAwSq*^2g|%XyE4zm@KP1xY*pa;rl{b)W3n;zyd>efx1^=r>+7Y1gtu!CzSEJOk03> zNAx)kz+Bt31*mb%KD)H4=;?UJY6e@bWVT(dl;eQfdZa+g>uDr}5Ozw+_E@-wq8u{) zdSG9I)XhY~oPO=(oa(IcE^hRkudZE(*`ZtX*us1DHs>qrr!i(V6~rJ1ca6ZwmJ$xs z$kDA%xG(wnAz;>BN$h(-*-Q3Dd;Z`6EKrwPea~~MztYa)d1`@yh*boSR4HrTp@1AQ z$^U@gc&0rW7 zL8e9(Pu^^ssjjD9#DXo;Z^(Vfsj$C@Gdz~^7%m$@>LyL3xjEyB1zaDlA}Rvng&PiJ z5lbTkgQJ-+@b_n*t0iv#`WnQOY*}734#QP@OuQ~8W-@Z3!@wgk;;2}FD*#^V7yAym zmfx9wwcYKH3PfM4RPKCsp3K8l>>?eaHYOOD7uH|Y zJIP{QfNT7RDu}psG&;!uYT|s_B2)5nq{Anc$=#l3+=&^p_C5p7R=k$|LFD3McyX6Nj_^|JX}u7L;7T@+B2rUtaG`G6YuRfkNP5~sNVkoTGzW+f(+?{XG|P*(<{mr zo-E?O5Fcthnj#BEK3@If&4ZQiEL1KmnB^#se`;o#%ZKBPbFZllea*c7?r0ILT~}Hn z!v2(o)aNwK8%e&QfB@VP0wpy`CEwm7_PxO8H7@@rjGzw^s&<2oOlV zwS(um_rCAoGb6`epPxa4ZIs64b9lJt6rdfj(BD| zSLBB)F!opkjxfv94K_6QwHCUIrRk~^LPkU)7~Lmhk*FZPa`onCukp8F7X$ECqo6h( zR1d^G5)qGEx!GDk+Dkl`>}PwqvfwZyP23aRk_?N%Z(F?DMfE=R_V(GD^brr{J&!tx z5S%95hZ@kdhEl5h#HXe)2+uGbQ8h4<5x6Y1ERB`?`h~guOnCAWxw&bf(sD|IZ7~5f z_YtupQ?{aPk8g8bv3PyKb)ub3m~Byd`s+_c3W#OByMtSz#Y?+Bt`qRejP3N3j)XPF z?Y>|tBG;3)wiJ=MV`%9~t&GMHcc+iP11@7jgMvuUWPB`_(c!Sa7Rn4ve|Qt^cGV5q z^;VRCqP?k~V?QP`Crez+XCtylFIF6r1UC7v*DuPrO#*Iy5pAlyBDM6(GvM?>$L}`g zv%gvHhN_BKHe9-Eri3sOoRDy8u&RbX?wQ>PRQbTwr=0bMqolI8l36tTWC~;O3K9QH0`cc^4tG!g}x&%Ktz1oxUVVXXU-`%XY$_i5nikt^M`s^n&B9xo?yRWX>C;V{# zet+l+h!d!ZC;aIxUU7`_Im^L&s>^p}z4ET5$I^*LX1}PRyoVxrFXYL5$!5wb6jB~V zmOd#n^MWdyp`%X|Cy8=kpb?{BXDV*1>KrdKmHiffOONprw9UWk6v%s50j>bL|AXT3 ze^)95?0^!2N>38l4dIDZ>}Fe-&dU6LVtr1!omHX2Nw&k|<)RYHvnOJl*!rCu`mA1G zup|pWJJ?iolKmkrkw@s&SJAR#Hu6+^@5bsNNOHjo2&N^@rN&M7W*68BVI^;&w>Bp; zh*Y>m9Ov^7Ndn1cIplw2e z_}SiB|GkX@TYue1kp$%P&z)swORpkK`hq!#n~?M#T$bU|xxM7%=sz@)xYU|w#~WPp zfx8%+g;U+CQ6XpLeWTR(9Qzrh+GM;8u<0$0c4-9S?H>*^+XqVvf2-IEI}A#@WT>*A zteqKfomswDc)@AzCT&p;=bs+J2rC)jUGp!RX4Zync9Re7Q}`P+)@mMRRz*eqm4O4@ zd9^}sE9?f3LhdgJn2t;Jkbx>+Vr}CA7D)OD=~Y=1&78k~yqTGBVJX(QS;u&PZ_ODU zAI<)g`Y1T+8bi}}SGHCaZmaI1z@(z1H!}d~} z<(+U{1M5;yP!RhuT?xMBugmsed!6r$V-ILXi-r2qeD>@3dKxvM-H-xt`|PnFhY-GM zqQZRrGCO^87S%aZU+F!^Hma2Vns|mfD%nHWd9Fa@`9*sKHD&oD%D-MT5{K*8M9hYQ zenLw@^Xe6p>c)*Cm4@Nrx>S*hG-&sPZy*q47faXBba&GXJa^Zvjc}V4LqnXIIslx? z{?41U`W#~hCK$U_W-MFnR_!a|rseZdu62@|O#CbPReywLM?w3s6PyrA3Xb1N5t)qB z!8AF9PJJ;9h`zty-IDDc<|KG%{@~zWF@Pksg~o`GykBWnW5X{k_YXs@J(v}V|`(C@Ho$rzYmcXH0Ux|7zaZl%Wx%sD7T`cZr_D!dUb@+@NX<<}uB{X=OS2f(N znh@`bJof5%RjJ9a=i#F&*S7&kF7uL7BN3Vmu3|O?_GFmR+VK zg@p`7YC*m7KGFO>3g-4afkT4lhN+)mI@j~KDSx>Mg{|6O;U^&SJ(cF$iGzO(C{DtQ zIF2{}>!_IuqSemxc-MmNzD}{70?}Y&Av5y#Pf!Wt(+b6TiKvc+b}yvxN&bi!e~Lc1 zxm#=8SkneMPycJ?$=he@zvx)I8(;an)8eIbl=Xm$Ga2x^%3f$3`ff?x6#;>a9g=Em zi;GaQKC*DfU&9Mu`Y}YWNgGs(mv`#FC7Y@2W;1EeZ*Fy zb{x{U*!ya8^S#j{(SFePPv^%F3q_LT7@3XF?!{d&IF36|A9Wf4q2O31ZAhGGA77*I zRV)ad;Jp)X1Z~a3#+F+z7zjtVP>dG%ba@oddY$pj)({1lE;xp#o}cDwe?LW=<5Q)i zmw-XmZ$(_kxi_?QZtrcE?t3tsLI(yo2D~&JEIfB(7Z;OEUqIaVbli2kBuJibf399o zz=jr99C@=I@kbYqi864CJS?86{6t>%%r^T;IE1%!z4luI>z!e)?l8>T9gMd-cc7nVg4K64ooJIK{YD>DC z^HIZG+Oc*0Ea&#SO=0qNAy4EZ2al?YC-!=OIIcb#`ad4BNb8FjbtIRL=#N=1@k`T+o z`yM*XN@TW%IeBwk_#{sZ!IB-n8pow`+H-c;vNbXN)90lb=v6*TW=wdwlh%N!;r~i( zQaqCeYO{-7zDj5Z>R!HxtcVKV9uVw1bKus#>&IuVA)%qIiOnjC#*G)|#bHtc0x_$B z7fwtEIw(_g5u9=AC(z`RA*jbH7ef;?6T6X3;&46+oa`ri_B>xCv2m)Xm_@|y-?~({ zmJIXZC2BoFK3UA3-66A$xqVlct_xDX6EmXKxrp7 z`77R58G9={7yST_wGMo$)IC)?2Rr}dT&PAQQSrZ!KZ{( z{CpiuL5aDW;pgAi;W2$V{gyS?fhcz-%X?ovp}O@zOnKo${$TtuvykQ8$k}qZ@+ncP(Qn$H|;8$tEAy_ZYuoFPst~n;x zEKkOLKz!ZVa5qZ-BQ=b#u;}GEX&|q`jh27YTtj_}6!T)Cs27$2W<4(^7IL}s&x^Vz zRTU-AA!vBvS*dE(Q%IG4i@oI$hqjwPm!$c-vXqFa`bm{EZ>eesy z&D@d-=hC?>&w1%8UNpqU-q5^p7aH_HXg;I=w6I0#`(;RBsD?a7Knk$z=uiVov&gH! zXzNc_>vuRkwg-;bdd@nMcD$GroUs#T>LCCVXoT_Ocylb6q@?mE z9wSbgRzqaNM$3)->AcT#1Ft&5!;_S9vf4#hAqzS#uGs%3Ft*Jx#l}ntb!2-7W-mw^Q&C8&f|a-SuP;_GS*yRh9MEHKv&o$q(eX7b zUnh0JawA8IDor6PCTKNp%xPj88(X%EvPRLkTu$UkT zma)Bz&So(AnRZwT*EIP~TLq9xrI+e>36h_D(`NRFhqukOQP-UiWmt$b9}h1Q*-$Ju zP3=4_Rp{$={5Z1x`{)~>L{hi*KJVr|AbcO?c%})xcy65ZmyY`y8GH;zGk*-c!A9ij zPWqctd3@`O4Lq$;tZ743A_kJ^6!yne?`H-~stLW|Q|$DQuBPeY$rTr4oqbr1&&P{C zEGJ}KK!G=iy#D?JbMTV?N5V|XI0Oi^X1POc4_ z)tKz*@YNM!1d&^!?~u@us_3~4bMTZ!TtDYZ)g-}@HP57HbT^CB1x>pTCgJ$}4<#kL z%b}+!m(Eov=eXz{Bc6{2c$;+9RJnwTKk)?z$HwQlBUuhk4$+2g8F~6&tZN2<=K}=j z$|VIL)i=M|);%wnbM@mAniyOkl6s;N-Mx*I;5$02#N~|^eWluHLOm?*^<&?UqRGubQgK2;NxxQDeM8=@STqapHr=^ zpEP4OCJe9XZpN!FEMz;Cu!bJOtK+{6Y;1p93BX&HSPXOr7}*g!izf%-CgiNP_NXE` z#rnzopQ6H@FpaFN8t`XoXR4cO=3E*x=?~jB0duY~=`-7SjPBITf|8)ENBb*wd>Ki4MKk%uF_7wHVs&Rk|Q1vx5KiPB|pjLj6myTyw&q z<{zYA4>&#Z1+2i~ZPT2Lc;AxQshjoLI_cBCeNBGj{`w#={N52dP+s8$ZgZ3QGGp@c z3~#Di&!NkQ#GqOHb3)^=xM~ffp@zh+8bt|VX4-7*)p~R+X@a>7Rl`oXTYLEqiO54D zYvW0gK~Fw#SoU7;`h|7%yI!MU>a%kTdcdKOvkJLgs8AvbN}}&sl+J#fq^Qz>Lz3^M zo8j@LXSn&{^4s;jELQCEbay4G`ANI~TjjxP0<#VJXWF`YVG8rTOW$BLaz^o3HFG_R z-3w7x$ zqJY4e4CV)U3Jh_Ey>~G$Y4qQtw(@B0v0RM;SGGH!Sggm-n2=|U+if!g-195YxB5NN z#iffYL^mH9XhPp>B%4-_cyv~-Ma9%ovTR9v@&qireLEPA(g?Kp1=Mmtfl>;5+0AvW6@G=iZHJ`_dHm~E8${jl@FhcFLoK9OK z94yGuPkPj~R_f(Tv<;*U-FTgRSVaNfYo6dzjj}=LYZ~~GP$v$*t)|8Mu-50oZa}?P zAI*KS1IBWNmmSE9Z$2RSLsq{em2ZDE@bB(?NBB;;(K+{a{ma=`m7?_@AT4tOuHD$A zcMvEp6Q?s(*OHo*6cufHXE|+`D%bABqYK&1gmRuH*1fa?hNAUlp5T>g_lAaKLx^>r zny$5!5P<=JA-rwH5X?kpFnQ~tYrtO5d$ZWq@q^jV2^`|`gJe$JG%x=?`pE3~LQH#~ z#S@L7CG7%WO}%U%ZgxqiV%kuA9fcH4R zyUNPzok?kFQGLO*8OSoY?U8%8H>q50Q_YmJu@IaiB(*LgStZq?FJG#!Sa?CK^6orx zZ#>YZx23I=Sxg+*ud8mKdvmjNaZghp_0!5(B5yfPn4TLtc1z$b3W1To$|)Nc-56fx+#Q5{lvs-v!Rn)z(9`wDgeK zmNEKj+%3vjsf(EJ>UmM~-u`uyD^L6h3i{Q0I6zbq6%cZxHOyLpsPGg&u73pC-ujc5 z!1YF6MQNY&!!23!CQk#nVNb@+pK z!%yB2n|)XuvAtftvBAqcPeVM=76UQ#G!0%q`*q^>>_~ymD|Dv+6O2Pr2;?#qTRbQ3JjVH%iiy8b{k-AW>*bX-(eUd% zINl3+TdnoOEof?~l*u3$liV-Qgbs-b3e5YYpW)yTW_EMi_VF`pyt~u6ge|C8Zzpck z(m!>h`CoNB2X4d@d8@L$)*6SkXEoHOftt4to9Vcxcpto;Lq9+ag~<&zQtJG*PG~evHWbD4&Dv=--hN z_B+Gy1G-I00s_8>#X2xfxO8vd8bpaj$cjn$(7gO7O4ZmD!g96V$%}*|F;;-v0i40v z!8K;};(iy?giPH-qYMII(E)RkoOjLR119g>RWi;u9z;?LjYH}a3)JiH){CRsEwkvV zL_ha=d98v0`tsxRVI!B)a?CG_;)@OG4x^|kMj=tNh><3dli?%!n6}4oo*UDyPsqKM zKrbEjJuoY*g-;zQRGb<<$%0H&l%-&~E%up&F zxMoVGdn@+CAg+y%D~{0QQu%R>jIn7O?R>SjRp7w;$BW>2{tFU@G@Bi1ub5n49SP%w zT`{^>NLsliHHVZ!T`F9)4lo;-)E5!%s!o;i^v*)(BuJ}cKbSnJw8DS1 z!1eg<;H7p3<5nqc?QU6feNsEl=??ZEhD}7PPD+GU(Rpi0jAkiHSaW!xK0;b*Zn9|}zBz-vieTz28|RT; z__%EG==GT8jPaNt_j3m)&GIKduG-lCa|aXN*W1F-Y~ng){@;;P^n5KKX96InW9x3& zK;}O_VsbZjH~TTO4C`hA-^QI;zdZ?+-z60|I5+~EH;OpOW>}7ePqNb0t5@$)5~Rm}&caI~#v^LJx3JGHy-&4al%4i?w3ZPb zE?)-M4RRl7^Vk)H6Q-Gsx46*pc=!Szytqk`#+dVnMSn@JMh|k!wt>$rwbqW6>EDrz zoj~Xg8=L*wwbC6cPQ!x8}K+n|9Q`l0?y$5YRfA0|FVsC&20 zG}e2$BHEQy-mqIhV+8~-DwM>c@U{X>D|8Kb1i`8VHTiK$*NXOXlDR{5G*TVb&y(%J zC4ez=SUJQ8MLch2k}RLV@&psrDgJn&#lv(BEbu2%hk$?EcX1z4L2X>+;P$qkw{LcR zsJU6Bamg#eK-%1yt9NnymvhB#ZV%iF@S}`(~3cUm#kZ|^U?}Y8fvJ6B7pJY|`E?5m^ z>x0WHv#lljt*JMH`jJ$B3U=W>xdVIoO|#LJs*GG(kfFTr1>lYGTIUyvb#sgq`}|g2 zEvUF$#!pyC{Z^u0>lqpSObv!nfAJe#B3U2%kQeu@6q73%dq`@(!MXLkEIZ{6>@A(A z$j3aQ#~nSCA`|J%TLK{Nm~eUDDQOSs_Nslmw4Gkn=wb}RbCtd;Y&$(Dh5yzRX1ki2 ziS%MDx@XaSn$Kh*=u1iP&U2E?O)rfEk2UVzSHW@!0UOV2TT|qm(zSWr*0}1uyHL8v zW(lz!z1XmwA2ETER8UYb-LO2j(Zk$1$-z-tRb4PA5%y{>yvuWduxIV2WNd9&p&$3F z&&&$pxhU$x39MdeF$xek<7n^V8qfx)Vlkvb?3Sya#G3$6U=K8a_uwSZsDSl zNB}y2xD~ma;E?r}Ta_@%NqyXW-|Nr&_IYJxW!6UZqnCYtnbD$U**KiN_-vu2w$h^Z zMdl3Ej6c4>J*1ZSDaL(s%w_njQ)NJ~m5lB2)HV;}yV$+~=PDG3|NZ*p7|ey>T8858 zFlXt4VDqW2ke#oRl1-{xAs$x~5bL5KF40paqunC`E*moxvJ`rHrEUS~rWK4&|C30G zb62z2-R}c}*0g3E;+}HzAqA%|@~hc)*CW!-NOfYgg>ImGw12l+?Nhhfm3Lx%X*<8@ zuNsuzr)pSl+s7F%eD7PNv@~HyBdgK~iv>11Z6mi4B;D ztP{%$C8v8I?r6YNT_;Ct+~Y;z*>rEn&~^sf8!A3{$44?>kq!RYLt$Bh2 ztj87kb}^X%Tz6{Tem05QIg{a~H@?Z?>iZE2?9|kNd%IAa{5tI?t5t(_>1SPgE)UOS zE;`a@IZ;t##4W(D}M7x#D@tis+m>f3=fF%9t-dF?q8Ir{FDa@yd^dCUb@rMWDx>-SLYF(H#pPtR z4V{9vl+xAhGj=OH-HR!jmPiqIWj=DU&bOa-sS4!>NBH5rP6wjRILWb-%md@pj-p0I? z17>(Ym06Tjy%{A>T|m@7+O>4A{rH&IC}ak0XN`0m;@+4;JQaG@dErTN$yF1>CFiNxV!vObEjB!s&;qTM8%I% zA(2XT4sG$I&?1vphfGQJVwb3)@N2~>z#rUlFN-M{Ha1Nb%r{+;_T;^9g4_!d4Hn`Z z^EdY{ZVJH4J8mAokM(D%->6FGf=3<`W!I3yH*5O_0_Oaci%rY3r|~{Pn3FcE=GNh_ ze%3|LwW_twJ<{S)ObQH!Z17el(^%Pyj%x==>Lx86{Tm&g%t6QccXlCRVvP@nDy~0b zya8Z1I!0me8k73*W*n{XYy%mjODj42$plr*t(dv{w=13U+lbPVvC2|kbm}Qz$tE*w)Sru z6~AgtsE%}p_QV42d=b#=@?z!H$D%lYrSU#?vO@?mGHMHGw`vzTy$~80b<|)d= z1yj+)mBfI<1#t}V1{GuSj$R&VCk3W%P(P{wEMqKvMQbZd!^;KOI9M;UY52em+ASPY zY%=r;xg5ay)xDeSq^d0(61G;1!L z<#x-`49?Qe!@_5gRO7iv+HPYqkV7Y=m~$PNcEd0$c@e3dk*of&4yEQan|y`UTHuDN zmEKY>{eDBR_}Tp{eKyoPd<&VfW(D^ni&|b)Or3aGGMwGU=rwi@1Zo!5sZB?HipR`e z9+9P*U5%fp^5qU2`G%`%8R{}RHhq69(xLI9`peVKTOVo+5Q$T&$k3Ia;#V@AFi$lV zPReRx{Yy?mS~k1muNz=ynZ*zSFqNBtr(4wcJP&N=ti}bty*2K-Z)+bPXIg5 z_qf#C9ug^z=a#XKU&cA_iimotRk|AV?dJ}s)SCor#HVi2?>C;FSSasj1&yYrXz4%2 zDIS8DPef^wh*N7757K2+te^^ca&0PIGttg9P%vX}0U(8<0ROXb^jqD*wdX2Dh0ii<)MLd~t^J+XBCq? z(lqS_QT@26rY-jUe;?A+h+y}tV zuRmtyydo&hdH1^~G&i|Td0tjJ0BbVA54KYUNB5O-5l4_6o4<6BP4EK$Wu2%{J-|=? zsc)~{&$>FG{znnZgO}i|1Bj1zzOwEoDH-q$=6LR$c zKkJ@+E_1Jq8hsBS83jq3h)YO}<0mtEOoZK;^Fl zW~Bpo(awFCwGe;{4udot*Xvb~U%TR03oxi3NE@^um1R(?7Tfh`tgPcX$^6OTFz ze>FRC9C;g*aupP?-f{Q~_=+{CJnSGN{rd9%*VUvk8IWiGyPD+Qk^lKKBmTZE7z$-j z0jE~hooBrxZ}*RrR|EgkOdE`h-<^S$tH$R7fcUNa+`23wQ3d>0@rlK=pS3EdfbI5; zZUZs|Jas_4Hb5aDj4Vk9d<=o9fX}OH4#-i26>$XAW>+_I1L!7T4G@3;92j7${>KV4 z6+0oZ&|GH0h}{1)<~Mw*!kMk^<0HkOx+~FpbwAIpy~2c0z&@D36a8) z-<7ihWU@2rZf@^pl*wVwjmeVt0_#&vSp8P-tgu3W8+@|Txr$vC0=idrzI4xJE3=3g zn2cHe&7h#9I&k^QtU0MO{ii118c~QB``3q;W`AP6dw&yl8OV1y0GgpBBSc7edHUS1 zT_Cs1c~Ar>i}6)A6C#Jk{lv`V|Go+agTQ+B@h1%MIsl)tcY()DY(m;5)NyRrF_IvK5pYEmh z%8t9IfP@IjA>((^x-$T^S4UG$FNXVdkU8-gjYnoj>9_G44w;dClUw&5*El-;_tZ#} z%Mnzey^O?jH8)7z&fq~7^uZLG*4YzVXF+}?viZr~2BHfb9d{(A)x20<$_lZR*Hg$6 z;@2F|`gH}?&8j!=m)T|@h;5qqrbvuIJO+|2xHAhg?-tR9!B|$~-5-COgdf_#DOh*p}*e_!!&!eVRXgPiw zm_jp)1L5Zb!k_%hMNIK(%2p7tiO_gZPXkiauD3=Y8G5Sd9Qn@fk56Lo-~5;_`P9uJ zh5lKr{|qon*)df-E(e*^r{};h5Q8sD0@w%qHWJ|!tCqvj%asGtJu$rXBK)_2iiQ-t zb}k2W=l)CJ94O!=bG(IY^Ml|e7b}*vAifuWo&d&yIw(u>Z6!!ie{%h}%IY%)zZ?QR zIy|pFlm=2+Ksy5#e>15Khtjnm{T2z7j*G`})_MgbLD~y4uwzZ+vPmjr$mgCXL(l%a z+Aj-&ZcUw^aZMYZ|6sWmNp{$4Ka8B!e`TgLfO!R43S=n@Mp)!wJO57nplbN@>ct?<6Bb|3IYO(#@4(X93YWkMcx1?YYwCNJ|6^E0`$5-5s8v}90C67f*FfxhnT+b zzp&(RpXNL`oPq|pnGH@mRn*NLww%#Mbob@kah?9Q<#RTON9tQAH?$pn&|HbZ3M6iQ6}4bF46k#o`KMOP-Q zT~nlSmsd=y)l!tnxWr~1O$y8oAO|*NM!gMg-O{JTp_M&T2{l?u<*P5kI+T^+P?xJe zrI~e#(|EH+=pC*m;a4E#WF;!QO%bk#$UC)J)$5jp$&cXS>HXYgXz#4DP(V+6SiEDSlm0fYit+wB5~40ZB;{Nk7dina*gl zyqy9A(~(t|_)bw{oxF;>O2nmRMp9qR$Kw$w(}sYa3XEvn zg`|0Y@klovH63RkbY`nZYXNiL)xbGjzr-Jg;JMXW;n`UfKUh3Y#%K`QwXpk@9A3V8 zTbHV=4b8Z-r%EDv#tFN{21<$8zRu|67IfQar4#lsSh|?xl6*_Fnijtxs(nCfAtT_e z`F4$|ORs%l?NG=&$Gv%=Boh6{89JwyI_W%iy3ppv~-HUx1;I9I>sM(S0FT zOA1qWzUmR_^5{v_)*EN6V9$0RbMkU!jCi7Yo~O$a({sYOFnTZ{byZ3#FYua5tfbM| zds^kA`PrlwgbO&X!FFHrkOBCChHDEL+v!4sYK=@fYX0dQMZU{Exc?gB>OB9^KRi|? z#42+yB3q;he2JaS90XZS6frvd<#2)F{#2CmvLxTK9#U*h&m}~vNIhk<;|RQxVCMrQ zPsnEXgCK&>?d^Tn1VlwQ2+eIl+6p~fKJKH$XJ<#sRj+*!+3LCGbi|aHYBs7-Ib6UI zoETqf=9*%*&}LtBcW~2Cs@`H3`ZtdA1)+-j%$;I!%rU zA~l>k8>jQDob4D+UE1ZcJ_L zJ-x?lVAOO8XB6^Jb!mrEDgE-LNUMjuWSM~Jj^$Z*Atz^3d3)3v_kfV;Se0i=<5hP9 zf1)&^*Fchk*_6lY@#(AL*BM|u|qk`^acPigXW8Sah*ER{6J~3pdL4XfkzEE_d~jvH(N3 zF!|V#b)S1gpu-kO>5xw7C5v}P`Q|e&ro1jacl1S&_B|+5NAw)5!=}U_q$gv+zO1kN zqh-N(UqK#}D&zP1O}}yCs%r>Q(MXh_r_qSHR2h|QqkbWZ87nAmJXmlyYAE3G{{P@Z;k{5vMad&yPm;N>)}BqUw=s2Xu?k_OAc zJ!8g0K{+B?aSb_G>5<{0w?^@C_C@mI56*E&I=H1Vs*r6BD2LnVwAukl_mFPb#@j`c zv2&mZ>J$fWZFR2_Aqo_c6`2NqB!HB>8DA;nm_98u_n~|R1+RU5;>aZV^TLhGSb2(N zX4GO5r>!~j8XrB9rmn>keNlCa%b}U6IUreGN~2|rsGAj^ZMbMOwf6*4pa69BYRmP4 zh4|+Z!rxc;8>D8h@7O;^h&%l@k&{g59aUadc!?E9O5^j+QFGsBr)1qQv&1-)FI6MB zR#MH{m$jDb0CP+2YB{#598xrzO{%|6N)CBa5UAe!mZ>r-1*Y!9_mSabR}0OI zw)q*~^99jX+|p!9#i*pdt96g}9_BtttMhV@?u>&YN*QanXMYhH5I>Pm>~ZEEi4|g& zSER_8Wi71j)eXDo6kgO$Xt#usa~*0?^WOq;E*n=Dk|Lc}Qi4=97#FYXVKYp2cKF*` z--k+85j8y)zwo0Malt~p%wahGdGq<09fz&frjmV+wt(K@LY2X5ZxA#|bt`+#1!qjz z+_tB347HHB(9z>ZUc)B72&jxS!>;%_;qi7#=Kn&xDT>ZH>yEM`dL7BtIkPo2XO=Kx zF%&yLz)u|l2)0w=_j5RE=vnZQUoynBw|a%mmiF!WdRqk9lN$b`g4)*xh6A)~D+qun zW{-T#%Le&O`?A)#zT05F=FJ<=nn@RwM2x_Cyinv^RP@kC(S|_(8J{=HhB{c9v;gvU zcAjt36$^u^p*?+9{skn;Iiw9FYc-rwDvca{aUUDqoQ<$qXQ*RbE|-P7@Sy+kZbXrU zD8W>-*6C5LW$GJsar+doZ^Bo2a*=+e2QeXv9w1%$4*rllI8ucsJc~sa`54qt`{aD=!Q_URb;Jj#vfB9 z{+ixa-gaXCO9l8Zv|{9pb(3_)M7d~!6}>or#j41ZAM|<6WU~VYs{>}7Hx$dH=-%wa zNs@8K+i(VcF2=UwJ;8}`M~uJ0-z->z2vQo;U?Ebv#1o85+4*wD85)H7Op>8nVB|=_ z{d!E+nnq<{txD3}DNTZ&{0)!+x~~dq6olkt&yVwQNa4ad33_5Zz%=Ac9m^W^NUm~^ zL%fiixx>(%PyaZ@g8NbV`^9fuW#eQ;60_Ia&pFM{>?y}qiBtv0cZwTg2%&Nd?RP@B z2{di2N?#^UwDN9g9j2VAfx0XP&knX4b9>=>uduiyNWZVH=cBf>wtjx`t4EJdXBVkk z0@nc~*v@83C1WCsnoGe~OMBn#^}vXd#M=MM zYFWxCw%56%M2bapD4VorOL#eJP(2UOiOz6xraC@kF(J6NA`c5Y8Z{WIcZ+5DbOi_)Hl68xTITR@X$|XVP zpABIF-hK$-gD5;((-H0W+r^x)lc2;5L_|;&VYs`egdo7ZXG*pRd|aC5%gjCH-*j(k z#R-kSkDMwk93YIA?N?TrhQM5=ruS$gSW(V{q7f4rb4>r;C9HQ*(<icL}g6s9Tgq zJO0dh5~vwG`c@U302jQ5YDqL=c6`YwmTNQ}jiw4kW_S6k!fe)ZQQM7>7 zNjYpf!Q-UBg67nb3N6=;9z=!ZH_d-x!L$%?0oE0Q#-QTcWFEWAPgsa>x%xZ=kgEff zu6|BN3}D~@rg9`z9CQ^l+Nn4yfH-p2M--fqKiERd{#EynQaHTk#36Aw2p~aawm{!$ z)nFE);3$!N(rkakPU_7BHpVdNt&jH-%cEj&AEQqd3o7ZRjpq&PP&;|g4PXWhs8!a3OKM&v+ zHJ}(ZsqGps>K0DW~e z%bZL(tBnpsFRc3i1xjlh0-$aiHr`S@|1vbI{>S1+%@`ycF+#3!E@~40HrsWY%4iKtUyYOGt>B`iznx3v7?sl<%SdVUJ z^=vW&zTSembO;#;8|I9O@9gtQ{E)82*}%-;vPUWqLJK;k@;V#HX>r+y?)*c3lvQ8* z{43~>`&=lc5+bh%6vT|4m)P}2)31NJo&b#qpz_t-M@Gcixt|Pr`gk%M@$a`+c~25D z0+;v^5-N-}<2HPsa$+E<)Zs*1p51wOfFM8x@Q#-Mh!OV6Nadb#U4MV5->rRtFB%es z?ou|Y{c-lnrN?pm*?Bj*PBzc)ZLhoZ0y$CQ3lafa?mBLkQ0Dh#a(h<7;tzK^c4}kpnczkOXxIgP4bBAxtorMU>s`+i zB$Q}#+r3KGDS>;~$i((wu9Ca)qj@vX3SAA9*#|%02OWg}2z~#T%77xjb+z4 zGt~SZB-W0yWZ?7T*l|!?4_dZNtQ8+5gOvi$9uOJ^`$7Zo`(^iGaZItY?AcmhJB;Mo z3JZ&BPcdZS=I_2-&I@SPW3SjW)$&Ux+zt+je&2Fnf zFd78@cVM8LQI&f`;VT)ID4utPG#b><)%r3V)HdtXU+x3E39zdRU{tpri+)$T9vvjZ zpoEKn&GK%1M~}adoVDPC0~K~J8(Gnf0dF^YLU-zk`X^RK#=fNcM>dSd`hOeQ{5NR$ zfA0V>#y|D{kI>=i_RLAd*s=&)w;8CNZ@=2y0%bU!9^$_qnq>Wt^JpIS^8X!;J=)QT z$u!*D0YFy_tFU7is-*Tr4C zMCLcn4=h!V7%4D-4L;T!%#OzvnxuIChKm!@R%olbs7+Thz+}^`5Jm$b`ET7HrhJUr z^JK4XHo+NU`8$F8$SAJZcWSq*4fyD9kvADwqUwT`7vq7qwLdg_;hJELl(x+sQ@L+K zlXep5L?#D;JB>>EXkv5MVI5wm-R7$}8y^YG?GCRFzaCu*vl`C0VPt6B#FXtpE(u-# zbk0`)YG_1lzxWvr_QO$*SvPM>!?GSwDsFfEWfR*Fktg}!56LoZece!jl3OG6elxn67mMkU-Z~?=eF*?il(qXCGB_r*{8w|2}pB=o2TML=i+6$ zP86-0O4bYS`?>x_PBUr#m${ph1y_$=`J>X^Z=7FU)04FQ_Qy8-JO-=G3nk{KB>c+k zi(Klob%m|?O{*N~JpvW>ZJzmsHOX?tKaJB-%C;R5uVj%MyBm*sQf<+mu}LTXtje=n zdDZ8V&U3=lb#8p+z{Sc8YKX!}Am&M3#9%-J_hS^k(*6Cm&6{_TWo##;yCQNndA z?f~9jsc~qz#>oNFh>e2ZrNnQ`>dTMXj!WPcJFJL9cbJ0XrhyJz42;*>_YBU&Pu7cDg9~ zcphCmdgY!KOdMWMF03s5S6;zH1))Ouz7ENs+L@mtM6Qr7zG;dd{wN_{UTkgz8*x>i zD?jnU&fQ8{*lB3FZFWg#6XkQs^P5QpPu=l1U#kkxjthTQyDa2amVP0sV_wbl1Z$mm z|H17>K614ErJGka!O`{c6GEvgmQj@99Z1%lx5Smp7TZyp!Pbmo$6k+>B_1<;aHA7`Q}&%*Kc+5f@g_Yc`dM96h}*ul_l5n%F_ELs;tf~6 zajQG3xE`wr5u{wgC@m*u&!4%jVYH-cQ%dzH2;*<EKj*4!Ut??pcQe99Ur|+;Z&*iwzgg$iXsyZ8P*gQ!W@u)h?wKCY?Qxo4-|Fzoaz*=TnU!Tmbl#muop8GRLP} zdn&V6LQQsPabmd*Wub`fdpx|YO*t&F9;5P-$n@Y=G^g1tDkRRf`ZJ#+ zWtklA|4(b*8Q0YEwHrl5Y^b21G!X$kl!yiCoC5+PB?#w84@yKp2)zf01wjl&iWDJ1 z1VIQ8LvH~r^xh$Xgx-4yA%ujx@%-<7-+SM$_e1jAb@r?^&wAFZwfD@}TE+k2H#vZk z-)q}X7$Hx%-{X1UO=?8WS|*o@4PX7GGTwLE6aXT(&MSe>P(^!=GLBmLrK|?mo6Ds# zA$smO{c;E1dmifO=?Jedg|Eo8Tj$REoTzG%OFdpKAQ_#LHY9R;0rar80^yn2^f~^H znn)}?kdpU=5R+=97cU5)8B}aR-PbPSkECviyIdhE&1)`k$pthM4QIx z_Zt?3fOHW&3cG4X%GfET=`ZLK6F09Ug=3JhPpF?qTuof#+QK{scP3Ql0SeVt+p!^2X8@4w6~odFGnr~FuI}% zW1*hF5+9e7TMGG@eRG?In?)IXh~tck+QG6M`vH$`s_;cz^IFSn%QY%`C#k1&w2!OO z^HkPK9_f3~pKfDu)@x-gnZG|&6I>ywE4u$)gq6C3XSI*ty*Pg7R%)ZLIs(cj3hIi=@Rv9 zo>Q8>*|5ZNGVxC9?D0UTR#Bj!Ea3?^f#FQqI>-28D_vbPsATm#lZr2NIsH7?WKPMq zpnHXA=r~{BLmJOkaA_6UN*%Khv=&S%QB_9baj+s{a^e0^xFJ5|m##}>8xcI5X0`W# z?l2T0omr+P81F;MNHeX|bA+8e(`j0B$6{Kr)ZhLJj43p>ICxsg6?@qgBZIM&XH;^{LE>nX+0K7V`O z_Wh;}+JEVz*Ny2kAoeC1N&qYvT~j8q=@;uxO3xg%GG~0{3C8cqmU~_K{sYYi^L^7`B+A|Hj3c`?G3A? zv=-JjyyF@c^X`yTv{7rRo3l}?^U*ZLOB_Z*QS>#I1L#lg2j38IH61YY!&W5%9(?5| zaF@WNTl{d60*p=!Oxx;EdH5>so}BzLP?CWAa20cm*Z}re%<`Id53u!?>(Pp|ukM;I% zy2^$pcBFFkh=Z1)R_V`mS1r(`a8g>N-r`%4t)P+0-gN68IA5{9Rcc&i&q_q})~n-} zFX9rd*r%{5saFv{pU-{{wz@?ugX=KMX8Y})lKMR(4E5bFC}r`)KJ1zS#8I57rRj|U z)o<}i(ZbuXB1XDLoyNjvt%4efe~VO~Y!Rd4;AXQ8wS!(ynIG_PMW#~*Bdps5Xp&;S zI|V+orP6SO!WJr?Sor??R0)-9sO9Vd1#9)4!=!oYQ^S!Sw@(5jQ%Z*Hx=e6Ko5s3k2+e|FDpdP`(tNH^&)m6u08-1X3EAf zHySg2u2NIJaA3_Y)|K(9KxJ_|(K{d8@deU)m@Nmm`QQVUrbilAlAt*6(IK|OkaFc* zl=yV<6=#`8i^|Fh`uS%l?|qbKRV1$c1u*4R5xvw5AEPD>6{MwP*P!=F-Wngd+jlML z%8#0Fwdj=?E|u1!mdb1+rl1B#CjlO4Y`pC(2r}_7+2^$h8zc>z-;3r|58{^!+HuX! zCmw}4mqjfos0VC3nx(Xj+!H~&>B&7=X_y6g;9KR(=F8ieQ(@cJSIUc8N|kb>)s&Np z*~0H8h;I2j3&Yo7XDqZO)sWD%60c02b6?=8;9wY#&!rtg1oz^{xV_ zol*`>4gb;{V>cwOrVzm$*d^vuG?^V-*ktGB4leYsM7qe}54eD3y7r|6Lr}c5nOUQx zr#<#6Av&MOid2Fl2;G{dV$={D%BcKgH+t=7KZ0apLc-&AezxQnQ#ijeBD}_4*9G$uMtR_vl~Rr5mDZyh7N7yM`3R&IGMJd$Oz7x^V4!(tZMiJG&Y%P zgV-WUOr1+ntLrXW98#`buqILs3$&_5{PECzHyW0D68%%pmJDsgsJsO)GN_%BPetC1 zU@e#fOUCcwLU!6Mkf@Lut!|&k8r%7Zb6VN$KVF;-XDwRL@(`pI*{rEz8_>-xgt z;^OWvgopX&KKVx>Yo&tKmNVfXSUy34hKOPdF(8nCk%tO}N|tgF7T8~@roHRGmdSFQe;xG_l^ zXuw*Zy;`jPR14o?lk<=u>Mq+@uqxfS8e_eiISNJJMQiZ3!AyNm;$L^yD0e*XTm0@k z#AcxBquBHro(y$rf7>PGfelTR9=d#I--7*VjpUi(ES*%AX-Z)m7H(K=jpp0diNs4m zqIX|>u&L|g-%NVnG5w)j%Hs-lY|8@=jdC+~P8EOMSx6*< zcD*Fzd^N^#S*R&!FdqDO6IPIVifN5JtkR{NbO$M~)@WyfQI))^53K`dQ&fH9%MBhG zv=v9cAr7sWTMXiC(|;}4**!`Wo{iPm$}P_qx-l!R(8W!&GU&WebW3GBf0!3FVNv&$ooK2rwcGY0c}2Bz_QW+@lEkJxax^g~L^(3ujw+sEs#c@{=7~QW3MXOk+Rg zt0WNULY>~dzy4+n6XQyu_5~hd>0QO;rxx(ZEbsXMd1iafkmjS4CtGrSl4+wT#-Blu zSP2AqBfwsZt_kbN<0pxR?Osz5RT0{pj<%@OBCEfc+?F8k%|viy)M{-@yG4i1RxK1g z{L_Ie%&3m<1Eosvn$lLhVuyZVJ$I?CF5_a~>zi8_boavjtEyhO?av7g`+edqM>1}M z>fZvM<2!~!FQovX{sY*l8ceqHBuI_vuC^cWNqN&g-I9?)r9#Lqg7C z%_7yB)IBWnfyAXxBAME*51H57lN*$JSDI^_Vz=9DHG6-hEf9v5mh@{U^4PchV-UH% zX#ZiM1!@>iYnlH6>kwd$uk|nE_aV@MeIz@-XT>BKHN#}tDpA+g?E7|)>;5Ad* zRRwpnBn3NGtC~{_rEvT_WiXT&W3Jd=G;mzf*wjYE3wnVOR zyRRdTZCwwrpk=I8_efDp*s23>SmeVD%*_SUaxtmtsudtGOt9Xqev?mid`*8^PTA+Y zEK$u?zv^V#_=$84tG*>{xQhtTTx+ya)6>8RZ7M79WcUfFp##i?-^@N}{Ms{A$muYg z5MIU|6~tpOKc*s^69NQ*XIxCDdsiHlTtv5pfApZtB&e6W8btF~7L{nTMvD!zD$h&< zmTz7S@5jUE@x-;Gcvjf5F{PX;1<#obbNzD!(oUB3t!>zTu}WcHp_dq3sb;TU$j$Vb z8i|Z{u!x9tfIW&HRv`w^#EM4P#fuksbzOwPM;Fyg-NK&s!bceGm%^bOc1xipZoyJ( zFJJTbaS94*vRb{yl5FN&Y#@m>>Z#|YSJZH|?KqkPYt*W(cu;99u@hX=k>%iQiNh@W zvQ92QSFP%JfOj0o31)`I=vMFV_(xXKFREd$J7?ccnX}t)&0^?JVwa};jYPMF+7+@5 z@jU_viA+4S-ZH8}Fkj;HStc?$#dCat`4)jm3aX^y!1X~9x3M!S?X7rJ3!~Umx7z5} zCaJsB@A7bb#o5opgpUq&BFwvlcdS%sL-#_`XKXR={G#TZZb1u`2g$e*X5-Eo-4AvU z?SWXbXCb1W*#iYTtBZ!$7~91Mzn$_ViY}wxL^UYjeC_t|k^piEIWcM)KD7oBjU5|* zsB`%JslzIZJggTw?akf+hWfDG6O^_*o{;OS2eB~idZWzg93zs7>oJ=o{y$M@s z?k$A2`}TtkWv^yLP0lx9yK5Xz105ruB`Yax)hQ?2!^ubfkPe5M=R;}ce=J@Yr%F@w zTkk7D95OH944yLfTIq30-GS?~4-UUFs&YXKU<^mJ^_QAMxQp4F&Nh!)QEUb9NAyQ4 zH843Q=dJZQMffLbTmZxDVL1p0GH(V*7PUA#S9`8>pdHG|K?u)zHfNU{%+tK&G|hRe zd<6*l=6}?dp=xC0EaiA)v3K_^_kC;CNbvzh`1_c0AeN(kR?1zYT?C?&yOsGJ^Zwbq zPg1`v>}HrRGLi@4qg04>YQhT)YMdV0ksB)1k|%!uB%dH%LC%sZFdo)Jz1a6MsuFz0Z8Q^;2K97VTSAa`ry3rNCFPiQ-QD^)rIvKhWb)yl zvIFdmuo=0dq8cZh;4i&`P)cq7L@nqJYi#pcwtPa%v7KvqI8)y`>rpR;c>hxk z8)JMj$pODJ-5?!o>n1`_u7co1d(t~_YQpBhZ&kO~8ygATM9Bd2NL3|=Yqrrd*YTs; zO-E8q?Hdd?)T9wE8jq(Q$R8y5Fr7tPLT#0ys!9=iB%qi4MO+`Ud7^8scsm1cJrH-E z3PWTL6YTII7kV>(1%N5X83xT1iRhM5A|Y0XR4s)V+|gpf>gnM%OV1tUmU*%R5N8TB z2xPdx}vdoh9Zy94^PR3EY&!uB|sx}otIz9p%<)xG(uEB!^`esIVlMZ-v$8{|H* zbG&AQ)^XSFxd^SO$bkq*39FW8e zthos;hzbu0YUng|p89T!Dk;lG(uVczN~6`#z}y(MGC~%<7vr^-%;_3|8+opQ{kz>P z;?rMLeRM)Jv<65bm>+{Um_XzHcIFAJr9DB(g6a2k79fpzOZS<*b;@IR4YnDR4|rRk@quVkxOJ1$yF_ zZPD()eSjE$g(V$EM}p7S|!I5(qy#oQ>1@kSD9y^ zXi2G;Wp0Zv%b;FLof|IITpDgWWv7%^D^lwJB{lwmXj$~O8$2eEwc&Xc?pIatK7Z4C z3EGuD7}24Y9}{YqhIUgc&qy1ZHMx@NN=Zei=v4Bz@_f+{M%Acx+L8J<1`1oMqVm&z zs?!HKA)VbHR~8<&4qHEJ9agnj8gtg4F7mkD7Ne;YQJXU);-t^SmT80Q-YXDjTBLG* zV73}q`1+-;CLh5Jfsai*H7w1+(vpj)msLn~OhZs(F;ZCSMG2V@0vE|fcAL;s?$nxF zmomj-`}$&Qw{eaYRqFQaj0a@kQ3R=M`Gts*Bn=$jP1rk8Jy$mdU^lD+@&!zr&t@Su@QaJhj)hTy%&dc^I zEaQuUJ6c{qdg@K10O^R{r;;gJ92K*TOV^wHvQLj=cc=R~BG(UNbz&r@ZOJ=6-KR(F z6Xmx2Yd9^3sN~qP-Ex$(_SoT1>hQUY5Y-V)`>h>VRbtgPy2aSjk=c!#sf;!(6D`np z7F532kHVpmSU-pYIs7a)`KrM|!mu;1yvhaep`^ z(cQ;?Hc^a2E`02h5uS}2iHON$Ry~8EqPcxJB)Up?nmiTU(k{WJcTc_Rwww@-46MxT zuvn3L2zXRZ7C15fEqE9b>o2c4ZRBG2P&tOXp~qMq4)bs4Un$*=cyD9^tIxR8=d4A? zQJIesFGq)@rH2%Z8d?KGYcY}#K?|5Hh;A*_L^TY ztqJiV1#Iv829X65Gt!JO4UK%L51_b;Kj<*${Pzy5&FvHQ8EO9vf^yyHGr2;%6Qez2 z*jxowl{RJUd*!(JCQsArnLg76cLKJbe@tH4){Z9P!g)21iG`l|_wQ}f*?pS(e>-q2 z;=7jqf7`#G{wh2%^o-|8`H($-zT=z*$v4D&kc`cluZ4w;6&_PJ96E)(eSNnVgEfyS z4Jx^hgK9TF;2!VoYzSzCs1{aCzMH-XlMxFQQol>~7thPfyWu*j*grVXsgAsZ)z z6x~4()r}(|oAK%617@}j;xVc}1oV%I5$a!ZaD997^Qh|Xe5U_ogw%Sc3h){WvHB!Z zVp=i0PQ>V#Sd${w45t$ipj5j%fIAF&)|nPDhe4c{LVKIwMs2@crr_?K(`?R3ArdWt zdL9NHe{=ZgX`#2g5Y<@u#50|kR$a0HK3*;0PonvmV=oeBDYicJi-pu+}cufZfDLKNHNl96m-AwBTkN}KZEbFe* z^XEVMAix;-GKHWy70SP&{%*9$Tfj)=Wl}j0kLo(;K#zBv2R~(JXa7f)WCB7S)_5{f zK>l!y`mUpA`hk3>)6-eY4i(!(XS{veg_82}Of2?o*X={X}vq{KR`WzY;VqZ z`u~OAUA_bG{MQv**Zm#!{tMj47EJ#~0U1CO0XHo8%yy)P8>T73-hc zkGGdchD)7?&c?m{U8duIO6%oHzG;6Pz`+50y=D#e_U1I!KKNqu^16F^Vyz*`Hx7vg zZ#~472v2@Vg8q1Mh~tJYu*h~vIwY>Q*Tgz-;fw$HORng$Yrm`7B=Mh0FNg!#?n{Dq zU4Qx2F>S1oEZT6TmjjM|po){1Zhsvj4dh#v9xr~@v3rlHc+T2?;*I~_MpMtgfKQ3` zZz+^k$N!b*z9_KDDBoN)ZgDfzFgeZ`0l)P98;cQq&RUT;ll^of`*sWfhLt6P5d9}Gc!55)R{6w zBGG-bMQX0F+<8bY-=g@aexl6xI0@hby0#>+u9MSs_IkL+r`%lbnVFffMIT?^$rK07 z_dNzx9?1>@M=l*ZMZTN=XYnws)HcXq}H zGks;OE6z}uWcvD#6X3^_zalDDtCnukz5!?U#y8m4+GrwbNu`3*MWc`b95>%$rzxO&x71M?hK8`>WW0PN&!5Qk(G7TH0HNSWcf`< z0lWun?;blKXBbR8Wap>o=`6eSPs|^rynd{hbkRoF`eq$IG^Q1jl{=h33TUnu4 zOu8=Vq%hws!?neZb|wxoT;(IJ6Lsms2>4%yu=(HCA3jg1(*Qi#@ifoY>;5|v;fFu~ zz3TwP)zjVmA~ErTjBTx|A3`haq+YFHaZXymY>NL>)ZK5MGY{yHAF`&|2H$~MBonP; zgp|&i+4!7yt=Z679x3KRvZ?8raHEg6xp8%DeB`-kF>5I01{k955$&Y^>_ukro%-U9;@i&$J^bF6qK{IsaLP+Owx&(}Qa zZ}s9OB{Y8Bvio&xZ>LG45uk~8PJP)=eukZ@U~Q9b&4|j|9dWIkxe&C^q%^gs+=CigjHZG#d}J9||JpOWcm8{I3^eEjNv E0hBxy!T+O9|4_ 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 Mx8#4*x5Vgw03TP=rvLx| diff --git a/Documentation/gocryptfs-logo.png b/Documentation/gocryptfs-logo.png deleted file mode 100644 index 0965dc43b087d84a2d7edb1a02b54d81c206b5b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7332 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 /dev/null -RM: time rm -Rf linux-3.0 - -(or just run benchmark.bash) - -VERSION WRITE READ UNTAR MD5 LS RM ENV CHANGE? COMMIT MSG -********************** -* CPU = Pentium G630 * -********************** -v0.4 48 1.5 5 -v0.5-rc1 56 7 19 -v0.5-rc1-1 54 4.1 9 -v0.5-rc1-2 45 1.7 3.4 -v0.6 47 1.8 4.3 -v0.7 43 1.7 4.3 -v0.7.2 26 1.8 4.3 -v0.8 23 1.8 4.3 -v0.9-rc2 94 24 1.8 4.5 -v0.9 94 24 1.8 4.5 -v0.11 104 22 1.7 4.5 -v1.1 104 20 1.5 3.4 go1.7.1 -v1.1.1-34 112 22 1.5 3.6 go1.7.3 -v1.2.1-33 112 21 12 1.6 4.4 go1.8 --serialize_reads 116 21 39 1.5 4.4 (v1.2.1-33 with -serialize_reads) -v1.3-27 113 20 11 1.4 4.2 -v1.3-53-gf44902a 119 19 12 1.6 4.1 -v1.3-64-g80516ed 123 19 11 1.3 4.2 -v1.3-67-g9837cb0 125 19 11 1.4 4.2 go1.8.3, Linux 4.10 -v1.3-69-ge52594d 145 19.0 11.6 1.4 4.1 -v1.4-1-g3c6fe98 154 17.2 11.7 1.4 4.1 -v1.4-5-g0cc6f53 182 144 16.7 11.1 1.3 3.3 -v1.4-8-g80676c6 178 148 16.1 11.0 1.3 4.0 -v1.4-14-g9f4bd76 182 286 15.4 7.5 1.3 4.1 -v1.4-45-gd5671b7 183 282 14.9 7.3 1.1 2.9 -v1.4-45-gd5671b7 252 285 15.5 7.2 1.1 2.9 go1.8.3, Linux 4.11 -v1.4.1 253 285 16.0 7.4 1.3 3.0 go1.9, Linux 4.12.5 -v1.4.1-6-g276567e 258 289 16.1 7.5 1.3 3.0 -v1.5 228 292 17.6 9.3 1.5 3.5 go1.10.2, Linux 4.16.8 -v1.6 250 289 17.7 8.0 1.3 3.2 go1.10.3, Linux 4.17.12 -v1.7-beta1 229 278 17.1 8.8 1.7 3.2 go1.11.4, Linux 4.19.12 -v1.7-rc1 226 289 17.6 8.9 1.7 2.9 -******************************************** -* CPU = Core i5-3470, governor = powersave * -******************************************** -v1.7 232 698 12.2 9.4 1.7 4.3 go1.12.9, Linux 5.2.17 -v1.7.1 450 697 11.5 9.5 1.5 3.6 -********************************************** -* CPU = Core i5-3470, governor = performance * -********************************************** -v1.7.1 556 1000 9.0 4.2 0.9 2.0 go1.13.6, Linux 5.4.17 -v1.7.1 577 1100 8.3 4.2 0.9 2.0 go1.14.2, Linux 5.6.7 -v1.7.1-60-gb23f77c 472 1100 12.7 4.2 0.8 2.0 -v1.8.0 410 1000 17.5 6.7 5.4 7.8 go1.15.3, Linux 5.8.13 -v2.0-beta1 387 1100 36.2 14.4 12.8 19.3 -v2.0-beta1-5-gc943ed3 417 1000 30.4 12.7 9.9 16.4 -v2.0-beta1-6 529 1100 17.5 9.0 3.6 9.0 -v2.0-beta1-9-g029e44d 477 1000 15.5 8.7 2.8 7.6 -v2.0-beta2-16-geaca820 542 997 15.9 8.8 6.2 7.8 go1.16.2, Linux 5.11.10 fusefrontend: do not encrypt ACLs -v2.0-beta2-36-g6aae2aa 505 1000 16.1 8.2 6.3 7.7 -v2.0-beta2-37-g24d5d39 558 1000 12.3 6.4 4.4 2.8 fs: add initial dirfd caching -v2.0-beta2-42-g4a07d65 549 1000 8.2 4.7 1.8 2.4 fusefrontend: make dirCache work for "node itself" -v2.0 420 1000 8.5 4.5 1.8 2.3 go1.16.5, Linux 5.11.21 - -Results for EncFS for comparison (benchmark.bash -encfs): - -VERSION WRITE READ UNTAR MD5 LS RM -********************** -* CPU = Pentium G630 * -********************** -encfs v1.9.1 95 20 8 2.8 3.8 -********************************************** -* CPU = Core i5-3470, governor = performance * -********************************************** -encfs v1.9.5 138 459 12.2 5.1 2.2 3.0 diff --git a/Makefile b/Makefile deleted file mode 100644 index bdc05cf..0000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -.phony: build -build: - ./build.bash - ./Documentation/MANPAGE-render.bash - -.phony: test -test: - ./test.bash - -.phony: root_test -root_test: - ./build.bash - cd tests/root_test && go test -c && sudo ./root_test.test -test.v - -.phony: format -format: - go fmt ./... - -.phony: install -install: - install -Dm755 -t "$(DESTDIR)/usr/bin/" gocryptfs - install -Dm755 -t "$(DESTDIR)/usr/bin/" gocryptfs-xray/gocryptfs-xray - install -Dm644 -t "$(DESTDIR)/usr/share/man/man1/" Documentation/gocryptfs.1 - install -Dm644 -t "$(DESTDIR)/usr/share/man/man1/" Documentation/gocryptfs-xray.1 - install -Dm644 -t "$(DESTDIR)/usr/share/licenses/gocryptfs" LICENSE diff --git a/README.md b/README.md index cac5f97..ca05427 100644 --- a/README.md +++ b/README.md @@ -1,700 +1,6 @@ -[![gocryptfs](Documentation/gocryptfs-logo.png)](https://nuetzlich.net/gocryptfs/) -[![Build Status](https://travis-ci.org/rfjakob/gocryptfs.svg?branch=master)](https://travis-ci.org/rfjakob/gocryptfs) -[![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) -[![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) +libgocryptfs is a re-desing of the original [gocryptfs](https://github.com/rfjakob/gocryptfs) code to work as a library. Volumes are not mounted with [FUSE](https://www.kernel.org/doc/html/latest/filesystems/fuse.html) but rather opened in memory and accessed through API calls. What the purpose ? +- Allow the use of gocryptfs in embedded devices where FUSE is not available (such as Android) +- Reduce attack surface by restricting volumes access to only one process rather than one user -An encrypted overlay filesystem written in Go. -Official website: https://nuetzlich.net/gocryptfs ([markdown source](https://github.com/rfjakob/gocryptfs-website/blob/master/docs/index.md)). - -![Folders side-by-side animation](Documentation/folders-side-by-side.gif) - -gocryptfs is built on top the excellent -[go-fuse](https://github.com/hanwen/go-fuse) FUSE library. -This project was inspired by EncFS and strives to fix its security -issues while providing good performance -([benchmarks](https://nuetzlich.net/gocryptfs/comparison/#performance)). -For details on the security of gocryptfs see the -[Security](https://nuetzlich.net/gocryptfs/security/) design document. - -All tags from v0.4 onward are signed by the *gocryptfs signing key*. -Please check [Signed Releases](https://nuetzlich.net/gocryptfs/releases/) -for details. - -Current Status --------------- - -gocryptfs has reached version 1.0 on July 17, 2016. It has gone through -hours and hours of stress (fsstress, extractloop.bash) and correctness -testing (xfstests). It is now considered ready for general consumption. - -The old principle still applies: Important data should have a backup. -Also, keep a copy of your master key (printed on mount) in a safe place. -This allows you to access the data even if the gocryptfs.conf config -file is damaged or you lose the password. - -The security of gocryptfs has been audited in March 3, 2017. The audit -is available [here (defuse.ca)](https://defuse.ca/audits/gocryptfs.htm). - -Platforms ---------- - -Linux is gocryptfs' native platform. - -Beta-quality Mac OS X support is available, which means most things work -fine but you may hit an occasional problem. Check out -[ticket #15](https://github.com/rfjakob/gocryptfs/issues/15) for the history -of Mac OS X support but please create a new ticket if you hit a problem. - -For Windows, an independent C++ reimplementation can be found here: -[cppcryptfs](https://github.com/bailey27/cppcryptfs) - -A standalone Python tool that can decrypt files & file names is here: -[gocryptfs-inspect](https://github.com/slackner/gocryptfs-inspect) - -Installation ------------- -Precompiled binaries that work on all x86_64 Linux systems are available for download from the github releases page. - -On Debian, gocryptfs is available as a deb package: -```bash -apt install gocryptfs -``` - -On Mac OS X, gocryptfs is available as a Homebrew formula: -```bash -brew install gocryptfs -``` - -On Fedora, gocryptfs is available as an rpm package: -```bash -sudo dnf install gocryptfs -``` - -If you use the standalone binary, make sure you install the `fuse` package -from your distributions package repository before running `gocryptfs`. - -See the [Quickstart](https://nuetzlich.net/gocryptfs/quickstart/) page for more info. - -Testing -------- - -gocryptfs comes with is own test suite that is constantly expanded as features are -added. Run it using `./test.bash`. It takes about 1 minute and requires FUSE -as it mounts several test filesystems. - -The `stress_tests` directory contains stress tests that run indefinitely. - -In addition, I have ported `xfstests` to FUSE, the result is the -[fuse-xfstests](https://github.com/rfjakob/fuse-xfstests) project. gocryptfs -passes the "generic" tests with one exception, results: [XFSTESTS.md](Documentation/XFSTESTS.md) - -A lot of work has gone into this. The testing has found bugs in gocryptfs -as well as in the go-fuse library. - -Compile -------- - -Install Go 1.11 or higher: - -* Debian/Ubuntu: `apt install golang` -* Fedora: `dnf install golang` - -Then, download the source code and compile: - - $ git clone https://github.com/rfjakob/gocryptfs.git - $ cd gocryptfs - $ ./build-without-openssl.bash - -This will compile a static binary that uses the Go stdlib crypto backend. - -If you want to use the OpenSSL crypto backend (faster on -old CPUs lacking AES-NI), you have to install a few dependencies: - -* Debian/Ubuntu: `apt install libssl-dev gcc pkg-config` -* Fedora: `dnf install openssl-devel gcc pkg-config` - -Then, run: - - $ ./build.bash - -Use ---- - - $ mkdir cipher plain - $ ./gocryptfs -init cipher - $ ./gocryptfs cipher plain - -See the [Quickstart](https://nuetzlich.net/gocryptfs/quickstart/) page for more info. - -The [MANPAGE.md](Documentation/MANPAGE.md) describes all available command-line options. - -Use: Reverse Mode ------------------ - - $ mkdir cipher plain - $ ./gocryptfs -reverse -init plain - $ ./gocryptfs -reverse plain cipher - -Graphical Interface -------------------- - -The [SiriKali](https://mhogomchungu.github.io/sirikali/) project supports -gocryptfs and runs on Linux and OSX. - -[cppcryptfs](https://github.com/bailey27/cppcryptfs) on Windows provides -its own GUI. - -Stable CLI ABI --------------- - -If you want to call gocryptfs from your app or script, see -[CLI_ABI.md](Documentation/CLI_ABI.md) for the official stable -ABI. This ABI is regression-tested by the test suite. - -Storage Overhead ----------------- - -* Empty files take 0 bytes on disk -* 18 byte file header for non-empty files (2 bytes version, 16 bytes random file id) -* 32 bytes of storage overhead per 4kB block (16 byte nonce, 16 bytes auth tag) - -[file-format.md](Documentation/file-format.md) contains a more detailed description. - -Performance ------------ - -Since version 0.7.2, gocryptfs is as fast as EncFS in the default mode, -and significantly faster than EncFS' "paranoia" mode that provides -a security level comparable to gocryptfs. - -On CPUs without AES-NI, gocryptfs uses OpenSSL through a thin wrapper called `stupidgcm`. -This provides a 4x speedup compared to Go's builtin AES-GCM -implementation. See [CPU-Benchmarks](https://github.com/rfjakob/gocryptfs/wiki/CPU-Benchmarks) -for details, or run `gocryptfs -speed` to see the encryption performance of your CPU. -Example for a CPU without AES-NI: - -``` -$ ./gocryptfs -speed -gocryptfs v2.0; go-fuse v2.1.1-0.20210508151621-62c5aa1919a7; 2021-06-06 go1.16.5 linux/amd64 -AES-GCM-256-OpenSSL 536.63 MB/s -AES-GCM-256-Go 831.84 MB/s (selected in auto mode) -AES-SIV-512-Go 155.85 MB/s -XChaCha20-Poly1305-Go 700.02 MB/s (benchmark only, not selectable yet) -``` - -You can run `./benchmark.bash` to run gocryptfs' canonical set of -benchmarks that include streaming write, extracting a linux kernel -tarball, recursively listing and finally deleting it. The output will -look like this: - -``` -$ ./benchmark.bash -Testing gocryptfs at /tmp/benchmark.bash.xFD: gocryptfs v2.0; go-fuse v2.1.1-0.20210508151621-62c5aa1919a7; 2021-06-06 go1.16.5 linux/amd64 -WRITE: 262144000 bytes (262 MB, 250 MiB) copied, 0,698174 s, 375 MB/s -READ: 262144000 bytes (262 MB, 250 MiB) copied, 0,268916 s, 975 MB/s -UNTAR: 8,970 -MD5: 4,846 -LS: 1,851 -RM: 2,367 -``` - -Changelog ---------- - -v2.0.1, 2021-06-07 -* Fix symlink creation reporting the wrong size, causing git to report it as modified - ([#574](https://github.com/rfjakob/gocryptfs/issues/574)) - -v2.0, 2021-06-05 -* Fix a few [issues discovered by xfstests](https://github.com/rfjakob/fuse-xfstests/wiki/results_2021-05-19) - * Biggest change: rewrite SEEK_HOLE / SEEK_DATA logic (now emulates 4k alignment) - -v2.0-beta4, 2021-05-15 -* **Make ACLs *actually* work (pass `-acl` to enable)** ([#536](https://github.com/rfjakob/gocryptfs/issues/536)) -* Blocklist `RENAME_EXCHANGE` and `RENAME_WHITEOUT` (broken as discovered by [fuse-xfstest/gocryptfs-2019-12](https://github.com/rfjakob/fuse-xfstests/tree/gocryptfs-2019-12)) - -v2.0-beta3, 2021-04-24 -* MANPAGE: Split options into sections acc. to where they apply ([#517](https://github.com/rfjakob/gocryptfs/issues/517)) -* `-idle`: count cwd inside the mount as busy ([#533](https://github.com/rfjakob/gocryptfs/issues/533)) -* Make `gocryptfs.diriv` and `gocryptfs.xxx.name` files world-readable to make encrypted backups easier - when mounting via [/etc/fstab](Documentation/MANPAGE.md#fstab) ([#539](https://github.com/rfjakob/gocryptfs/issues/539)) -* Make it work with MacFUSE v4.x ([#524](https://github.com/rfjakob/gocryptfs/issues/524)) -* **Disable ACL encryption**, it causes a lot of problems ([#543](https://github.com/rfjakob/gocryptfs/issues/543), - [#536](https://github.com/rfjakob/gocryptfs/issues/536)) - * Old encrypted ACLs are reported by `gocryptfs -fsck` but otherwise ignored - * This fixes inheritance, but does not yet enforce them correctly -* Include `gocryptfs-xray` in binary releases ([#496](https://github.com/rfjakob/gocryptfs/issues/496)) -* go-fuse: track *most recent* parent. This improves robustness when the filesystem is modified behind - the back of gocryptfs. Helps both with `-sharedstorage` and also without. - ([commit 1](https://github.com/hanwen/go-fuse/commit/c3186132bf8b7a04b5e5bc27489d88181f92e4e0), - [commit 2](https://github.com/hanwen/go-fuse/commit/a90e1f463c3f172a7690a6449fe5955a180dfec3), - [#549](https://github.com/rfjakob/gocryptfs/issues/549)) -* Add directory fd caching for 2x - 3x speed boost in small file ops compared to v2.0-beta2 - ([performance numbers](https://github.com/rfjakob/gocryptfs/blob/5cb1e55714aa92a848c0fb5fc3fa7b91625210fe/Documentation/performance.txt#L73)) - -v2.0-beta2, 2020-11-14 -* Improve [performance](Documentation/performance.txt#L69) -* Fix [GETATTR panic](https://github.com/rfjakob/gocryptfs/issues/519#issuecomment-718790790) in reverse mode - -v2.0-beta1, 2020-10-15 -* **Switch to the improved go-fuse [v2 API](https://pkg.go.dev/github.com/hanwen/go-fuse/v2@v2.0.3/fs)** - * This is a big change, a lot of code has been reorganized or rewritten - to fit the v2 API model. - * Please test & report bugs - * No changes to the on-disk format - * File descriptor caching is not yet implemented, - causing a slowdown. Caching will be implemented for v2.0 final. -* **Add support for FIDO2 tokens (`-fido2`, [#505](https://github.com/rfjakob/gocryptfs/pull/505))** -* Add `-encrypt-paths` / `-decrypt-paths` functionality to `gocryptfs-xray` - ([#416](https://github.com/rfjakob/gocryptfs/issues/416)) -* Accept multiple `-passfile`s - ([#288](https://github.com/rfjakob/gocryptfs/issues/288)) -* Make `-masterkey=stdin` work together with `-passwd` - ([#461](https://github.com/rfjakob/gocryptfs/issues/461)) -* Fix `Unknown opcode 2016` crash on Google Cloud - ([go-fuse #276](https://github.com/hanwen/go-fuse/issues/276), - [gocryptfs commit ec74d1d](https://github.com/rfjakob/gocryptfs/commit/ec74d1d2f4217a9a337d1db9902f32ae2aecaf33)) - -v1.8.0, 2020-05-09 -* Enable ACL support ([#453](https://github.com/rfjakob/gocryptfs/issues/453)) - * **Warning 2021-02-07**: This feature is incomplete! Do not use ACLs before gocryptfs v2.0 final! - Reading and writing ACLs works, but they are not enforced or inherited ([#542](https://github.com/rfjakob/gocryptfs/issues/542)) -* Ignore `.nfsXXX` temporary files - ([#367](https://github.com/rfjakob/gocryptfs/issues/431)) -* Handle inode number collisions from multiple devices - ([#435](https://github.com/rfjakob/gocryptfs/issues/435)) -* Drop `-nonempty` for fusermount3 - ([#440](https://github.com/rfjakob/gocryptfs/pull/440)) -* Reverse mode: improve inode number mapping and max=1000000000000000000 limitation - ([#457](https://github.com/rfjakob/gocryptfs/issues/457)) -* Enable `--buildmode=pie` ([#460](https://github.com/rfjakob/gocryptfs/pull/460)) -* Migrate from dep to Go Modules - ([commit cad711993](https://github.com/rfjakob/gocryptfs/commit/cad711993d67dd920f9749a09414dbbba6ab8136)) -* go mod: update dependencies - ([commit b23f77c](https://github.com/rfjakob/gocryptfs/commit/b23f77c8ead0dbb5ed59dd50e94f13aacf7dbaf1)) -* `gocryptfs -speed`: add XChaCha20-Poly1305-Go - ([#452](https://github.com/rfjakob/gocryptfs/issues/452)) -* Respect `GOMAXPROCS` environment variable - ([commit ff210a06f](https://github.com/rfjakob/gocryptfs/commit/ff210a06fb3097eecd5668ddb3ace9c76873eb00) -* Completely remove Trezor-related code (commit 1364b44ae356da31e24e5605fe73a307e9d6fb03) - * Has been disabled since v1.7 due to issues a third-party module. - * Please use FIDO2 instead (gocryptfs v2.0) - -v1.7.1, 2019-10-06 -* Support wild cards in reverse mode via `--exclude-wildcard` - ([#367](https://github.com/rfjakob/gocryptfs/pull/367)). Thanks @ekalin! -* Create `gocryptfs.diriv` files with 0440 permissions to make it easier to - share an encrypted folder via a network drive - ([#387](https://github.com/rfjakob/gocryptfs/issues/387)). - Note: as a security precaution, the owner must still manually - `chmod gocryptfs.conf 0440` to allow mounting. -* Allow the `nofail` option in `/etc/fstab` -* `-passwd` can now change the `-scryptn` parameter for existing filesystems - ([#400](https://github.com/rfjakob/gocryptfs/issues/400)) -* Fix `-idle` unmounting the filesystem despite recent activity - ([#421](https://github.com/rfjakob/gocryptfs/issues/421)) -* **Fix a race condition related to inode number reuse - ([#363](https://github.com/rfjakob/gocryptfs/issues/363))**. - It could be triggered by concurrently creating and deleting files and can lead to data loss - in the affected file. This bug was found by the automated tests on Travis - and was very hard to trigger locally. -* tests: use /var/tmp instead of /tmp by default - ([commit 8c4429](https://github.com/rfjakob/gocryptfs/commit/8c4429408716d9890a98a48c246d616dbfea7e31)) - -v1.7, 2019-03-17 -* **Fix possible symlink race attacks in forward mode** when using allow_other + plaintextnames - * If you use *both* `-allow_other` *and* `-plaintextnames`, you should upgrade. - Malicious users could trick gocryptfs into modifying files outside of `CIPHERDIR`, - or reading files inside `CIPHERDIR` that they should not have access to. - * If you do not use `-plaintextnames` (disabled per default), these attacks do - not work as symlinks are encrypted. - * Forward mode has been reworked to use the "\*at" family of system calls everywhere - (`Openat/Unlinkat/Symlinkat/...`). - * As a result, gocryptfs may run slightly slower, as the caching logic has been - replaced and is very simple at the moment. - * The possibility for such attacks was found during an internal code review. -* Reverse mode: fix excluded, unaccessible files showing up in directory listings - ([#285](https://github.com/rfjakob/gocryptfs/issues/285), - [#286](https://github.com/rfjakob/gocryptfs/issues/286)) -* gocryptfs-xray: add `-aessiv` flag for correctly parsing AES-SIV format files - ([#299](https://github.com/rfjakob/gocryptfs/issues/299)) -* Ensure that standard fds 0,1,2 are always initialized - ([#320](https://github.com/rfjakob/gocryptfs/issues/320)). - Prevents trouble in the unlikely case that gocryptfs is called with - stdin,stdout and/or stderr closed. -* `-extpass` now can be specified multiple times to support arguments containing spaces - ([#289](https://github.com/rfjakob/gocryptfs/issues/289)) -* Drop Fstatat, Mkdirat, Syslinkat, Fchownat, Unlinkat, Renameat, Openat emulation of MacOS - and instead use native functions (thanks @slackner !) -* Use `Setreuid` to robustly set the owner with allow_other (@slackner, - ([commit](https://github.com/rfjakob/gocryptfs/commit/03b9d65cce53fb95b7d489ecd03d0853b9b923fb))) -* Pack the rendered man page into the source code archive for user convenience - ([issue 355](https://github.com/rfjakob/gocryptfs/issues/355)) -* Disable Trezor support again (commit 16fac26c57ba303bf60266d24c17f5243e5ea376) - * Trezor support has been broken since Sept 2018 due to issues - in a third-party module ([#261](https://github.com/rfjakob/gocryptfs/issues/261)) - -v1.6.1, 2018-12-12 -* Fix "Operation not supported" chmod errors on Go 1.11 - ([#271](https://github.com/rfjakob/gocryptfs/issues/271)) - -v1.6, 2018-08-18 -* **Add `-e` / `-exclude` option** for reverse mode - ([#235](https://github.com/rfjakob/gocryptfs/issues/235), - [commit](https://github.com/rfjakob/gocryptfs/commit/ec2fdc19cf9358ae7ba09c528a5807b6b0760f9b)) -* Add support for the Trezor One HSM [PR#247](https://github.com/rfjakob/gocryptfs/pull/247), thanks @xaionaro! - * Use `./build.bash -tags enable_trezor` to compile with Trezor support - * Then, use `gocryptfs -init -trezor` to create a filesystem locked with a physical Trezor device. - * Note 2021-01-31: Support was removed again in gocryptfs v1.7. Please use `-fido2` in gocryptfs v2.0. -* Only print master key once, on init - ([#76](https://github.com/rfjakob/gocryptfs/issues/76), - [commit](https://github.com/rfjakob/gocryptfs/commit/6d64dfe8f7acd8e9ca4a659d26318e442c2db85a)) -* Fall back to buffered IO even when passed `O_DIRECT` - ([commit](https://github.com/rfjakob/gocryptfs/commit/893e41149ed353f355047003b89eeff456990e76)) - -v1.5, 2018-06-12 -* **Support extended attributes (xattr)** in forward mode - ([#217](https://github.com/rfjakob/gocryptfs/issues/217)). Older gocryptfs versions - will ignore the extended attributes. -* **Add `-fsck` function** - ([#191](https://github.com/rfjakob/gocryptfs/issues/191)) -* Fix clobbered timestamps on MacOS High Sierra - ([#229](https://github.com/rfjakob/gocryptfs/issues/229)) -* Add `-masterkey=stdin` functionality - ([#218](https://github.com/rfjakob/gocryptfs/issues/218)) -* Accept `-dev`/`-nodev`, `suid`/`nosuid`, `-exec`/`-noexec`, - `-ro`/`-rw` flags to make mounting via `/etc/fstab` possible. - Thanks @mahkoh! ([#233](https://github.com/rfjakob/gocryptfs/pull/233), - [commit](https://github.com/rfjakob/gocryptfs/commit/53d6a9999dd0e4c31636d16179f284fff35a35d9), - [commit](https://github.com/rfjakob/gocryptfs/commit/10212d791a3196c2c8705a7a3cccdeb14a8efdbe)) -* Fix a `logger` path issue on SuSE - [#225](https://github.com/rfjakob/gocryptfs/issues/225) -* Stop printing the help text on a "flag provided but not defined" - error ([commit](https://github.com/rfjakob/gocryptfs/commit/5ad26495fc86527bbfe75ac6b46528d49a373676)) - -v1.4.4, 2018-03-18 -* Overwrite secrets in memory with zeros as soon as possible - ([#211](https://github.com/rfjakob/gocryptfs/issues/211)) -* Fix Getdents problems on i386 and mips64le - ([#197](https://github.com/rfjakob/gocryptfs/issues/197), - [#200](https://github.com/rfjakob/gocryptfs/issues/200)) -* Make building with gccgo work - ([#201](https://github.com/rfjakob/gocryptfs/issues/201)) -* MacOS: fix `osxfuse: vnode changed generation` / `Error code -36` issue in go-fuse - ([#213](https://github.com/rfjakob/gocryptfs/issues/213), - [commit](https://github.com/hanwen/go-fuse/commit/a9ddcb8a4b609500fc59c89ccc9ee05f00a5fefd)) -* Fix various test issues on MacOS - -v1.4.3, 2018-01-21 -* **Fix several symlink race attacks** in connection with reverse mode - and allow_other. Thanks to @slackner for reporting and helping to fix - the issues: - * Fix symlink races in reverse mode - ([issue #165](https://github.com/rfjakob/gocryptfs/issues/165)) - * Fix symlink races in connection with `-allow_other` - ([issue #177](https://github.com/rfjakob/gocryptfs/issues/177)) -* Fix problems with special names when using `-plaintextnames` - ([issue #174](https://github.com/rfjakob/gocryptfs/issues/174)) -* Add `-devrandom` command-line option - ([commit](https://github.com/rfjakob/gocryptfs/commit/f3c777d5eaa682d878c638192311e52f9c204294)) -* Add `-sharedstorage` command-line option - ([commit](https://github.com/rfjakob/gocryptfs/commit/e36a0ebf189a826aaa63909c5518c16356f5f903), - [issue #156](https://github.com/rfjakob/gocryptfs/issues/156)) -* MacOS: let OSXFuse create the mountpoint if it does not exist - ([issue #194](https://github.com/rfjakob/gocryptfs/issues/194)) - -v1.4.2, 2017-11-01 -* Add `Gopkg.toml` file for `dep` vendoring and reproducible builds - ([issue #142](https://github.com/rfjakob/gocryptfs/issues/142)) -* MacOS: deal with `.DS_Store` files inside CIPHERDIR - ([issue #140](https://github.com/rfjakob/gocryptfs/issues/140)) -* Reverse mode: fix ENOENT error affecting names exactly 176 bytes long - ([issue #143](https://github.com/rfjakob/gocryptfs/issues/143)) -* Support kernels compiled with > 128 kiB FUSE request size (Synology NAS) - ([issue #145](https://github.com/rfjakob/gocryptfs/issues/145), - [commit](https://github.com/rfjakob/gocryptfs/commit/4954c87979efaf5b8184efccc7d9a38c21e4209b)) -* Fix a startup hang when `$PATH` contains the mountpoint - ([issue #146](https://github.com/rfjakob/gocryptfs/issues/146)) - -v1.4.1, 2017-08-21 -* **Use memory pools for buffer handling** ( - [3c6fe98](https://github.com/rfjakob/gocryptfs/commit/3c6fe98), - [b2a23e9](https://github.com/rfjakob/gocryptfs/commit/b2a23e9), - [12c0101](https://github.com/rfjakob/gocryptfs/commit/12c0101)) - * On my machine, this **doubles** the streaming read speed - (see [performance.txt](https://github.com/rfjakob/gocryptfs/blob/v1.4.1/Documentation/performance.txt#L38)) -* Implement and use the getdents(2) syscall for a more efficient - OpenDir implementation - ([e50a6a5](https://github.com/rfjakob/gocryptfs/commit/e50a6a5)) -* Purge masterkey from memory as soon as possible - ([issue #137](https://github.com/rfjakob/gocryptfs/issues/137)) -* Reverse mode: fix inode number collision between .name and .diriv - files - ([d12aa57](https://github.com/rfjakob/gocryptfs/commit/d12aa57)) -* Prevent the logger from holding stdout open - ([issue #130](https://github.com/rfjakob/gocryptfs/issues/130)) -* MacOS: make testing without openssl work properly - ([ccf1a84](https://github.com/rfjakob/gocryptfs/commit/ccf1a84)) -* MacOS: specify a volume name - ([9f8e19b](https://github.com/rfjakob/gocryptfs/commit/9f8e19b)) -* Enable writing to write-only files - ([issue #125](https://github.com/rfjakob/gocryptfs/issues/125)) - -v1.4, 2017-06-20 -* **Switch to static binary releases** - * From gocryptfs v1.4, I will only release statically-built binaries. - These support all Linux distributions but cannot use OpenSSL. - * OpenSSL is still supported - just compile from source! -* Add `-force_owner` option to allow files to be presented as owned by a - different user or group from the user running gocryptfs. Please see caveats - and guidance in the man page before using this functionality. -* Increase open file limit to 4096 ([#82](https://github.com/rfjakob/gocryptfs/issues/82)). -* Implement path decryption via ctlsock ([#84](https://github.com/rfjakob/gocryptfs/issues/84)). - Previously, decryption was only implemented for reverse mode. Now both - normal and reverse mode support both decryption and encryption of - paths via ctlsock. -* Add more specific exit codes for the most common failure modes, - documented in [CLI_ABI.md](Documentation/CLI_ABI.md) -* Reverse mode: make sure hard-linked files always return the same - ciphertext - ([commit 9ecf2d1a](https://github.com/rfjakob/gocryptfs/commit/9ecf2d1a3f69e3d995012073afe3fc664bd928f2)) -* Display a shorter, friendlier help text by default. -* **Parallelize file content encryption** by splitting data blocks into two - threads ([ticket#116](https://github.com/rfjakob/gocryptfs/issues/116)) -* Prefetch random nonces in the background - ([commit 80516ed](https://github.com/rfjakob/gocryptfs/commit/80516ed3351477793eec882508969b6b29b69b0a)) -* Add `-info` option to pretty-print infos about a filesystem. - -v1.3, 2017-04-29 -* **Use HKDF to derive separate keys for GCM and EME** - * New feature flag: `HKDF` (enabled by default) - * This is a forwards-compatible change. gocryptfs v1.3 can mount - filesystems created by earlier versions but not the other way round. -* **Enable Raw64 filename encoding by default (gets rid of trailing `==` characters)** - * This is a forwards-compatible change. gocryptfs v1.3 can mount - filesystems created by earlier versions but not the other way round. -* Drop Go 1.4 compatibility. You now need Go 1.5 (released 2015-08-19) - or higher to build gocryptfs. -* Add `-serialize_reads` command-line option - * This can greatly improve performance on storage - that is very slow for concurrent out-of-order reads. Example: - Amazon Cloud Drive ([#92](https://github.com/rfjakob/gocryptfs/issues/92)) -* Reject file-header-only files - ([#90 2.2](https://github.com/rfjakob/gocryptfs/issues/90), - [commit](https://github.com/rfjakob/gocryptfs/commit/14038a1644f17f50b113a05d09a2a0a3b3e973b2)) -* Increase max password size to 2048 bytes ([#93](https://github.com/rfjakob/gocryptfs/issues/93)) -* Use stable 64-bit inode numbers in reverse mode - * This may cause problems for very old 32-bit applications - that were compiled without Large File Support. -* Passing "--" now also blocks "-o" parsing - -v1.2.1, 2017-02-26 -* Add an integrated speed test, `gocryptfs -speed` -* Limit password size to 1000 bytes and reject trailing garbage after the newline -* Make the test suite work on [Mac OS X](https://github.com/rfjakob/gocryptfs/issues/15) -* Handle additional corner cases in `-ctlsock` path sanitization -* Use dedicated exit code 12 on "password incorrect" - -v1.2, 2016-12-04 -* Add a control socket interface. Allows to encrypt and decrypt filenames. - For details see [backintime#644](https://github.com/bit-team/backintime/issues/644#issuecomment-259835183). - * New command-line option: `-ctlsock` -* Under certain circumstances, concurrent truncate and read could return - an I/O error. This is fixed by introducing a global open file table - that stores the file IDs - ([commit](https://github.com/rfjakob/gocryptfs/commit/0489d08ae21107990d0efd0685443293aa26b35f)). -* Coalesce 4kB ciphertext block writes up to the size requested through - the write FUSE call - ([commit with benchmarks](https://github.com/rfjakob/gocryptfs/commit/024511d9c71558be4b1169d6bb43bd18d65539e0)) -* Add `-noprealloc` command-line option - * Greatly speeds up writes on Btrfs - ([#63](https://github.com/rfjakob/gocryptfs/issues/63)) - at the cost of reduced out-of-space robustness. - * This is a workaround for Btrfs' slow fallocate(2) -* Preserve owner for symlinks an device files (fixes bug [#64](https://github.com/rfjakob/gocryptfs/issues/64)) -* Include rendered man page `gocryptfs.1` in the release tarball - -v1.1.1, 2016-10-30 -* Fix a panic on setting file timestamps ([go-fuse#131](https://github.com/hanwen/go-fuse/pull/131)) -* Work around an issue in tmpfs that caused a panic in xfstests generic/075 - ([gocryptfs#56](https://github.com/rfjakob/gocryptfs/issues/56)) -* Optimize NFS streaming writes - ([commit](https://github.com/rfjakob/gocryptfs/commit/a08d55f42d5b11e265a8617bee16babceebfd026)) - -v1.1, 2016-10-19 -* **Add reverse mode ([#19](https://github.com/rfjakob/gocryptfs/issues/19))** - * AES-SIV (RFC5297) encryption to implement deterministic encryption - securely. Uses the excellent - [jacobsa/crypto](https://github.com/jacobsa/crypto) library. - The corresponding feature flag is called `AESSIV`. - * New command-line options: `-reverse`, `-aessiv` - * Filesystems using reverse mode can only be mounted with gocryptfs v1.1 - and later. - * The default, forward mode, stays fully compatible with older versions. - Forward mode will keep using GCM because it is much faster. -* Accept `-o foo,bar,baz`-style options that are passed at the end of - the command-line, like mount(1) does. All other options must still - precede the passed paths. - * This allows **mounting from /etc/fstab**. See - [#45](https://github.com/rfjakob/gocryptfs/issues/45) for details. - * **Mounting on login using pam_mount** works as well. It is - [described in the wiki](https://github.com/rfjakob/gocryptfs/wiki/Mounting-on-login-using-pam_mount). -* To prevent confusion, the old `-o` option had to be renamed. It is now - called `-ko`. Arguments to `-ko` are passed directly to the kernel. -* New `-passfile` command-line option. Provides an easier way to read - the password from a file. Internally, this is equivalent to - `-extpass "/bin/cat FILE"`. -* Enable changing the password when you only know the master key - ([#28](https://github.com/rfjakob/gocryptfs/issues/28)) - -v1.0, 2016-07-17 -* Deprecate very old filesystems, stage 3/3 - * Filesystems created by v0.6 can no longer be mounted - * Drop command-line options `-gcmiv128`, `-emenames`, `-diriv`. These - are now always enabled. -* Add fallocate(2) support -* New command-line option `-o` - * Allows to pass mount options directly to the kernel -* Add support for device files and suid binaries - * Only works when running as root - * Must be explicitly enabled by passing "-o dev" or "-o suid" or "-o suid,dev" -* Experimental Mac OS X support. See - [ticket #15](https://github.com/rfjakob/gocryptfs/issues/15) for details. - -v0.12, 2016-06-19 -* Deprecate very old filesystems, stage 2/3 - * Filesystems created by v0.6 and older can only be mounted read-only - * A [message](https://github.com/rfjakob/gocryptfs/blob/v0.12/internal/configfile/config_file.go#L120) - explaining the situation is printed as well -* New command line option: `-ro` - * Mounts the filesystem read-only -* Accept password from stdin as well ([ticket #30](https://github.com/rfjakob/gocryptfs/issues/30)) - -v0.11, 2016-06-10 -* Deprecate very old filesystems, stage 1/3 - * Filesystems created by v0.6 and older can still be mounted but a - [warning](https://github.com/rfjakob/gocryptfs/blob/v0.11/internal/configfile/config_file.go#L120) - is printed - * See [ticket #29](https://github.com/rfjakob/gocryptfs/issues/29) for details and - join the discussion -* Add rsync stress test "pingpong-rsync.bash" - * Fix chown and utimens failures that caused rsync to complain -* Build release binaries with Go 1.6.2 - * Big speedup for CPUs with AES-NI, see [ticket #23](https://github.com/rfjakob/gocryptfs/issues/23) - -v0.10, 2016-05-30 -* **Replace `spacemonkeygo/openssl` with `stupidgcm`** - * gocryptfs now has its own thin wrapper to OpenSSL's GCM implementation - called `stupidgcm`. - * This should fix the [compile issues](https://github.com/rfjakob/gocryptfs/issues/21) - people are seeing with `spacemonkeygo/openssl`. It also gets us - a 20% performance boost for streaming writes. -* **Automatically choose between OpenSSL and Go crypto** [issue #23](https://github.com/rfjakob/gocryptfs/issues/23) - * Go 1.6 added an optimized GCM implementation in amd64 assembly that uses AES-NI. - This is faster than OpenSSL and is used if available. In all other - cases OpenSSL is much faster and is used instead. - * `-openssl=auto` is the new default - * Passing `-openssl=true/false` overrides the autodetection. -* Warn but continue anyway if fallocate(2) is not supported by the - underlying filesystem, see [issue #22](https://github.com/rfjakob/gocryptfs/issues/22) - * Enables to use gocryptfs on ZFS and ext3, albeit with reduced out-of-space safety. -* [Fix statfs](https://github.com/rfjakob/gocryptfs/pull/27), by @lxp -* Fix a fsstress [failure](https://github.com/hanwen/go-fuse/issues/106) - in the go-fuse library. - -v0.9, 2016-04-10 -* **Long file name support** - * gocryptfs now supports file names up to 255 characters. - * This is a forwards-compatible change. gocryptfs v0.9 can mount filesystems - created by earlier versions but not the other way round. -* Refactor gocryptfs into multiple "internal" packages -* New command-line options: - * `-longnames`: Enable long file name support (default true) - * `-nosyslog`: Print messages to stdout and stderr instead of syslog (default false) - * `-wpanic`: Make warning messages fatal (used for testing) - * `-d`: Alias for `-debug` - * `-q`: Alias for `-quiet` - -v0.8, 2016-01-23 -* Redirect output to syslog when running in the background -* New command-line option: - * `-memprofile`: Write a memory allocation debugging profile the specified - file - -v0.7.2, 2016-01-19 -* **Fix performance issue in small file creation** - * This brings performance on-par with EncFS paranoia mode, with streaming writes - significantly faster - * The actual [fix](https://github.com/hanwen/go-fuse/commit/c4b6b7949716d13eec856baffc7b7941ae21778c) - is in the go-fuse library. There are no code changes in gocryptfs. - -v0.7.1, 2016-01-09 -* Make the `build.bash` script compatible with Go 1.3 -* Disable fallocate on OSX (system call not available) -* Introduce pre-built binaries for Fedora 23 and Debian 8 - -v0.7, 2015-12-20 -* **Extend GCM IV size to 128 bit from Go's default of 96 bit** - * This pushes back the birthday bound to make IV collisions virtually - impossible - * This is a forwards-compatible change. gocryptfs v0.7 can mount filesystems - created by earlier versions but not the other way round. -* New command-line option: - * `-gcmiv128`: Use 128-bit GCM IVs (default true) - -v0.6, 2015-12-08 -* **Wide-block filename encryption using EME + DirIV** - * EME (ECB-Mix-ECB) provides even better security than CBC as it fixes - the prefix leak. The used Go EME implementation is - https://github.com/rfjakob/eme which is, as far as I know, the first - implementation of EME in Go. - * This is a forwards-compatible change. gocryptfs v0.6 can mount filesystems - created by earlier versions but not the other way round. -* New command-line option: - * `-emenames`: Enable EME filename encryption (default true) - -v0.5.1, 2015-12-06 -* Fix a rename regression caused by DirIV and add test case -* Use fallocate to guard against out-of-space errors - -v0.5, 2015-12-04 -* **Stronger filename encryption: DirIV** - * Each directory gets a random 128 bit file name IV on creation, - stored in `gocryptfs.diriv` - * This makes it impossible to identify identically-named files across - directories - * A single-entry IV cache brings the performance cost of DirIV close to - zero for common operations (see performance.txt) - * This is a forwards-compatible change. gocryptfs v0.5 can mount filesystems - created by earlier versions but not the other way round. -* New command-line option: - * `-diriv`: Use the new per-directory IV file name encryption (default true) - * `-scryptn`: allows to set the scrypt cost parameter N. This option - can be used for faster mounting at the cost of lower brute-force - resistance. It was mainly added to speed up the automated tests. - -v0.4, 2015-11-15 -* New command-line options: - * `-plaintextnames`: disables filename encryption, added on user request - * `-extpass`: calls an external program for prompting for the password - * `-config`: allows to specify a custom gocryptfs.conf path -* Add `FeatureFlags` gocryptfs.conf parameter - * This is a config format change, hence the on-disk format is incremented - * Used for ext4-style filesystem feature flags. This should help avoid future - format changes. The first user is `-plaintextnames`. -* On-disk format 2 - -v0.3, 2015-11-01 -* **Add a random 128 bit file header to authenticate file->block ownership** - * This is an on-disk-format change -* On-disk format 1 - -v0.2, 2015-10-11 -* Replace bash daemonization wrapper with native Go implementation -* Better user feedback on mount failures - -v0.1, 2015-10-07 -* First release -* On-disk format 0 +## Warning ! +The only goal of this library is to be integrated in [DroidFS](https://forge.chapril.org/hardcoresushi/DroidFS). It's not actually ready for other usages. libgocryptfs doesn't implement all features provided by gocryptfs like symbolic links creation, thread-safety, reverse volume creation... Use it at your own risk ! \ No newline at end of file diff --git a/allocator/allocator32.go b/allocator/allocator32.go new file mode 100644 index 0000000..7fb5605 --- /dev/null +++ b/allocator/allocator32.go @@ -0,0 +1,13 @@ +// +build !arm64 +// +build !amd64 + +package allocator + +import ( + "C" + "unsafe" +) + +func Malloc(size int) unsafe.Pointer { + return C.malloc(C.uint(C.sizeof_int * size)) +} diff --git a/allocator/allocator64.go b/allocator/allocator64.go new file mode 100644 index 0000000..11a51d7 --- /dev/null +++ b/allocator/allocator64.go @@ -0,0 +1,12 @@ +// +build arm64 amd64 + +package allocator + +import ( + "C" + "unsafe" +) + +func Malloc(size int) unsafe.Pointer { + return C.malloc(C.ulong(C.sizeof_int * size)) +} diff --git a/benchmark-reverse.bash b/benchmark-reverse.bash deleted file mode 100755 index be98fc1..0000000 --- a/benchmark-reverse.bash +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -eu - -# Benchmark gocryptfs' reverse mode - -cd "$(dirname "$0")" -MYNAME=$(basename "$0") -source tests/fuse-unmount.bash - -# Download /tmp/linux-3.0.tar.gz -./tests/dl-linux-tarball.bash - -cd /tmp -PLAIN=linux-3.0 - -SIZE=0 -if [[ -d $PLAIN ]]; then - SIZE=$(du -s --apparent-size $PLAIN | cut -f1) -fi - - -if [[ $SIZE -ne 412334 ]] ; then - echo "Extracting linux-3.0.tar.gz" - rm -Rf $PLAIN - tar xf linux-3.0.tar.gz -fi - -rm -f $PLAIN/.gocryptfs.reverse.conf -gocryptfs -q -init -reverse -extpass="echo test" -scryptn=10 $PLAIN - -MNT=$(mktemp -d /tmp/linux-3.0.reverse.mnt.XXX) - -# Cleanup trap -trap 'rm -f "$PLAIN/.gocryptfs.reverse.conf" ; fuse-unmount -z "$MNT" ; rmdir "$MNT"' EXIT - -# Mount -gocryptfs -q -reverse -extpass="echo test" "$PLAIN" "$MNT" - -# Execute command, discard all stdout output, print elapsed time -# (to stderr, unfortunately). -function etime { - # Make the bash builtin "time" print out only the elapse wall clock - # seconds - TIMEFORMAT=%R - time "$@" > /dev/null -} - -echo -n "LS: " -etime ls -lR "$MNT" -echo -n "CAT: " -etime find "$MNT" -type f -exec cat {} + diff --git a/benchmark.bash b/benchmark.bash deleted file mode 100755 index 578979d..0000000 --- a/benchmark.bash +++ /dev/null @@ -1,103 +0,0 @@ -#!/bin/bash -eu - -# Run the set of "canonical" benchmarks that are shown on -# https://nuetzlich.net/gocryptfs/comparison/ - -cd "$(dirname "$0")" -MYNAME=$(basename "$0") -source tests/fuse-unmount.bash - -function usage { - echo "Usage: $MYNAME [-encfs] [-openssl=true] [-openssl=false] [-dd] [DIR]" -} - -OPT_ENCFS=0 -OPT_LOOPBACK=0 -OPT_OPENSSL="" -OPT_DIR="" -DD_ONLY="" - -while [[ $# -gt 0 ]] ; do - case $1 in - -h) - usage - exit 1 - ;; - -encfs) - OPT_ENCFS=1 - ;; - -openssl=true) - OPT_OPENSSL="-openssl=true" - ;; - -openssl=false) - OPT_OPENSSL="-openssl=false" - ;; - -dd) - DD_ONLY=1 - ;; - -loopback) - OPT_LOOPBACK=1 - ;; - -*) - echo "Invalid option: $1" - usage - exit 2 - ;; - *) - if [[ -n $OPT_DIR ]] ; then - echo "Duplicate DIR argument: $1" - usage - exit 3 - fi - OPT_DIR=$1 - ;; - esac - shift -done - -if [[ -z $OPT_DIR ]] ; then - OPT_DIR=/tmp -fi - -# Create directories -CRYPT=$(mktemp -d "$OPT_DIR/$MYNAME.XXX") -MNT=$CRYPT.mnt -mkdir "$MNT" - -# Mount -if [[ $OPT_ENCFS -eq 1 ]]; then - if [[ -n $OPT_OPENSSL ]] ; then - echo "The option $OPT_OPENSSL only works with gocryptfs" - exit 1 - fi - echo -n "Testing EncFS at $CRYPT: " - encfs --version - encfs --extpass="echo test" --standard "$CRYPT" "$MNT" > /dev/null -elif [[ $OPT_LOOPBACK -eq 1 ]]; then - echo "Testing go-fuse loopback" - "$HOME/go/src/github.com/hanwen/go-fuse/example/loopback/loopback" "$MNT" "$CRYPT" & - sleep 0.5 -else - echo -n "Testing gocryptfs at $CRYPT: " - gocryptfs -version - gocryptfs -q -init -extpass="echo test" -scryptn=10 "$CRYPT" - gocryptfs -q -extpass="echo test" $OPT_OPENSSL "$CRYPT" "$MNT" -fi - -# Make sure we have actually mounted something -if ! mountpoint "$MNT" ; then - exit 1 -fi - -# Cleanup trap -trap 'cd /; fuse-unmount -z "$MNT"; rm -rf "$CRYPT" "$MNT"' EXIT - -# Benchmarks -if [[ $DD_ONLY -eq 1 ]]; then - echo -n "WRITE: " - dd if=/dev/zero "of=$MNT/zero" bs=131072 count=20000 2>&1 | tail -n 1 - rm "$MNT/zero" -else - ./tests/canonical-benchmarks.bash "$MNT" -fi - diff --git a/build-without-openssl.bash b/build-without-openssl.bash deleted file mode 100755 index d5dc218..0000000 --- a/build-without-openssl.bash +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -eu - -cd "$(dirname "$0")" - -CGO_ENABLED=0 source ./build.bash -tags without_openssl - -if ldd gocryptfs 2> /dev/null ; then - echo "build-without-openssl.bash: error: compiled binary is not static" - exit 1 -fi diff --git a/build.bash b/build.bash deleted file mode 100755 index 627a31d..0000000 --- a/build.bash +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash -eu -# -# Compile gocryptfs and bake the git version string of itself and the go-fuse -# library into the binary. -# -# If you want to fake a build date to reproduce a specific build, -# you can use: -# BUILDDATE=2017-02-03 ./build.bash -# or -# SOURCE_DATE_EPOCH=1544192417 ./build.bash -# . - -cd "$(dirname "$0")" - -# Make sure we have the go binary -go version > /dev/null - -# Enable Go Modules on Go 1.11 and 1.12 -# https://dev.to/maelvls/why-is-go111module-everywhere-and-everything-about-go-modules-24k#-raw-go111module-endraw-with-go-111-and-112 -export GO111MODULE=on - -# GOPATH may contain multiple paths separated by ":" -GOPATH1=$(go env GOPATH | cut -f1 -d:) - -# gocryptfs version according to git or a VERSION file -if [[ -d .git ]] ; then - GITVERSION=$(git describe --tags --dirty || echo "[no_tags_found]") - GITBRANCH=$(git rev-parse --abbrev-ref HEAD) - if [[ -n $GITBRANCH && $GITBRANCH != master ]] ; then - GITVERSION="$GITVERSION.$GITBRANCH" - fi -elif [[ -f VERSION ]] ; then - GITVERSION=$(cat VERSION) -else - echo "Warning: could not determine gocryptfs version" - GITVERSION="[unknown]" -fi - -# go-fuse version, if available -if [[ -d vendor/github.com/hanwen/go-fuse ]] ; then - GITVERSIONFUSE="[vendored]" -else - # go-fuse version according to Go Modules - FAIL=0 - OUT=$(go list -m github.com/hanwen/go-fuse/v2 | cut -d' ' -f2-) || FAIL=1 - if [[ $FAIL -eq 0 ]]; then - GITVERSIONFUSE=$OUT - else - echo "Warning: could not determine go-fuse version" - GITVERSIONFUSE="[unknown]" - fi -fi - -# Build date, something like "2017-09-06". Don't override BUILDDATE -# if it is already set. This may be done for reproducible builds. -if [[ -z ${BUILDDATE:-} ]] ; then - BUILDDATE=$(date +%Y-%m-%d) -fi - -# If SOURCE_DATE_EPOCH is set, it overrides BUILDDATE. This is the -# standard environment variable for faking the date in reproducible builds. -if [[ -n ${SOURCE_DATE_EPOCH:-} ]] ; then - BUILDDATE=$(date --utc --date="@${SOURCE_DATE_EPOCH}" +%Y-%m-%d) -fi - -# Only set GOFLAGS if it is not already set by the user -if [[ -z ${GOFLAGS:-} ]] ; then - GOFLAGS="" - # For reproducible builds, we get rid of $HOME references in the - # binary using "-trimpath". - # However, -trimpath needs Go 1.13+, and we support Go 1.11 and Go 1.12 - # too. So don't add it there. - GV=$(go version) - if [[ $GV != *"1.11"* && $GV != *"1.12"* ]] ; then - GOFLAGS="-trimpath" - fi - # Also, Fedora and Arch want pie enabled, so enable it. - # * https://fedoraproject.org/wiki/Changes/golang-buildmode-pie - # * https://github.com/rfjakob/gocryptfs/pull/460 - # But not with CGO_ENABLED=0 (https://github.com/golang/go/issues/30986)! - if [[ ${CGO_ENABLED:-1} -ne 0 ]] ; then - GOFLAGS="$GOFLAGS -buildmode=pie" - fi - export GOFLAGS -fi - -GO_LDFLAGS="-X \"main.GitVersion=$GITVERSION\" -X \"main.GitVersionFuse=$GITVERSIONFUSE\" -X \"main.BuildDate=$BUILDDATE\"" - -# If LDFLAGS is set, add it as "-extldflags". -if [[ -n ${LDFLAGS:-} ]] ; then - GO_LDFLAGS="$GO_LDFLAGS \"-extldflags=$LDFLAGS\"" -fi - -# Actual "go build" call for gocryptfs -go build "-ldflags=$GO_LDFLAGS" "$@" -# Additional binaries -for d in gocryptfs-xray contrib/statfs contrib/findholes contrib/atomicrename ; do - (cd "$d"; go build "-ldflags=$GO_LDFLAGS" "$@") -done - -./gocryptfs -version - -mkdir -p "$GOPATH1/bin" -cp -af gocryptfs "$GOPATH1/bin" diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..909f164 --- /dev/null +++ b/build.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +if [ -z ${ANDROID_NDK_HOME+x} ]; then + echo "Error: \$ANDROID_NDK_HOME is not defined." +elif [ -z ${OPENSSL_PATH+x} ]; then + echo "Error: \$OPENSSL_PATH is not defined." +else + NDK_BIN_PATH="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin" + declare -a ABIs=("x86_64" "x86" "arm64-v8a" "armeabi-v7a") + + compile_openssl(){ + if [ ! -d "./lib/$1" ]; then + if [ "$1" = "x86_64" ]; then + OPENSSL_ARCH="android-x86_64" + elif [ "$1" = "x86" ]; then + OPENSSL_ARCH="android-x86" + elif [ "$1" = "arm64-v8a" ]; then + OPENSSL_ARCH="android-arm64" + elif [ "$1" = "armeabi-v7a" ]; then + OPENSSL_ARCH="android-arm" + else + echo "Invalid ABI: $1" + exit + fi + + export CFLAGS=-D__ANDROID_API__=21 + export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$ANDROID_NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin:$PATH + (cd "$OPENSSL_PATH" && if [ -f "Makefile" ]; then make clean; fi && ./Configure $OPENSSL_ARCH -D__ANDROID_API__=21 && make -j4 build_libs) + mkdir -p "./lib/$1" && cp "$OPENSSL_PATH/libcrypto.a" "$OPENSSL_PATH/libssl.a" "./lib/$1" + mkdir -p "./include/$1" && cp -r "$OPENSSL_PATH"/include/* "./include/$1/" + fi + } + + compile_for_arch() { + compile_openssl $1 + if [ "$1" = "x86_64" ]; then + CFN="x86_64-linux-android21-clang" + elif [ "$1" = "x86" ]; then + export GOARCH=386 + CFN="i686-linux-android21-clang" + elif [ "$1" = "arm64-v8a" ]; then + CFN="aarch64-linux-android21-clang" + export GOARCH=arm64 + export GOARM=7 + elif [ "$1" = "armeabi-v7a" ]; then + CFN="armv7a-linux-androideabi21-clang" + export GOARCH=arm + export GOARM=7 + else + echo "Invalid ABI: $1" + exit + fi + + export CC="$NDK_BIN_PATH/$CFN" + export CXX="$NDK_BIN_PATH/$CFN++" + export CGO_ENABLED=1 + export GOOS=android + export CGO_CFLAGS="-I ${PWD}/include/$1" + export CGO_LDFLAGS="-Wl,-soname=libgocryptfs.so -L${PWD}/lib/$1" + go build -o build/$1/libgocryptfs.so -buildmode=c-shared + } + + if [ "$#" -eq 1 ]; then + compile_for_arch $1 + else + for abi in ${ABIs[@]}; do + echo "Compiling for $abi..." + compile_for_arch $abi + done + fi + echo "Done." +fi \ No newline at end of file diff --git a/cli_args.go b/cli_args.go deleted file mode 100644 index 7743120..0000000 --- a/cli_args.go +++ /dev/null @@ -1,332 +0,0 @@ -package main - -// Should be initialized before anything else. -// This import line MUST be in the alphabitcally first source code file of -// package main! -import _ "github.com/rfjakob/gocryptfs/internal/ensurefds012" - -import ( - "flag" - "fmt" - "net" - "os" - "strconv" - "strings" - "time" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// argContainer stores the parsed CLI options and arguments -type argContainer struct { - debug, init, zerokey, fusedebug, openssl, passwd, fg, version, - plaintextnames, quiet, nosyslog, wpanic, - longnames, allow_other, reverse, aessiv, nonempty, raw64, - noprealloc, speed, hkdf, serialize_reads, forcedecode, hh, info, - sharedstorage, devrandom, fsck bool - // 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 - // -extpass, -badname, -passfile can be passed multiple times - extpass, badname, passfile multipleStrings - // For reverse mode, several ways to specify exclusions. All can be specified multiple times. - exclude, excludeWildcard, excludeFrom multipleStrings - // Configuration file name override - config string - notifypid, scryptn int - // Idle time before autounmount - idle time.Duration - // Helper variables that are NOT cli options all start with an underscore - // _configCustom is true when the user sets a custom config file name. - _configCustom bool - // _ctlsockFd stores the control socket file descriptor (ctlsock stores the path) - _ctlsockFd net.Listener - // _forceOwner is, if non-nil, a parsed, validated Owner (as opposed to the string above) - _forceOwner *fuse.Owner - // _explicitScryptn is true then the user passed "-scryptn=xyz" - _explicitScryptn bool -} - -type multipleStrings []string - -func (s *multipleStrings) String() string { - s2 := []string(*s) - return fmt.Sprint(s2) -} - -func (s *multipleStrings) Set(val string) error { - *s = append(*s, val) - return nil -} - -func (s *multipleStrings) Empty() bool { - s2 := []string(*s) - return len(s2) == 0 -} - -var flagSet *flag.FlagSet - -// prefixOArgs transform options passed via "-o foo,bar" into regular options -// like "-foo -bar" and prefixes them to the command line. -// Testcases in TestPrefixOArgs(). -func prefixOArgs(osArgs []string) ([]string, error) { - // Need at least 3, example: gocryptfs -o foo,bar - // ^ 0 ^ 1 ^ 2 - if len(osArgs) < 3 { - return osArgs, nil - } - // Passing "--" disables "-o" parsing. Ignore element 0 (program name). - for _, v := range osArgs[1:] { - if v == "--" { - return osArgs, nil - } - } - // Find and extract "-o foo,bar" - var otherArgs, oOpts []string - for i := 1; i < len(osArgs); i++ { - if osArgs[i] == "-o" { - // Last argument? - if i+1 >= len(osArgs) { - return nil, fmt.Errorf("The \"-o\" option requires an argument") - } - oOpts = strings.Split(osArgs[i+1], ",") - // Skip over the arguments to "-o" - i++ - } else if strings.HasPrefix(osArgs[i], "-o=") { - oOpts = strings.Split(osArgs[i][3:], ",") - } else { - otherArgs = append(otherArgs, osArgs[i]) - } - } - // Start with program name - newArgs := []string{osArgs[0]} - // Add options from "-o" - for _, o := range oOpts { - if o == "" { - continue - } - if o == "o" || o == "-o" { - tlog.Fatal.Printf("You can't pass \"-o\" to \"-o\"") - os.Exit(exitcodes.Usage) - } - newArgs = append(newArgs, "-"+o) - } - // Add other arguments - newArgs = append(newArgs, otherArgs...) - return newArgs, nil -} - -// parseCliOpts - parse command line options (i.e. arguments that start with "-") -func parseCliOpts() (args argContainer) { - var err error - var opensslAuto string - - os.Args, err = prefixOArgs(os.Args) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Usage) - } - - flagSet = flag.NewFlagSet(tlog.ProgramName, flag.ContinueOnError) - flagSet.Usage = func() {} - flagSet.BoolVar(&args.debug, "d", false, "") - flagSet.BoolVar(&args.debug, "debug", false, "Enable debug output") - flagSet.BoolVar(&args.fusedebug, "fusedebug", false, "Enable fuse library debug output") - flagSet.BoolVar(&args.init, "init", false, "Initialize encrypted directory") - flagSet.BoolVar(&args.zerokey, "zerokey", false, "Use all-zero dummy master key") - // Tri-state true/false/auto - flagSet.StringVar(&opensslAuto, "openssl", "auto", "Use OpenSSL instead of built-in Go crypto") - flagSet.BoolVar(&args.passwd, "passwd", false, "Change password") - flagSet.BoolVar(&args.fg, "f", false, "") - flagSet.BoolVar(&args.fg, "fg", false, "Stay in the foreground") - flagSet.BoolVar(&args.version, "version", false, "Print version and exit") - flagSet.BoolVar(&args.plaintextnames, "plaintextnames", false, "Do not encrypt file names") - flagSet.BoolVar(&args.quiet, "q", false, "") - flagSet.BoolVar(&args.quiet, "quiet", false, "Quiet - silence informational messages") - flagSet.BoolVar(&args.nosyslog, "nosyslog", false, "Do not redirect output to syslog when running in the background") - flagSet.BoolVar(&args.wpanic, "wpanic", false, "When encountering a warning, panic and exit immediately") - flagSet.BoolVar(&args.longnames, "longnames", true, "Store names longer than 176 bytes in extra files") - flagSet.BoolVar(&args.allow_other, "allow_other", false, "Allow other users to access the filesystem. "+ - "Only works if user_allow_other is set in /etc/fuse.conf.") - flagSet.BoolVar(&args.reverse, "reverse", false, "Reverse mode") - flagSet.BoolVar(&args.aessiv, "aessiv", false, "AES-SIV encryption") - flagSet.BoolVar(&args.nonempty, "nonempty", false, "Allow mounting over non-empty directories") - flagSet.BoolVar(&args.raw64, "raw64", true, "Use unpadded base64 for file names") - flagSet.BoolVar(&args.noprealloc, "noprealloc", false, "Disable preallocation before writing") - flagSet.BoolVar(&args.speed, "speed", false, "Run crypto speed test") - flagSet.BoolVar(&args.hkdf, "hkdf", true, "Use HKDF as an additional key derivation step") - flagSet.BoolVar(&args.serialize_reads, "serialize_reads", false, "Try to serialize read operations") - flagSet.BoolVar(&args.forcedecode, "forcedecode", false, "Force decode of files even if integrity check fails."+ - " Requires gocryptfs to be compiled with openssl support and implies -openssl true") - flagSet.BoolVar(&args.hh, "hh", false, "Show this long help text") - flagSet.BoolVar(&args.info, "info", false, "Display information about CIPHERDIR") - flagSet.BoolVar(&args.sharedstorage, "sharedstorage", false, "Make concurrent access to a shared CIPHERDIR safer") - flagSet.BoolVar(&args.devrandom, "devrandom", false, "Use /dev/random for generating master key") - flagSet.BoolVar(&args.fsck, "fsck", false, "Run a filesystem check on CIPHERDIR") - - // Mount options with opposites - flagSet.BoolVar(&args.dev, "dev", false, "Allow device files") - flagSet.BoolVar(&args.nodev, "nodev", false, "Deny device files") - flagSet.BoolVar(&args.suid, "suid", false, "Allow suid binaries") - flagSet.BoolVar(&args.nosuid, "nosuid", false, "Deny suid binaries") - flagSet.BoolVar(&args.exec, "exec", false, "Allow executables") - flagSet.BoolVar(&args.noexec, "noexec", false, "Deny executables") - flagSet.BoolVar(&args.rw, "rw", false, "Mount the filesystem read-write") - flagSet.BoolVar(&args.ro, "ro", false, "Mount the filesystem read-only") - flagSet.BoolVar(&args.kernel_cache, "kernel_cache", false, "Enable the FUSE kernel_cache option") - flagSet.BoolVar(&args.acl, "acl", false, "Enforce ACLs") - - flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key") - flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file") - flagSet.StringVar(&args.memprofile, "memprofile", "", "Write memory profile to specified file") - flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf") - flagSet.StringVar(&args.ko, "ko", "", "Pass additional options directly to the kernel, comma-separated list") - flagSet.StringVar(&args.ctlsock, "ctlsock", "", "Create control socket at specified path") - flagSet.StringVar(&args.fsname, "fsname", "", "Override the filesystem name") - 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") - - // Exclusion options - flagSet.Var(&args.exclude, "e", "Alias for -exclude") - flagSet.Var(&args.exclude, "exclude", "Exclude relative path from reverse view") - flagSet.Var(&args.excludeWildcard, "ew", "Alias for -exclude-wildcard") - flagSet.Var(&args.excludeWildcard, "exclude-wildcard", "Exclude path from reverse view, supporting wildcards") - flagSet.Var(&args.excludeFrom, "exclude-from", "File from which to read exclusion patterns (with -exclude-wildcard syntax)") - - // multipleStrings options ([]string) - flagSet.Var(&args.extpass, "extpass", "Use external program for the password prompt") - flagSet.Var(&args.badname, "badname", "Glob pattern invalid file names that should be shown") - flagSet.Var(&args.passfile, "passfile", "Read password from file") - - flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+ - "successful mount - used internally for daemonization") - const scryptn = "scryptn" - flagSet.IntVar(&args.scryptn, scryptn, configfile.ScryptDefaultLogN, "scrypt cost parameter logN. Possible values: 10-28. "+ - "A lower value speeds up mounting and reduces its memory needs, but makes the password susceptible to brute-force attacks") - - flagSet.DurationVar(&args.idle, "i", 0, "Alias for -idle") - flagSet.DurationVar(&args.idle, "idle", 0, "Auto-unmount after specified idle duration (ignored in reverse mode). "+ - "Durations are specified like \"500s\" or \"2h45m\". 0 means stay mounted indefinitely.") - - var nofail bool - flagSet.BoolVar(&nofail, "nofail", false, "Ignored for /etc/fstab compatibility") - - var dummyString string - flagSet.StringVar(&dummyString, "o", "", "For compatibility with mount(1), options can be also passed as a comma-separated list to -o on the end.") - // Actual parsing - err = flagSet.Parse(os.Args[1:]) - if err == flag.ErrHelp { - helpShort() - os.Exit(0) - } - if err != nil { - tlog.Fatal.Printf("Invalid command line: %s. Try '%s -help'.", prettyArgs(), tlog.ProgramName) - os.Exit(exitcodes.Usage) - } - // We want to know if -scryptn was passed explicitly - if isFlagPassed(flagSet, scryptn) { - args._explicitScryptn = true - } - // "-openssl" needs some post-processing - if opensslAuto == "auto" { - args.openssl = stupidgcm.PreferOpenSSL() - } else { - args.openssl, err = strconv.ParseBool(opensslAuto) - if err != nil { - tlog.Fatal.Printf("Invalid \"-openssl\" setting: %v", err) - os.Exit(exitcodes.Usage) - } - } - // "-forcedecode" only works with openssl. Check compilation and command line parameters - if args.forcedecode == true { - if stupidgcm.BuiltWithoutOpenssl == true { - tlog.Fatal.Printf("The -forcedecode flag requires openssl support, but gocryptfs was compiled without it!") - os.Exit(exitcodes.Usage) - } - if args.aessiv == true { - tlog.Fatal.Printf("The -forcedecode and -aessiv flags are incompatible because they use different crypto libs (openssl vs native Go)") - os.Exit(exitcodes.Usage) - } - if args.reverse == true { - tlog.Fatal.Printf("The reverse mode and the -forcedecode option are not compatible") - os.Exit(exitcodes.Usage) - } - // Has the user explicitly disabled openssl using "-openssl=false/0"? - if !args.openssl && opensslAuto != "auto" { - tlog.Fatal.Printf("-forcedecode requires openssl, but is disabled via command-line option") - os.Exit(exitcodes.Usage) - } - args.openssl = true - - // Try to make it harder for the user to shoot himself in the foot. - args.ro = true - args.allow_other = false - args.ko = "noexec" - } - if !args.extpass.Empty() && len(args.passfile) != 0 { - tlog.Fatal.Printf("The options -extpass and -passfile cannot be used at the same time") - os.Exit(exitcodes.Usage) - } - if len(args.passfile) != 0 && args.masterkey != "" { - tlog.Fatal.Printf("The options -passfile and -masterkey cannot be used at the same time") - os.Exit(exitcodes.Usage) - } - if !args.extpass.Empty() && args.masterkey != "" { - tlog.Fatal.Printf("The options -extpass and -masterkey cannot be used at the same time") - os.Exit(exitcodes.Usage) - } - if !args.extpass.Empty() && args.fido2 != "" { - tlog.Fatal.Printf("The options -extpass and -fido2 cannot be used at the same time") - os.Exit(exitcodes.Usage) - } - if args.idle < 0 { - tlog.Fatal.Printf("Idle timeout cannot be less than 0") - os.Exit(exitcodes.Usage) - } - return args -} - -// prettyArgs pretty-prints the command-line arguments. -func prettyArgs() string { - pa := fmt.Sprintf("%v", os.Args) - // Get rid of "[" and "]" - pa = pa[1 : len(pa)-1] - return pa -} - -// countOpFlags counts the number of operation flags we were passed. -func countOpFlags(args *argContainer) int { - var count int - if args.info { - count++ - } - if args.passwd { - count++ - } - if args.init { - count++ - } - if args.fsck { - count++ - } - return count -} - -// isFlagPassed finds out if the flag was explictely passed on the command line. -// https://stackoverflow.com/a/54747682/1380267 -func isFlagPassed(flagSet *flag.FlagSet, name string) bool { - found := false - flagSet.Visit(func(f *flag.Flag) { - if f.Name == name { - found = true - } - }) - return found -} diff --git a/cli_args_test.go b/cli_args_test.go deleted file mode 100644 index 8e5ae3d..0000000 --- a/cli_args_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "reflect" - "testing" -) - -type testcase struct { - // i is the input - i []string - // o is the expected output - o []string - // Do we expect an error? - e bool -} - -// TestPrefixOArgs checks that the "-o x,y,z" parsing works correctly. -func TestPrefixOArgs(t *testing.T) { - testcases := []testcase{ - { - i: nil, - o: nil, - }, - { - i: []string{"gocryptfs"}, - o: []string{"gocryptfs"}, - }, - { - i: []string{"gocryptfs", "-v"}, - o: []string{"gocryptfs", "-v"}, - }, - { - i: []string{"gocryptfs", "foo", "bar", "-v"}, - o: []string{"gocryptfs", "foo", "bar", "-v"}, - }, - { - i: []string{"gocryptfs", "foo", "bar", "-o", "a"}, - o: []string{"gocryptfs", "-a", "foo", "bar"}, - }, - { - i: []string{"gocryptfs", "foo", "bar", "-o", "a,b,xxxxx"}, - o: []string{"gocryptfs", "-a", "-b", "-xxxxx", "foo", "bar"}, - }, - { - i: []string{"gocryptfs", "foo", "bar", "-d", "-o=a,b,xxxxx"}, - o: []string{"gocryptfs", "-a", "-b", "-xxxxx", "foo", "bar", "-d"}, - }, - { - i: []string{"gocryptfs", "foo", "bar", "-oooo", "a,b,xxxxx"}, - o: []string{"gocryptfs", "foo", "bar", "-oooo", "a,b,xxxxx"}, - }, - // https://github.com/mhogomchungu/sirikali/blob/a36d91d3e39f0c1eb9a79680ed6c28ddb6568fa8/src/siritask.cpp#L192 - { - i: []string{"gocryptfs", "-o", "rw", "--config", "fff", "ccc", "mmm"}, - o: []string{"gocryptfs", "-rw", "--config", "fff", "ccc", "mmm"}, - }, - // "--" should also block "-o" parsing. - { - i: []string{"gocryptfs", "foo", "bar", "--", "-o", "a"}, - o: []string{"gocryptfs", "foo", "bar", "--", "-o", "a"}, - }, - { - i: []string{"gocryptfs", "--", "-o", "a"}, - o: []string{"gocryptfs", "--", "-o", "a"}, - }, - // This should error out - { - i: []string{"gocryptfs", "foo", "bar", "-o"}, - e: true, - }, - } - for _, tc := range testcases { - o, err := prefixOArgs(tc.i) - e := (err != nil) - if !reflect.DeepEqual(o, tc.o) || e != tc.e { - t.Errorf("\n in=%q\nwant=%q err=%v\n got=%q err=%v", tc.i, tc.o, tc.e, o, e) - } - } -} - -func TestStringSlice(t *testing.T) { - var s multipleStrings - s.Set("foo") - s.Set("bar") - want := "[foo bar]" - have := s.String() - if want != have { - t.Errorf("Wrong string representation: want=%q have=%q", want, have) - } -} diff --git a/codelingo.yaml b/codelingo.yaml deleted file mode 100644 index 8e5d778..0000000 --- a/codelingo.yaml +++ /dev/null @@ -1,4 +0,0 @@ -tenets: -- import: codelingo/effective-go -- import: codelingo/code-review-comments -- import: codelingo/rfjakob-gocryptfs diff --git a/common_ops.go b/common_ops.go new file mode 100644 index 0000000..cb61e86 --- /dev/null +++ b/common_ops.go @@ -0,0 +1,88 @@ +package main + +import ( + "C" + "syscall" + + "golang.org/x/sys/unix" + + "./internal/nametransform" + "./internal/syscallcompat" +) + +//export gcf_get_attrs +func gcf_get_attrs(sessionID int, relPath string) (uint64, int64, bool) { + volume := OpenedVolumes[sessionID] + dirfd, cName, err := volume.prepareAtSyscall(relPath) + if err != nil { + return 0, 0, false + } + defer syscall.Close(dirfd) + + st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + return 0, 0, false + } + + // Translate ciphertext size to plaintext size + size := volume.translateSize(dirfd, cName, st) + + return size, int64(st.Mtim.Sec), true +} + +//export gcf_rename +func gcf_rename(sessionID int, oldPath string, newPath string) bool { + volume := OpenedVolumes[sessionID] + dirfd, cName, err := volume.prepareAtSyscall(oldPath) + if err != nil { + return false + } + defer syscall.Close(dirfd) + + dirfd2, cName2, err := volume.prepareAtSyscall(newPath) + if err != nil { + return false + } + defer syscall.Close(dirfd2) + + // Easy case. + if volume.plainTextNames { + return errToBool(syscallcompat.Renameat2(dirfd, cName, dirfd2, cName2, 0)) + } + // Long destination file name: create .name file + nameFileAlreadyThere := false + if nametransform.IsLongContent(cName2) { + err = volume.nameTransform.WriteLongNameAt(dirfd2, cName2, newPath) + // Failure to write the .name file is expected when the target path already + // exists. Since hashes are pretty unique, there is no need to modify the + // .name file in this case, and we ignore the error. + if err == syscall.EEXIST { + nameFileAlreadyThere = true + } else if err != nil { + return false + } + } + // Actual rename + err = syscallcompat.Renameat2(dirfd, cName, dirfd2, cName2, 0) + if err == syscall.ENOTEMPTY || err == syscall.EEXIST { + // If an empty directory is overwritten we will always get an error as + // the "empty" directory will still contain gocryptfs.diriv. + // Interestingly, ext4 returns ENOTEMPTY while xfs returns EEXIST. + // We handle that by trying to fs.Rmdir() the target directory and trying + // again. + if gcf_rmdir(sessionID, newPath) { + err = syscallcompat.Renameat2(dirfd, cName, dirfd2, cName2, 0) + } + } + if err != nil { + if nametransform.IsLongContent(cName2) && !nameFileAlreadyThere { + // Roll back .name creation unless the .name file was already there + nametransform.DeleteLongNameAt(dirfd2, cName2) + } + return false + } + if nametransform.IsLongContent(cName) { + nametransform.DeleteLongNameAt(dirfd, cName) + } + return true +} diff --git a/contrib/atomicrename/.gitignore b/contrib/atomicrename/.gitignore deleted file mode 100644 index b91a212..0000000 --- a/contrib/atomicrename/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/atomicrename diff --git a/contrib/atomicrename/main.go b/contrib/atomicrename/main.go deleted file mode 100644 index 67088b0..0000000 --- a/contrib/atomicrename/main.go +++ /dev/null @@ -1,101 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "io/ioutil" - "os" - "strings" - "sync/atomic" - "syscall" -) - -const fileCount = 100 - -type stats struct { - renameOk int - renameError int - readOk int - readError int - readContentMismatch int -} - -func usage() { - fmt.Printf(`atomicrename creates %d "src" files in the current directory, renames -them in random order over a single "dst" file while reading the "dst" -file concurrently in a loop. - -Progress and errors are reported as they occour in addition to a summary -printed at the end. cifs and fuse filesystems are known to fail, local -filesystems and nfs seem ok. - -See https://github.com/hanwen/go-fuse/issues/398 for background info. -`, fileCount) - os.Exit(1) -} - -func main() { - flag.Usage = usage - flag.Parse() - - hello := []byte("hello world") - srcFiles := make(map[string]struct{}) - - // prepare source files - fmt.Print("creating files") - for i := 0; i < fileCount; i++ { - srcName := fmt.Sprintf("src.atomicrename.%d", i) - srcFiles[srcName] = struct{}{} - buf := bytes.Repeat([]byte("_"), i) - buf = append(buf, hello...) - if err := ioutil.WriteFile(srcName, buf, 0600); err != nil { - panic(err) - } - fmt.Print(".") - } - fmt.Print("\n") - - // prepare destination file - const dstName = "dst.atomicrename" - if err := ioutil.WriteFile(dstName, hello, 0600); err != nil { - panic(err) - } - - var running int32 = 1 - - stats := stats{} - - // read thread - go func() { - for atomic.LoadInt32(&running) == 1 { - have, err := ioutil.ReadFile(dstName) - if err != nil { - fmt.Println(err) - stats.readError++ - continue - } - if !strings.HasSuffix(string(have), string(hello)) { - fmt.Printf("content mismatch: have %q\n", have) - stats.readContentMismatch++ - continue - } - fmt.Printf("content ok len=%d\n", len(have)) - stats.readOk++ - } - }() - - // rename thread = main thread - for srcName := range srcFiles { - if err := os.Rename(srcName, dstName); err != nil { - fmt.Println(err) - stats.renameError++ - } - stats.renameOk++ - } - // Signal the Read goroutine to stop when loop is done - atomic.StoreInt32(&running, 0) - - syscall.Unlink(dstName) - fmt.Printf("stats: %#v\n", stats) -} diff --git a/contrib/cleanup-tmp-mounts.sh b/contrib/cleanup-tmp-mounts.sh deleted file mode 100755 index 481d2b7..0000000 --- a/contrib/cleanup-tmp-mounts.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# -# Umount all FUSE filesystems mounted below /tmp and /var/tmp. -# -# Useful when you have lots of broken mounts after something in -# the test suite went wrong. - -set -eu - -MOUNTS=$(mount | grep ' type fuse\.' | grep 'on /var/tmp/\|on /tmp/\|on /mnt/ext4-ramdisk/' | cut -d' ' -f 3) - -for i in $MOUNTS ; do - echo "Unmounting $i" - fusermount -u -z "$i" -done diff --git a/contrib/findholes/.gitignore b/contrib/findholes/.gitignore deleted file mode 100644 index 15c37f6..0000000 --- a/contrib/findholes/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/findholes diff --git a/contrib/findholes/holes/holes.go b/contrib/findholes/holes/holes.go deleted file mode 100644 index 7d77ae1..0000000 --- a/contrib/findholes/holes/holes.go +++ /dev/null @@ -1,199 +0,0 @@ -// Package holes finds and pretty-prints holes & data sections of a file. -// Used by TestFileHoleCopy in the gocryptfs test suite. -package holes - -import ( - "fmt" - "log" - "math/rand" - "os" - "syscall" - "time" -) - -const ( - SEEK_DATA = 3 - SEEK_HOLE = 4 - - SegmentHole = SegmentType(100) - SegmentData = SegmentType(101) - SegmentEOF = SegmentType(102) -) - -type Whence int - -func (w Whence) String() string { - switch w { - case SEEK_DATA: - return "SEEK_DATA" - case SEEK_HOLE: - return "SEEK_HOLE" - default: - return "???" - } -} - -type Segment struct { - Offset int64 - Type SegmentType -} - -func (s Segment) String() string { - return fmt.Sprintf("%10d %v", s.Offset, s.Type) -} - -type SegmentType int - -func (s SegmentType) String() string { - switch s { - case SegmentHole: - return "hole" - case SegmentData: - return "data" - case SegmentEOF: - return "eof" - default: - return "???" - } -} - -// PrettyPrint pretty-prints the Segments. -func PrettyPrint(segments []Segment) (out string) { - for i, s := range segments { - out += s.String() - if i < len(segments)-1 { - out += "\n" - } - } - return out -} - -// Find examines the file passed via file descriptor and returns the found holes -// and data sections. -func Find(fd int) (segments []Segment, err error) { - var st syscall.Stat_t - err = syscall.Fstat(fd, &st) - if err != nil { - return nil, err - } - totalSize := st.Size - - var cursor int64 - - // find out if file starts with data or hole - off, err := syscall.Seek(fd, 0, SEEK_DATA) - // starts with hole and has no data - if err == syscall.ENXIO { - segments = append(segments, - Segment{0, SegmentHole}, - Segment{totalSize, SegmentEOF}) - return segments, nil - } - if err != nil { - return nil, err - } - // starts with data - if off == cursor { - segments = append(segments, Segment{0, SegmentData}) - } else { - // starts with hole - segments = append(segments, - Segment{0, SegmentHole}, - Segment{off, SegmentData}) - cursor = off - } - - // now we are at the start of data. - // find next hole, then next data, then next hole, then next data... - for { - oldCursor := cursor - // Next hole - off, err = syscall.Seek(fd, cursor, SEEK_HOLE) - if err != nil { - return nil, err - } - segments = append(segments, Segment{off, SegmentHole}) - cursor = off - - // Next data - off, err := syscall.Seek(fd, cursor, SEEK_DATA) - // No more data? - if err == syscall.ENXIO { - segments = append(segments, - Segment{totalSize, SegmentEOF}) - return segments, nil - } - if err != nil { - return nil, err - } - segments = append(segments, Segment{off, SegmentData}) - cursor = off - - if oldCursor == cursor { - return nil, fmt.Errorf("%s\nerror: seek loop!", PrettyPrint(segments)) - } - } - return segments, nil -} - -// Verify the gives `segments` using a full bytewise file scan -func Verify(fd int, segments []Segment) (err error) { - last := segments[len(segments)-1] - if last.Type != SegmentEOF { - log.Panicf("BUG: last segment is not EOF. segments: %v", segments) - } - - for i, s := range segments { - var whence int - switch s.Type { - case SegmentHole: - whence = SEEK_HOLE - case SegmentData: - whence = SEEK_DATA - case SegmentEOF: - continue - default: - log.Panicf("BUG: unkown segment type %d", s.Type) - } - for off := s.Offset; off < segments[i+1].Offset; off++ { - res, err := syscall.Seek(fd, off, whence) - if err != nil { - return fmt.Errorf("error: seek(%d, %s) returned error %v", off, Whence(whence).String(), err) - } - if res != off { - return fmt.Errorf("error: seek(%d, %s) returned new offset %d", off, Whence(whence).String(), res) - } - } - } - return err -} - -// Create a test file at `path` with random holes -func Create(path string) { - f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600) - if err != nil { - panic(err) - } - defer f.Close() - - rand.Seed(time.Now().UnixNano()) - offsets := make([]int64, 10) - for i := range offsets { - offsets[i] = int64(rand.Int31n(60000)) - } - - buf := []byte("x") - for _, off := range offsets { - _, err = f.WriteAt(buf, off) - if err != nil { - panic(err) - } - } - - // Expand the file to 50000 bytes so we sometimes have a hole on the end - if offsets[len(offsets)-1] < 50000 { - f.Truncate(50000) - } - - f.Sync() -} diff --git a/contrib/findholes/main.go b/contrib/findholes/main.go deleted file mode 100644 index 17597ae..0000000 --- a/contrib/findholes/main.go +++ /dev/null @@ -1,57 +0,0 @@ -// Find and pretty-print holes & data sections of a file. -package main - -import ( - "flag" - "fmt" - "os" - - "github.com/rfjakob/gocryptfs/contrib/findholes/holes" -) - -func main() { - flags := struct { - verify *bool - create *bool - }{} - flags.verify = flag.Bool("verify", false, "Verify results using full file scan") - flags.create = flag.Bool("create", false, "Create test file with random holes") - flag.Parse() - if flag.NArg() != 1 { - fmt.Printf("Usage: findholes FILE\n") - os.Exit(1) - } - - path := flag.Arg(0) - - if *flags.create { - holes.Create(path) - } - - f, err := os.Open(path) - if err != nil { - // os.Open() gives nicer error messages than syscall.Open() - fmt.Println(err) - os.Exit(1) - } - defer f.Close() - - segments, err := holes.Find(int(f.Fd())) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Println(holes.PrettyPrint(segments)) - - if *flags.verify { - err = holes.Verify(int(f.Fd()), segments) - if err != nil { - fmt.Println(err) - os.Exit(1) - } else { - fmt.Println("verify ok") - } - } - -} diff --git a/contrib/getdents-debug/getdents/.gitignore b/contrib/getdents-debug/getdents/.gitignore deleted file mode 100644 index 6dae481..0000000 --- a/contrib/getdents-debug/getdents/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/getdents diff --git a/contrib/getdents-debug/getdents/getdents.go b/contrib/getdents-debug/getdents/getdents.go deleted file mode 100644 index a3cdac4..0000000 --- a/contrib/getdents-debug/getdents/getdents.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Small tool to try to debug unix.Getdents problems on CIFS mounts -( https://github.com/rfjakob/gocryptfs/issues/483 ) - -Example output: - -$ while sleep 1 ; do ./getdents /mnt/synology/public/tmp/g1 ; done -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=3192, err= -unix.Getdents fd3: n=0, err= -total 24072 bytes -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=-1, err=no such file or directory -total 16704 bytes -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=4176, err= -unix.Getdents fd3: n=3192, err= -unix.Getdents fd3: n=0, err= -total 24072 bytes - - -Failure looks like this in strace: - -[pid 189974] getdents64(6, 0xc000105808, 10000) = -1 ENOENT (No such file or directory) -*/ - -package main - -import ( - "flag" - "fmt" - "os" - "time" - - "golang.org/x/sys/unix" -) - -const ( - myName = "getdents" -) - -func main() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s PATH\n", myName) - fmt.Fprintf(os.Stderr, "Run getdents(2) on PATH in a 100ms loop until we hit an error\n") - os.Exit(1) - } - flag.Parse() - if flag.NArg() != 1 { - flag.Usage() - } - path := flag.Arg(0) - - tmp := make([]byte, 10000) - for i := 1; ; i++ { - sum := 0 - fd, err := unix.Open(path, unix.O_RDONLY, 0) - if err != nil { - fmt.Printf("%3d: unix.Open returned err=%v\n", i, err) - os.Exit(1) - } - fmt.Printf("%3d: unix.Getdents: ", i) - for { - n, err := unix.Getdents(fd, tmp) - fmt.Printf("n=%d; ", n) - if n <= 0 { - fmt.Printf("err=%v; total %d bytes\n", err, sum) - if err != nil { - os.Exit(1) - } - break - } - sum += n - } - unix.Close(fd) - time.Sleep(100 * time.Millisecond) - } -} diff --git a/contrib/getdents-debug/getdents_c/.gitignore b/contrib/getdents-debug/getdents_c/.gitignore deleted file mode 100644 index 2f94993..0000000 --- a/contrib/getdents-debug/getdents_c/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/getdents_c diff --git a/contrib/getdents-debug/getdents_c/Makefile b/contrib/getdents-debug/getdents_c/Makefile deleted file mode 100644 index 95e47dc..0000000 --- a/contrib/getdents-debug/getdents_c/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -getdents_c: *.c Makefile - gcc getdents.c -o getdents_c diff --git a/contrib/getdents-debug/getdents_c/getdents.c b/contrib/getdents-debug/getdents_c/getdents.c deleted file mode 100644 index 94f8c97..0000000 --- a/contrib/getdents-debug/getdents_c/getdents.c +++ /dev/null @@ -1,53 +0,0 @@ -// See ../getdents/getdents.go for some info on why -// this exists. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - if(argc != 2) { - printf("Usage: %s PATH\n", argv[0]); - printf("Run getdents(2) on PATH in a 100ms loop\n"); - exit(1); - } - - const char *path = argv[1]; - - for (int i = 1 ; ; i ++ ) { - int fd = open(path, O_RDONLY); - if (fd == -1) { - printf("%3d: open: %s\n", i, strerror(errno)); - if(errno == EINTR) { - continue; - } - exit(1); - } - - char tmp[10000]; - int sum = 0; - printf("%3d: getdents64: ", i); - for ( ; ; ) { - errno = 0; - int n = syscall(SYS_getdents64, fd, tmp, sizeof(tmp)); - printf("n=%d; ", n); - if (n <= 0) { - printf("errno=%d total %d bytes\n", errno, sum); - if (n < 0) { - exit(1); - } - break; - } - sum += n; - } - close(fd); - usleep(100000); - } -} diff --git a/contrib/getdents-debug/readdirnames/.gitignore b/contrib/getdents-debug/readdirnames/.gitignore deleted file mode 100644 index 228dbb3..0000000 --- a/contrib/getdents-debug/readdirnames/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/readdirnames diff --git a/contrib/getdents-debug/readdirnames/readdirnames.go b/contrib/getdents-debug/readdirnames/readdirnames.go deleted file mode 100644 index dc33512..0000000 --- a/contrib/getdents-debug/readdirnames/readdirnames.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Small tool to try to debug unix.Getdents problems on CIFS mounts -( https://github.com/rfjakob/gocryptfs/issues/483 ) - -Example output: - -$ while sleep 1 ; do ./readdirnames /mnt/synology/public/tmp/g1 ; done -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=868, err=readdirent: no such file or directory -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -2020/05/24 23:50:39 os.Open returned err=open /mnt/synology/public/tmp/g1: interrupted system call -Readdirnames: len=1001, err= -Readdirnames: len=1001, err= -*/ - -package main - -import ( - "flag" - "fmt" - "log" - "os" -) - -const ( - myName = "readdirnames" -) - -func main() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s PATH\n", myName) - fmt.Fprintf(os.Stderr, "Run os.File.Readdirnames on PATH\n") - os.Exit(1) - } - flag.Parse() - if flag.NArg() != 1 { - flag.Usage() - } - path := flag.Arg(0) - - f, err := os.Open(path) - if err != nil { - log.Fatalf("os.Open returned err=%v", err) - } - - names, err := f.Readdirnames(0) - fmt.Printf("Readdirnames: len=%d, err=%v\n", len(names), err) -} diff --git a/contrib/gocryptfs-maybe.bash b/contrib/gocryptfs-maybe.bash deleted file mode 100755 index 78a6b2c..0000000 --- a/contrib/gocryptfs-maybe.bash +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# -# Conditionally try to mount a gocryptfs filesystem. If either -# * CIPHERDIR/gocryptfs.conf does not exist OR -# * something is already mounted on MOUNTPOINT -# print a message to stdout (not stderr!) but exit with 0. -# -# This is meant to be called from automated mount systems like pam_mount, -# where you want to avoid error messages if the filesystem does not exist, -# or duplicate mounts if the filesystem has already been mounted. -# -# Note that pam_mount ignores messages on stdout which is why printing -# to stdout is ok. -set -eu -MYNAME=$(basename $0) -if [[ $# -lt 2 || $1 == -* ]]; then - echo "Usage: $MYNAME CIPHERDIR MOUNTPOINT [-o COMMA-SEPARATED-OPTIONS]" >&2 - exit 1 -fi -if [[ ! -f $1/gocryptfs.conf ]]; then - echo "$MYNAME: \"$1\" does not look like a gocryptfs filesystem, ignoring mount request" - exit 0 -fi -if mountpoint "$2" > /dev/null; then - echo "$MYNAME: something is already mounted on \"$2\", ignoring mount request" - exit 0 -fi -exec gocryptfs "$@" diff --git a/contrib/mount-ext4-ramdisk.sh b/contrib/mount-ext4-ramdisk.sh deleted file mode 100755 index 5ff7ef1..0000000 --- a/contrib/mount-ext4-ramdisk.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -ex - -MNT=/mnt/ext4-ramdisk - -if mountpoint $MNT ; then - exit 1 -fi - -IMG=$(mktemp /tmp/ext4-ramdisk-XXX.img) - -# unlink the file when done, space will be -# reclaimed once the fs is unmounted. Also -# cleans up in the error case. -trap 'rm "$IMG"' EXIT - -dd if=/dev/zero of="$IMG" bs=1M count=1030 status=none -mkfs.ext4 -q "$IMG" -mkdir -p "$MNT" -mount "$IMG" "$MNT" -chmod 777 "$MNT" diff --git a/contrib/statfs/.gitignore b/contrib/statfs/.gitignore deleted file mode 100644 index 1ad5baa..0000000 --- a/contrib/statfs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/statfs diff --git a/contrib/statfs/statfs.go b/contrib/statfs/statfs.go deleted file mode 100644 index 163d95a..0000000 --- a/contrib/statfs/statfs.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -const ( - myName = "statfs" -) - -func main() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s PATH\n", myName) - fmt.Fprintf(os.Stderr, "Dump the statfs information for PATH to the console, JSON format.\n") - os.Exit(1) - } - flag.Parse() - if flag.NArg() != 1 { - flag.Usage() - } - path := flag.Arg(0) - var st unix.Statfs_t - err := unix.Statfs(path, &st) - if err != nil { - fmt.Fprintf(os.Stderr, "statfs syscall returned error: %v\n", err) - os.Exit(2) - } - jsn, _ := json.MarshalIndent(st, "", "\t") - fmt.Println(string(jsn)) -} diff --git a/contrib/statvsfstat/.gitignore b/contrib/statvsfstat/.gitignore deleted file mode 100644 index 7f17e5f..0000000 --- a/contrib/statvsfstat/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/statvsfstat diff --git a/contrib/statvsfstat/statvsfstat.go b/contrib/statvsfstat/statvsfstat.go deleted file mode 100644 index e5159ce..0000000 --- a/contrib/statvsfstat/statvsfstat.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - - "golang.org/x/sys/unix" -) - -const ( - myName = "statvsfstat" -) - -func main() { - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: %s PATH\n", myName) - fmt.Fprintf(os.Stderr, "Dump the stat and fstat information for PATH to the console, JSON format.\n") - os.Exit(1) - } - flag.Parse() - if flag.NArg() != 1 { - flag.Usage() - } - path := flag.Arg(0) - - var st unix.Stat_t - err := unix.Stat(path, &st) - if err != nil { - fmt.Fprintf(os.Stderr, "stat syscall returned error: %v\n", err) - os.Exit(4) - } - - fd, err := unix.Open(path, unix.O_RDONLY, 0) - if err != nil { - fmt.Fprintf(os.Stderr, "open syscall returned error: %v\n", err) - os.Exit(3) - } - var fst unix.Stat_t - err = unix.Fstat(fd, &fst) - if err != nil { - fmt.Fprintf(os.Stderr, "fstat syscall returned error: %v\n", err) - os.Exit(2) - } - - fmt.Printf("stat result: %#v\n", st) - fmt.Printf("fstat result: %#v\n", fst) - if st == fst { - fmt.Println("results are identical") - } else { - fmt.Println("results differ") - } -} diff --git a/crossbuild.bash b/crossbuild.bash deleted file mode 100755 index 0904d54..0000000 --- a/crossbuild.bash +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -eu -# -# Build on all supported architectures & operating systems - -cd "$(dirname "$0")" - -export GO111MODULE=on -B="go build -tags without_openssl" - -set -x - -export CGO_ENABLED=0 - -GOOS=linux GOARCH=amd64 $B - -# See https://github.com/golang/go/wiki/GoArm -GOOS=linux GOARCH=arm GOARM=7 $B -GOOS=linux GOARCH=arm64 $B - -# MacOS on Intel -GOOS=darwin GOARCH=amd64 $B - -# MacOS on Apple Silicon M1. -# Go 1.16 added support for the M1 and added ios/arm64, -# so we use this to check if we should attempt a build. -if go tool dist list | grep ios/arm64 ; then - GOOS=darwin GOARCH=arm64 $B -fi - -# The cross-built binary is not useful on the compile host. -rm gocryptfs diff --git a/ctlsock/ctlsock.go b/ctlsock/ctlsock.go deleted file mode 100644 index 1c440b5..0000000 --- a/ctlsock/ctlsock.go +++ /dev/null @@ -1,61 +0,0 @@ -// Package ctlsock is a Go library that can be used to query the -// gocryptfs control socket interface. This interface can be -// activated by passing `-ctlsock /tmp/my.sock` to gocryptfs on the -// command line. -// See gocryptfs-xray for a usage example. -package ctlsock - -import ( - "encoding/json" - "fmt" - "net" - "time" -) - -func (r *ResponseStruct) Error() string { - return fmt.Sprintf("errno %d: %s", r.ErrNo, r.ErrText) -} - -// CtlSock encapsulates a control socket -type CtlSock struct { - Conn net.Conn -} - -// New opens the socket at `socketPath` and stores it in a `CtlSock` object. -func New(socketPath string) (*CtlSock, error) { - conn, err := net.DialTimeout("unix", socketPath, 1*time.Second) - if err != nil { - return nil, err - } - return &CtlSock{Conn: conn}, nil -} - -// Query sends a request to the control socket returns the response. -func (c *CtlSock) Query(req *RequestStruct) (*ResponseStruct, error) { - c.Conn.SetDeadline(time.Now().Add(time.Second)) - msg, err := json.Marshal(req) - if err != nil { - return nil, err - } - _, err = c.Conn.Write(msg) - if err != nil { - return nil, err - } - buf := make([]byte, 5000) - n, err := c.Conn.Read(buf) - if err != nil { - return nil, err - } - buf = buf[:n] - var resp ResponseStruct - json.Unmarshal(buf, &resp) - if resp.ErrNo != 0 { - return nil, &resp - } - return &resp, nil -} - -// Close closes the socket -func (c *CtlSock) Close() { - c.Conn.Close() -} diff --git a/ctlsock/json_abi.go b/ctlsock/json_abi.go deleted file mode 100644 index 7deff08..0000000 --- a/ctlsock/json_abi.go +++ /dev/null @@ -1,26 +0,0 @@ -package ctlsock - -// RequestStruct is sent by a client (encoded as JSON). -// You cannot perform both encryption and decryption in the same request. -type RequestStruct struct { - // EncryptPath is the path that should be encrypted. - EncryptPath string - // DecryptPath is the path that should be decrypted. - DecryptPath string -} - -// ResponseStruct is sent by the server in response to a request -// (encoded as JSON). -type ResponseStruct struct { - // Result is the resulting decrypted or encrypted path. Empty on error. - Result string - // ErrNo is the error number as defined in errno.h. - // 0 means success and -1 means that the error number is not known - // (look at ErrText in this case). - ErrNo int32 - // ErrText is a detailed error message. - ErrText string - // WarnText contains warnings that may have been encountered while - // processing the message. - WarnText string -} diff --git a/daemonize.go b/daemonize.go deleted file mode 100644 index 6fdd1e4..0000000 --- a/daemonize.go +++ /dev/null @@ -1,106 +0,0 @@ -package main - -import ( - "fmt" - "os" - "os/exec" - "os/signal" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// The child sends us USR1 if the mount was successful. Exit with error code -// 0 if we get it. -func exitOnUsr1() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGUSR1) - go func() { - <-c - os.Exit(0) - }() -} - -// forkChild - execute ourselves once again, this time with the "-fg" flag, and -// wait for SIGUSR1 or child exit. -// This is a workaround for the missing true fork function in Go. -func forkChild() int { - name := os.Args[0] - // Use the full path to our executable if we can get if from /proc. - buf := make([]byte, syscallcompat.PATH_MAX) - n, err := syscall.Readlink("/proc/self/exe", buf) - if err == nil { - name = string(buf[:n]) - tlog.Debug.Printf("forkChild: readlink worked: %q", name) - } - newArgs := []string{"-fg", fmt.Sprintf("-notifypid=%d", os.Getpid())} - newArgs = append(newArgs, os.Args[1:]...) - c := exec.Command(name, newArgs...) - c.Stdout = os.Stdout - c.Stderr = os.Stderr - c.Stdin = os.Stdin - exitOnUsr1() - err = c.Start() - if err != nil { - tlog.Fatal.Printf("forkChild: starting %s failed: %v", name, err) - return exitcodes.ForkChild - } - err = c.Wait() - if err != nil { - if exiterr, ok := err.(*exec.ExitError); ok { - if waitstat, ok := exiterr.Sys().(syscall.WaitStatus); ok { - os.Exit(waitstat.ExitStatus()) - } - } - tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v", err) - return exitcodes.ForkChild - } - // The child exited with 0 - let's do the same. - return 0 -} - -// redirectStdFds redirects stderr and stdout to syslog; stdin to /dev/null -func redirectStdFds() { - // Create a pipe pair "pw" -> "pr" and start logger reading from "pr". - // We do it ourselves instead of using StdinPipe() because we need access - // to the fd numbers. - pr, pw, err := os.Pipe() - if err != nil { - tlog.Warn.Printf("redirectStdFds: could not create pipe: %v\n", err) - return - } - tag := fmt.Sprintf("gocryptfs-%d-logger", os.Getpid()) - cmd := exec.Command("logger", "-t", tag) - cmd.Stdin = pr - err = cmd.Start() - if err != nil { - tlog.Warn.Printf("redirectStdFds: could not start logger: %v\n", err) - return - } - // The logger now reads on "pr". We can close it. - pr.Close() - // Redirect stout and stderr to "pw". - err = syscallcompat.Dup3(int(pw.Fd()), 1, 0) - if err != nil { - tlog.Warn.Printf("redirectStdFds: stdout dup error: %v\n", err) - } - syscallcompat.Dup3(int(pw.Fd()), 2, 0) - 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. - pw.Close() - // Redirect stdin to /dev/null - nullFd, err := os.Open("/dev/null") - if err != nil { - tlog.Warn.Printf("redirectStdFds: could not open /dev/null: %v\n", err) - return - } - err = syscallcompat.Dup3(int(nullFd.Fd()), 0, 0) - if err != nil { - tlog.Warn.Printf("redirectStdFds: stdin dup error: %v\n", err) - } - nullFd.Close() -} diff --git a/directory.go b/directory.go new file mode 100644 index 0000000..8ff2659 --- /dev/null +++ b/directory.go @@ -0,0 +1,242 @@ +package main + +import ( + "C" + "fmt" + "io" + "strings" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" + + "./allocator" + "./internal/configfile" + "./internal/cryptocore" + "./internal/nametransform" + "./internal/syscallcompat" +) + +func mkdirWithIv(dirfd int, cName string, mode uint32) error { + // Between the creation of the directory and the creation of gocryptfs.diriv + // the directory is inconsistent. Take the lock to prevent other readers + // from seeing it. + err := unix.Mkdirat(dirfd, cName, mode) + if err != nil { + return err + } + dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscallcompat.O_PATH, 0) + if err == nil { + // Create gocryptfs.diriv + err = nametransform.WriteDirIVAt(dirfd2) + syscall.Close(dirfd2) + } + if err != nil { + // Delete inconsistent directory (missing gocryptfs.diriv!) + syscallcompat.Unlinkat(dirfd, cName, unix.AT_REMOVEDIR) + } + return err +} + +//export gcf_list_dir +func gcf_list_dir(sessionID int, dirName string) (*C.char, *C.int, C.int) { + volume := OpenedVolumes[sessionID] + parentDirFd, cDirName, err := volume.prepareAtSyscall(dirName) + if err != nil { + return nil, nil, 0 + } + defer syscall.Close(parentDirFd) + // Read ciphertext directory + fd, err := syscallcompat.Openat(parentDirFd, cDirName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + return nil, nil, 0 + } + defer syscall.Close(fd) + cipherEntries, err := syscallcompat.Getdents(fd) + if err != nil { + return nil, nil, 0 + } + // Get DirIV (stays nil if PlaintextNames is used) + var cachedIV []byte + if !OpenedVolumes[sessionID].plainTextNames { + // Read the DirIV from disk + cachedIV, err = nametransform.ReadDirIVAt(fd) + if err != nil { + return nil, nil, 0 + } + } + // Decrypted directory entries + var plain strings.Builder + var modes []uint32 + // Filter and decrypt filenames + for i := range cipherEntries { + cName := cipherEntries[i].Name + if dirName == "" && cName == configfile.ConfDefaultName { + // silently ignore "gocryptfs.conf" in the top level dir + continue + } + if OpenedVolumes[sessionID].plainTextNames { + plain.WriteString(cipherEntries[i].Name + "\x00") + modes = append(modes, cipherEntries[i].Mode) + continue + } + if cName == nametransform.DirIVFilename { + // silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled + continue + } + // Handle long file name + isLong := nametransform.NameType(cName) + if isLong == nametransform.LongNameContent { + cNameLong, err := nametransform.ReadLongNameAt(fd, cName) + if err != nil { + continue + } + cName = cNameLong + } else if isLong == nametransform.LongNameFilename { + // ignore "gocryptfs.longname.*.name" + continue + } + name, err := OpenedVolumes[sessionID].nameTransform.DecryptName(cName, cachedIV) + if err != nil { + continue + } + // Override the ciphertext name with the plaintext name but reuse the rest + // of the structure + cipherEntries[i].Name = name + plain.WriteString(cipherEntries[i].Name + "\x00") + modes = append(modes, cipherEntries[i].Mode) + } + p := allocator.Malloc(len(modes)) + for i := 0; i < len(modes); i++ { + offset := C.sizeof_int * uintptr(i) + *(*C.int)(unsafe.Pointer(uintptr(p) + offset)) = (C.int)(modes[i]) + } + return C.CString(plain.String()), (*C.int)(p), (C.int)(len(modes)) +} + +//export gcf_mkdir +func gcf_mkdir(sessionID int, path string, mode uint32) bool { + volume := OpenedVolumes[sessionID] + dirfd, cName, err := volume.prepareAtSyscall(path) + if err != nil { + return false + } + defer syscall.Close(dirfd) + + if volume.plainTextNames { + err = unix.Mkdirat(dirfd, cName, mode) + if err != nil { + return false + } + var ust unix.Stat_t + err = syscallcompat.Fstatat(dirfd, cName, &ust, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + return false + } + } else { + // We need write and execute permissions to create gocryptfs.diriv. + // Also, we need read permissions to open the directory (to avoid + // race-conditions between getting and setting the mode). + origMode := mode + mode := mode | 0700 + + // Handle long file name + if nametransform.IsLongContent(cName) { + // Create ".name" + err = OpenedVolumes[sessionID].nameTransform.WriteLongNameAt(dirfd, cName, path) + if err != nil { + return false + } + + // Create directory + err = mkdirWithIv(dirfd, cName, mode) + if err != nil { + nametransform.DeleteLongNameAt(dirfd, cName) + return false + } + } else { + err = mkdirWithIv(dirfd, cName, mode) + if err != nil { + return false + } + } + + fd, err := syscallcompat.Openat(dirfd, cName, + syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + return false + } + defer syscall.Close(fd) + + var st syscall.Stat_t + err = syscall.Fstat(fd, &st) + if err != nil { + return false + } + + // Fix permissions + if origMode != mode { + // Preserve SGID bit if it was set due to inheritance. + origMode = uint32(st.Mode&^0777) | origMode + syscall.Fchmod(fd, origMode) + } + } + + return true +} + +//export gcf_rmdir +func gcf_rmdir(sessionID int, relPath string) bool { + volume := OpenedVolumes[sessionID] + parentDirFd, cName, err := volume.openBackingDir(relPath) + if err != nil { + return false + } + defer syscall.Close(parentDirFd) + if volume.plainTextNames { + // Unlinkat with AT_REMOVEDIR is equivalent to Rmdir + err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) + return errToBool(err) + } + dirfd, err := syscallcompat.Openat(parentDirFd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) + if err != nil { + return false + } + defer syscall.Close(dirfd) + // Check directory contents + children, err := syscallcompat.Getdents(dirfd) + if err == io.EOF { + // The directory is empty + err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) + return errToBool(err) + } + if err != nil { + return false + } + // If the directory is not empty besides gocryptfs.diriv, do not even + // attempt the dance around gocryptfs.diriv. + if len(children) > 1 { + return false + } + // Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ" + tmpName := fmt.Sprintf("%s.rmdir.%d", nametransform.DirIVFilename, cryptocore.RandUint64()) + err = syscallcompat.Renameat(dirfd, nametransform.DirIVFilename, parentDirFd, tmpName) + if err != nil { + return false + } + // Actual Rmdir + err = syscallcompat.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) + if err != nil { + // This can happen if another file in the directory was created in the + // meantime, undo the rename + syscallcompat.Renameat(parentDirFd, tmpName, dirfd, nametransform.DirIVFilename) + return errToBool(err) + } + // Delete "gocryptfs.diriv.rmdir.XYZ" + syscallcompat.Unlinkat(parentDirFd, tmpName, 0) + // Delete .name file + if nametransform.IsLongContent(cName) { + nametransform.DeleteLongNameAt(parentDirFd, cName) + } + return true +} diff --git a/file.go b/file.go new file mode 100644 index 0000000..0e4a56b --- /dev/null +++ b/file.go @@ -0,0 +1,488 @@ +package main + +import ( + "C" + "bytes" + "io" + "math" + "os" + "syscall" + + "./internal/contentenc" + "./internal/nametransform" + "./internal/syscallcompat" +) + +// mangleOpenFlags is used by Create() and Open() to convert the open flags the user +// wants to the flags we internally use to open the backing file. +// The returned flags always contain O_NOFOLLOW. +func mangleOpenFlags(flags uint32) (newFlags int) { + newFlags = int(flags) + // Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles. + if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY { + newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR + } + // We also cannot open the file in append mode, we need to seek back for RMW + newFlags = newFlags &^ os.O_APPEND + // O_DIRECT accesses must be aligned in both offset and length. Due to our + // crypto header, alignment will be off, even if userspace makes aligned + // accesses. Running xfstests generic/013 on ext4 used to trigger lots of + // EINVAL errors due to missing alignment. Just fall back to buffered IO. + newFlags = newFlags &^ syscallcompat.O_DIRECT + // Create and Open are two separate FUSE operations, so O_CREAT should not + // be part of the open flags. + newFlags = newFlags &^ syscall.O_CREAT + // We always want O_NOFOLLOW to be safe against symlink races + newFlags |= syscall.O_NOFOLLOW + return newFlags +} + +func (volume *Volume) registerFileHandle(file File) int { + handleID := -1 + c := 0 + for handleID == -1 { + _, ok := volume.file_handles[c] + if !ok { + handleID = c + } + c++ + } + volume.file_handles[handleID] = file + return handleID +} + +// readFileID loads the file header from disk and extracts the file ID. +// Returns io.EOF if the file is empty. +func readFileID(fd *os.File) ([]byte, error) { + // We read +1 byte to determine if the file has actual content + // and not only the header. A header-only file will be considered empty. + // This makes File ID poisoning more difficult. + readLen := contentenc.HeaderLen + 1 + buf := make([]byte, readLen) + _, err := fd.ReadAt(buf, 0) + if err != nil { + return nil, err + } + buf = buf[:contentenc.HeaderLen] + h, err := contentenc.ParseHeader(buf) + if err != nil { + return nil, err + } + return h.ID, nil +} + +// createHeader creates a new random header and writes it to disk. +// Returns the new file ID. +// The caller must hold fileIDLock.Lock(). +func createHeader(fd *os.File) (fileID []byte, err error) { + h := contentenc.RandomHeader() + buf := h.Pack() + // Prevent partially written (=corrupt) header by preallocating the space beforehand + err = syscallcompat.EnospcPrealloc(int(fd.Fd()), 0, contentenc.HeaderLen) + if err != nil { + return nil, err + } + // Actually write header + _, err = fd.WriteAt(buf, 0) + if err != nil { + return nil, err + } + return h.ID, err +} + +// doRead - read "length" plaintext bytes from plaintext offset "off" and append +// to "dst". +// Arguments "length" and "off" do not have to be block-aligned. +// +// doRead reads the corresponding ciphertext blocks from disk, decrypts them and +// returns the requested part of the plaintext. +// +// Called by Read() for normal reading, +// by Write() and Truncate() via doWrite() for Read-Modify-Write. +func (volume *Volume) doRead(handleID int, dst []byte, off uint64, length uint64) ([]byte, bool) { + f, ok := volume.file_handles[handleID] + if !ok { + return nil, false + } + fd := f.fd + // Get the file ID, either from the open file table, or from disk. + var fileID []byte + if volume.fileIDs[handleID] != nil { + // Use the cached value in the file table + fileID = volume.fileIDs[handleID] + } else { + // Not cached, we have to read it from disk. + var err error + fileID, err = readFileID(fd) + if err != nil { + return nil, false + } + // Save into the file table + volume.fileIDs[handleID] = fileID + } + + // Read the backing ciphertext in one go + blocks := volume.contentEnc.ExplodePlainRange(off, length) + alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) + // f.fd.ReadAt takes an int64! + if alignedOffset > math.MaxInt64 { + return nil, false + } + skip := blocks[0].Skip + + ciphertext := volume.contentEnc.CReqPool.Get() + ciphertext = ciphertext[:int(alignedLength)] + n, err := fd.ReadAt(ciphertext, int64(alignedOffset)) + if err != nil && err != io.EOF { + return nil, false + } + // The ReadAt came back empty. We can skip all the decryption and return early. + if n == 0 { + volume.contentEnc.CReqPool.Put(ciphertext) + return dst, true + } + // Truncate ciphertext buffer down to actually read bytes + ciphertext = ciphertext[0:n] + + firstBlockNo := blocks[0].BlockNo + + // Decrypt it + plaintext, err := volume.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID) + volume.contentEnc.CReqPool.Put(ciphertext) + if err != nil { + return nil, false + } + + // Crop down to the relevant part + var out []byte + lenHave := len(plaintext) + lenWant := int(skip + length) + if lenHave > lenWant { + out = plaintext[skip:lenWant] + } else if lenHave > int(skip) { + out = plaintext[skip:lenHave] + } + // else: out stays empty, file was smaller than the requested offset + + out = append(dst, out...) + volume.contentEnc.PReqPool.Put(plaintext) + + return out, true +} + +// doWrite - encrypt "data" and write it to plaintext offset "off" +// +// Arguments do not have to be block-aligned, read-modify-write is +// performed internally as necessary +// +// Called by Write() for normal writing, +// and by Truncate() to rewrite the last file block. +// +// Empty writes do nothing and are allowed. +func (volume *Volume) doWrite(handleID int, data []byte, off uint64) (uint32, bool) { + fileWasEmpty := false + // Get the file ID, create a new one if it does not exist yet. + var fileID []byte + + f, ok := volume.file_handles[handleID] + if !ok { + return 0, false + } + fd := f.fd + // The caller has exclusively locked ContentLock, which blocks all other + // readers and writers. No need to take IDLock. + if volume.fileIDs[handleID] != nil { + fileID = volume.fileIDs[handleID] + } else { + // If the file ID is not cached, read it from disk + var err error + fileID, err = readFileID(fd) + // Write a new file header if the file is empty + if err == io.EOF { + fileID, err = createHeader(fd) + fileWasEmpty = true + } + if err != nil { + return 0, false + } + volume.fileIDs[handleID] = fileID + } + // Handle payload data + dataBuf := bytes.NewBuffer(data) + blocks := volume.contentEnc.ExplodePlainRange(off, uint64(len(data))) + toEncrypt := make([][]byte, len(blocks)) + for i, b := range blocks { + blockData := dataBuf.Next(int(b.Length)) + // Incomplete block -> Read-Modify-Write + if b.IsPartial() { + // Read + oldData, success := volume.doRead(handleID, nil, b.BlockPlainOff(), volume.contentEnc.PlainBS()) + if !success { + return 0, false + } + // Modify + blockData = volume.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip)) + } + // Write into the to-encrypt list + toEncrypt[i] = blockData + } + // Encrypt all blocks + ciphertext := volume.contentEnc.EncryptBlocks(toEncrypt, blocks[0].BlockNo, fileID) + // Preallocate so we cannot run out of space in the middle of the write. + // This prevents partially written (=corrupt) blocks. + var err error + cOff := blocks[0].BlockCipherOff() + // f.fd.WriteAt & syscallcompat.EnospcPrealloc take int64 offsets! + if cOff > math.MaxInt64 { + return 0, false + } + err = syscallcompat.EnospcPrealloc(int(fd.Fd()), int64(cOff), int64(len(ciphertext))) + if err != nil { + if fileWasEmpty { + // Kill the file header again + syscall.Ftruncate(int(fd.Fd()), 0) + gcf_close_file(volume.volumeID, handleID) + } + return 0, false + } + // Write + _, err = f.fd.WriteAt(ciphertext, int64(cOff)) + // Return memory to CReqPool + volume.contentEnc.CReqPool.Put(ciphertext) + if err != nil { + return 0, false + } + return uint32(len(data)), true +} + +// Zero-pad the file of size plainSize to the next block boundary. This is a no-op +// if the file is already block-aligned. +func (volume *Volume) zeroPad(handleID int, plainSize uint64) bool { + lastBlockLen := plainSize % volume.contentEnc.PlainBS() + if lastBlockLen == 0 { + // Already block-aligned + return true + } + missing := volume.contentEnc.PlainBS() - lastBlockLen + pad := make([]byte, missing) + _, success := volume.doWrite(handleID, pad, plainSize) + return success +} + +// truncateGrowFile extends a file using seeking or ftruncate performing RMW on +// the first and last block as necessary. New blocks in the middle become +// file holes unless they have been fallocate()'d beforehand. +func (volume *Volume) truncateGrowFile(handleID int, oldPlainSz uint64, newPlainSz uint64) bool { + if newPlainSz <= oldPlainSz { + return false + } + newEOFOffset := newPlainSz - 1 + if oldPlainSz > 0 { + n1 := volume.contentEnc.PlainOffToBlockNo(oldPlainSz - 1) + n2 := volume.contentEnc.PlainOffToBlockNo(newEOFOffset) + // The file is grown within one block, no need to pad anything. + // Write a single zero to the last byte and let doWrite figure out the RMW. + if n1 == n2 { + buf := make([]byte, 1) + _, success := volume.doWrite(handleID, buf, newEOFOffset) + return success + } + } + // The truncate creates at least one new block. + // + // Make sure the old last block is padded to the block boundary. This call + // is a no-op if it is already block-aligned. + success := volume.zeroPad(handleID, oldPlainSz) + if !success { + return false + } + // The new size is block-aligned. In this case we can do everything ourselves + // and avoid the call to doWrite. + if newPlainSz%volume.contentEnc.PlainBS() == 0 { + // The file was empty, so it did not have a header. Create one. + if oldPlainSz == 0 { + id, err := createHeader(volume.file_handles[handleID].fd) + if err != nil { + return false + } + volume.fileIDs[handleID] = id + } + cSz := int64(volume.contentEnc.PlainSizeToCipherSize(newPlainSz)) + err := syscall.Ftruncate(int(volume.file_handles[handleID].fd.Fd()), cSz) + return errToBool(err) + } + // The new size is NOT aligned, so we need to write a partial block. + // Write a single zero to the last byte and let doWrite figure it out. + buf := make([]byte, 1) + _, success = volume.doWrite(handleID, buf, newEOFOffset) + return success +} + +func (volume *Volume) truncate(handleID int, newSize uint64) bool { + fileFD := int(volume.file_handles[handleID].fd.Fd()) + var err error + // Common case first: Truncate to zero + if newSize == 0 { + err = syscall.Ftruncate(fileFD, 0) + return err == nil + } + // We need the old file size to determine if we are growing or shrinking + // the file + oldSize, _, success := gcf_get_attrs(volume.volumeID, volume.file_handles[handleID].path) + if !success { + return false + } + + // File size stays the same - nothing to do + if newSize == oldSize { + return true + } + // File grows + if newSize > oldSize { + return volume.truncateGrowFile(handleID, oldSize, newSize) + } + + // File shrinks + blockNo := volume.contentEnc.PlainOffToBlockNo(newSize) + cipherOff := volume.contentEnc.BlockNoToCipherOff(blockNo) + plainOff := volume.contentEnc.BlockNoToPlainOff(blockNo) + lastBlockLen := newSize - plainOff + var data []byte + if lastBlockLen > 0 { + data, success = volume.doRead(handleID, nil, plainOff, lastBlockLen) + if !success { + return false + } + } + // Truncate down to the last complete block + err = syscall.Ftruncate(fileFD, int64(cipherOff)) + if err != nil { + return false + } + // Append partial block + if lastBlockLen > 0 { + _, success := volume.doWrite(handleID, data, plainOff) + return success + } + return true +} + +//export gcf_open_read_mode +func gcf_open_read_mode(sessionID int, path string) int { + volume := OpenedVolumes[sessionID] + dirfd, cName, err := volume.prepareAtSyscall(path) + if err != nil { + return -1 + } + defer syscall.Close(dirfd) + + // Open backing file + fd, err := syscallcompat.Openat(dirfd, cName, mangleOpenFlags(0), 0) + if err != nil { + return -1 + } + return volume.registerFileHandle(File{os.NewFile(uintptr(fd), cName), path}) +} + +//export gcf_open_write_mode +func gcf_open_write_mode(sessionID int, path string, mode uint32) int { + volume := OpenedVolumes[sessionID] + dirfd, cName, err := volume.prepareAtSyscall(path) + if err != nil { + return -1 + } + defer syscall.Close(dirfd) + + fd := -1 + newFlags := mangleOpenFlags(syscall.O_RDWR) + // Handle long file name + if !volume.plainTextNames && nametransform.IsLongContent(cName) { + // Create ".name" + err = volume.nameTransform.WriteLongNameAt(dirfd, cName, path) + if err != nil { + return -1 + } + // Create content + fd, err = syscallcompat.Openat(dirfd, cName, newFlags|syscall.O_CREAT, mode) + if err != nil { + nametransform.DeleteLongNameAt(dirfd, cName) + } + } else { + // Create content, normal (short) file name + fd, err = syscallcompat.Openat(dirfd, cName, newFlags|syscall.O_CREAT, mode) + } + if err != nil { + return -1 + } + return volume.registerFileHandle(File{os.NewFile(uintptr(fd), cName), path}) +} + +//export gcf_truncate +func gcf_truncate(sessionID int, handleID int, offset uint64) bool { + volume := OpenedVolumes[sessionID] + return volume.truncate(handleID, offset) +} + +//export gcf_read_file +func gcf_read_file(sessionID, handleID int, offset uint64, dst_buff []byte) uint32 { + length := len(dst_buff) + if length > contentenc.MAX_KERNEL_WRITE { + // This would crash us due to our fixed-size buffer pool + return 0 + } + + volume := OpenedVolumes[sessionID] + out, success := volume.doRead(handleID, dst_buff[:0], offset, uint64(length)) + if !success { + return 0 + } else { + return uint32(len(out)) + } +} + +//export gcf_write_file +func gcf_write_file(sessionID, handleID int, offset uint64, data []byte) uint32 { + length := len(data) + if length > contentenc.MAX_KERNEL_WRITE { + // This would crash us due to our fixed-size buffer pool + return 0 + } + + volume := OpenedVolumes[sessionID] + n, _ := volume.doWrite(handleID, data, offset) + return n +} + +//export gcf_close_file +func gcf_close_file(sessionID, handleID int) { + f, ok := OpenedVolumes[sessionID].file_handles[handleID] + if ok { + f.fd.Close() + delete(OpenedVolumes[sessionID].file_handles, handleID) + _, ok := OpenedVolumes[sessionID].fileIDs[handleID] + if ok { + delete(OpenedVolumes[sessionID].fileIDs, handleID) + } + } +} + +//export gcf_remove_file +func gcf_remove_file(sessionID int, path string) bool { + volume := OpenedVolumes[sessionID] + dirfd, cName, err := volume.prepareAtSyscall(path) + if err != nil { + return false + } + defer syscall.Close(dirfd) + + // Delete content + err = syscallcompat.Unlinkat(dirfd, cName, 0) + if err != nil { + return false + } + // Delete ".name" file + if !volume.plainTextNames && nametransform.IsLongContent(cName) { + err = nametransform.DeleteLongNameAt(dirfd, cName) + } + return errToBool(err) +} diff --git a/fsck.go b/fsck.go deleted file mode 100644 index 1f40514..0000000 --- a/fsck.go +++ /dev/null @@ -1,346 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "os/signal" - "path/filepath" - "strings" - "sync" - "syscall" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/fusefrontend" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -type fsckObj struct { - rootNode *fusefrontend.RootNode - // mnt is the mountpoint of the temporary mount - mnt string - // List of corrupt files - corruptList []string - // List of skipped files - skippedList []string - // Protects corruptList - listLock sync.Mutex - // stop a running watchMitigatedCorruptions thread - watchDone chan struct{} - // Inode numbers of hard-linked files (Nlink > 1) that we have already checked - seenInodes map[uint64]struct{} - // abort the running fsck operation? Checked in a few long-running loops. - abort bool -} - -func runsAsRoot() bool { - return syscall.Geteuid() == 0 -} - -func (ck *fsckObj) markCorrupt(path string) { - ck.listLock.Lock() - ck.corruptList = append(ck.corruptList, path) - ck.listLock.Unlock() -} - -func (ck *fsckObj) markSkipped(path string) { - ck.listLock.Lock() - ck.skippedList = append(ck.skippedList, path) - ck.listLock.Unlock() -} - -func (ck *fsckObj) abs(relPath string) (absPath string) { - return filepath.Join(ck.mnt, relPath) -} - -// Watch for mitigated corruptions that occur during OpenDir() -func (ck *fsckObj) watchMitigatedCorruptionsOpenDir(path string) { - for { - select { - case item := <-ck.rootNode.MitigatedCorruptions: - fmt.Printf("fsck: corrupt entry in dir %q: %q\n", path, item) - ck.markCorrupt(filepath.Join(path, item)) - case <-ck.watchDone: - return - } - } -} - -// Recursively check dir for corruption -func (ck *fsckObj) dir(relPath string) { - tlog.Debug.Printf("ck.dir %q\n", relPath) - ck.xattrs(relPath) - // Run OpenDir and catch transparently mitigated corruptions - go ck.watchMitigatedCorruptionsOpenDir(relPath) - f, err := os.Open(ck.abs(relPath)) - ck.watchDone <- struct{}{} - if err != nil { - fmt.Printf("fsck: error opening dir %q: %v\n", relPath, err) - if err == os.ErrPermission && !runsAsRoot() { - ck.markSkipped(relPath) - } else { - ck.markCorrupt(relPath) - } - return - } - go ck.watchMitigatedCorruptionsOpenDir(relPath) - entries, err := f.Readdirnames(0) - ck.watchDone <- struct{}{} - if err != nil { - fmt.Printf("fsck: error reading dir %q: %v\n", relPath, err) - ck.markCorrupt(relPath) - return - } - for _, entry := range entries { - if ck.abort { - return - } - if entry == "." || entry == ".." { - continue - } - nextPath := filepath.Join(relPath, entry) - var st syscall.Stat_t - err := syscall.Lstat(ck.abs(nextPath), &st) - if err != nil { - ck.markCorrupt(filepath.Join(relPath, entry)) - continue - } - filetype := st.Mode & syscall.S_IFMT - //fmt.Printf(" %q %x\n", entry.Name, entry.Mode) - switch filetype { - case syscall.S_IFDIR: - ck.dir(nextPath) - case syscall.S_IFREG: - ck.file(nextPath) - case syscall.S_IFLNK: - ck.symlink(nextPath) - case syscall.S_IFIFO, syscall.S_IFSOCK, syscall.S_IFBLK, syscall.S_IFCHR: - // nothing to check - default: - fmt.Printf("fsck: unhandled file type %x\n", filetype) - } - } -} - -func (ck *fsckObj) symlink(relPath string) { - _, err := os.Readlink(ck.abs(relPath)) - if err != nil { - ck.markCorrupt(relPath) - fmt.Printf("fsck: error reading symlink %q: %v\n", relPath, err) - } -} - -// Watch for mitigated corruptions that occur during Read() -func (ck *fsckObj) watchMitigatedCorruptionsRead(path string) { - for { - select { - case item := <-ck.rootNode.MitigatedCorruptions: - fmt.Printf("fsck: corrupt file %q (inode %s)\n", path, item) - ck.markCorrupt(path) - case <-ck.watchDone: - return - } - } -} - -// Check file for corruption -func (ck *fsckObj) file(relPath string) { - tlog.Debug.Printf("ck.file %q\n", relPath) - var st syscall.Stat_t - err := syscall.Lstat(ck.abs(relPath), &st) - if err != nil { - ck.markCorrupt(relPath) - fmt.Printf("fsck: error stating file %q: %v\n", relPath, err) - return - } - if st.Nlink > 1 { - // Due to hard links, we may have already checked this file. - if _, ok := ck.seenInodes[st.Ino]; ok { - tlog.Debug.Printf("ck.file : skipping %q (inode number %d already seen)\n", relPath, st.Ino) - return - } - ck.seenInodes[st.Ino] = struct{}{} - } - ck.xattrs(relPath) - f, err := os.Open(ck.abs(relPath)) - if err != nil { - fmt.Printf("fsck: error opening file %q: %v\n", relPath, err) - if err == os.ErrPermission && !runsAsRoot() { - ck.markSkipped(relPath) - } else { - ck.markCorrupt(relPath) - } - return - } - defer f.Close() - // 128 kiB of zeros - allZero := make([]byte, fuse.MAX_KERNEL_WRITE) - buf := make([]byte, fuse.MAX_KERNEL_WRITE) - var off int64 - // Read() through the whole file and catch transparently mitigated corruptions - go ck.watchMitigatedCorruptionsRead(relPath) - defer func() { ck.watchDone <- struct{}{} }() - for { - if ck.abort { - return - } - tlog.Debug.Printf("ck.file: read %d bytes from offset %d\n", len(buf), off) - n, err := f.ReadAt(buf, off) - if err != nil && err != io.EOF { - ck.markCorrupt(relPath) - fmt.Printf("fsck: error reading file %q (inum %d): %v\n", relPath, inum(f), err) - return - } - // EOF - if err == io.EOF { - return - } - off += int64(n) - // If we seem to be in the middle of a file hole, try to skip to the next - // data section. - data := buf[:n] - if bytes.Equal(data, allZero) { - tlog.Debug.Printf("ck.file: trying to skip file hole\n") - const SEEK_DATA = 3 - nextOff, err := syscall.Seek(int(f.Fd()), off, SEEK_DATA) - if err == nil { - off = nextOff - } - } - } -} - -// Watch for mitigated corruptions that occur during ListXAttr() -func (ck *fsckObj) watchMitigatedCorruptionsListXAttr(path string) { - for { - select { - case item := <-ck.rootNode.MitigatedCorruptions: - fmt.Printf("fsck: corrupt xattr name on file %q: %q\n", path, item) - ck.markCorrupt(path + " xattr:" + item) - case <-ck.watchDone: - return - } - } -} - -// Check xattrs on file/dir at path -func (ck *fsckObj) xattrs(relPath string) { - // Run ListXAttr() and catch transparently mitigated corruptions - go ck.watchMitigatedCorruptionsListXAttr(relPath) - attrs, err := syscallcompat.Llistxattr(ck.abs(relPath)) - ck.watchDone <- struct{}{} - if err != nil { - fmt.Printf("fsck: error listing xattrs on %q: %v\n", relPath, err) - ck.markCorrupt(relPath) - return - } - // Try to read all xattr values - for _, a := range attrs { - _, err := syscallcompat.Lgetxattr(ck.abs(relPath), a) - if err != nil { - fmt.Printf("fsck: error reading xattr %q from %q: %v\n", a, relPath, err) - if err == syscall.EACCES && !runsAsRoot() { - ck.markSkipped(relPath) - } else { - ck.markCorrupt(relPath) - } - } - } -} - -// entrypoint from main() -func fsck(args *argContainer) (exitcode int) { - if args.reverse { - tlog.Fatal.Printf("Running -fsck with -reverse is not supported") - os.Exit(exitcodes.Usage) - } - args.allow_other = false - args.ro = true - var err error - args.mountpoint, err = ioutil.TempDir("", "gocryptfs.fsck.") - if err != nil { - tlog.Fatal.Printf("fsck: TmpDir: %v", err) - os.Exit(exitcodes.MountPoint) - } - pfs, wipeKeys := initFuseFrontend(args) - rn := pfs.(*fusefrontend.RootNode) - rn.MitigatedCorruptions = make(chan string) - ck := fsckObj{ - mnt: args.mountpoint, - rootNode: rn, - watchDone: make(chan struct{}), - seenInodes: make(map[uint64]struct{}), - } - if args.quiet { - // go-fuse throws a lot of these: - // writer: Write/Writev failed, err: 2=no such file or directory. opcode: INTERRUPT - // This is ugly and causes failures in xfstests. Hide them away in syslog. - tlog.SwitchLoggerToSyslog() - } - // Mount - srv := initGoFuse(pfs, args) - // Handle SIGINT & SIGTERM - ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt) - signal.Notify(ch, syscall.SIGTERM) - go func() { - <-ch - ck.abort = true - }() - defer func() { - err = srv.Unmount() - if err != nil { - tlog.Warn.Printf("failed to unmount %q: %v", ck.mnt, err) - } else { - if err := syscall.Rmdir(ck.mnt); err != nil { - tlog.Warn.Printf("cleaning up %q failed: %v", ck.mnt, err) - } - } - }() - // Recursively check the root dir - ck.dir("") - // Report results - wipeKeys() - if ck.abort { - tlog.Info.Printf("fsck: aborted") - return exitcodes.Other - } - if len(ck.corruptList) == 0 && len(ck.skippedList) == 0 { - tlog.Info.Printf("fsck summary: no problems found\n") - return 0 - } - if len(ck.skippedList) > 0 { - tlog.Warn.Printf("fsck: re-run this program as root to check all files!\n") - } - fmt.Printf("fsck summary: %d corrupt files, %d files skipped\n", len(ck.corruptList), len(ck.skippedList)) - return exitcodes.FsckErrors -} - -type sortableDirEntries []fuse.DirEntry - -func (s sortableDirEntries) Len() int { - return len(s) -} - -func (s sortableDirEntries) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s sortableDirEntries) Less(i, j int) bool { - return strings.Compare(s[i].Name, s[j].Name) < 0 -} - -func inum(f *os.File) uint64 { - var st syscall.Stat_t - err := syscall.Fstat(int(f.Fd()), &st) - if err != nil { - tlog.Warn.Printf("inum: fstat failed: %v", err) - return 0 - } - return st.Ino -} diff --git a/go.mod b/go.mod deleted file mode 100644 index e80a4bc..0000000 --- a/go.mod +++ /dev/null @@ -1,19 +0,0 @@ -module github.com/rfjakob/gocryptfs - -go 1.13 - -require ( - github.com/hanwen/go-fuse/v2 v2.1.1-0.20210508151621-62c5aa1919a7 - github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115 - github.com/jacobsa/oglematchers v0.0.0-20150720000706-141901ea67cd // indirect - github.com/jacobsa/oglemock v0.0.0-20150831005832-e94d794d06ff // indirect - github.com/jacobsa/ogletest v0.0.0-20170503003838-80d50a735a11 // indirect - github.com/jacobsa/reqtrace v0.0.0-20150505043853-245c9e0234cb // indirect - github.com/pkg/xattr v0.4.1 - github.com/rfjakob/eme v1.1.1 - github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 - github.com/stretchr/testify v1.5.1 // indirect - golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 - golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect - golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 8a59eb2..0000000 --- a/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -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 v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= -github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= -github.com/hanwen/go-fuse/v2 v2.1.1-0.20210508151621-62c5aa1919a7 h1:9K/MBPvPptwwCYIw8gBi/Sup5Uw8UeYlyKBxxzl931Y= -github.com/hanwen/go-fuse/v2 v2.1.1-0.20210508151621-62c5aa1919a7/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc= -github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115 h1:YuDUUFNM21CAbyPOpOP8BicaTD/0klJEKt5p8yuw+uY= -github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115/go.mod h1:LadVJg0XuawGk+8L1rYnIED8451UyNxEMdTWCEt5kmU= -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/pkg/xattr v0.4.1 h1:dhclzL6EqOXNaPDWqoeb9tIxATfBSmjqL0b4DpSjwRw= -github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs= -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.1 h1:t+CgvcOn+eDvj2xdglxsSnkgg8LM8jwdxnV7OnsrTn0= -github.com/rfjakob/eme v1.1.1/go.mod h1:U2bmx0hDj8EyDdcxmD5t3XHDnBFnyNNc22n1R4008eM= -github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0= -github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= -golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181021155630-eda9bb28ed51/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w= -golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/gocryptfs-xray/.gitignore b/gocryptfs-xray/.gitignore deleted file mode 100644 index bad094d..0000000 --- a/gocryptfs-xray/.gitignore +++ /dev/null @@ -1 +0,0 @@ -gocryptfs-xray diff --git a/gocryptfs-xray/paths_ctlsock.go b/gocryptfs-xray/paths_ctlsock.go deleted file mode 100644 index c489f0e..0000000 --- a/gocryptfs-xray/paths_ctlsock.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "os" - - "github.com/rfjakob/gocryptfs/ctlsock" -) - -func decryptPaths(socketPath string, sep0 bool) { - var req ctlsock.RequestStruct - transformPaths(socketPath, &req, &req.DecryptPath, sep0) -} - -func encryptPaths(socketPath string, sep0 bool) { - var req ctlsock.RequestStruct - transformPaths(socketPath, &req, &req.EncryptPath, sep0) -} - -func transformPaths(socketPath string, req *ctlsock.RequestStruct, in *string, sep0 bool) { - errorCount := 0 - c, err := ctlsock.New(socketPath) - if err != nil { - fmt.Printf("fatal: %v\n", err) - os.Exit(1) - } - line := 1 - var separator byte = '\n' - if sep0 { - separator = '\000' - } - r := bufio.NewReader(os.Stdin) - for eof := false; eof == false; line++ { - val, err := r.ReadBytes(separator) - if len(val) == 0 { - break - } - if err != nil { - // break the loop after we have processed the last val - eof = true - } else { - // drop trailing separator - val = val[:len(val)-1] - } - *in = string(val) - resp, err := c.Query(req) - if err != nil { - fmt.Fprintf(os.Stderr, "error at input line %d %q: %v\n", line, *in, err) - errorCount++ - continue - } - if resp.WarnText != "" { - fmt.Fprintf(os.Stderr, "warning at input line %d %q: %v\n", line, *in, resp.WarnText) - } - fmt.Printf("%s%c", resp.Result, separator) - } - if errorCount == 0 { - os.Exit(0) - } - os.Exit(1) -} diff --git a/gocryptfs-xray/xray_main.go b/gocryptfs-xray/xray_main.go deleted file mode 100644 index b99268b..0000000 --- a/gocryptfs-xray/xray_main.go +++ /dev/null @@ -1,205 +0,0 @@ -package main - -import ( - "encoding/hex" - "flag" - "fmt" - "io" - "os" - "runtime" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/fido2" - "github.com/rfjakob/gocryptfs/internal/readpassword" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// GitVersion is the gocryptfs version according to git, set by build.bash -var GitVersion = "[GitVersion not set - please compile using ./build.bash]" - -// GitVersionFuse is the go-fuse library version, set by build.bash -var GitVersionFuse = "[GitVersionFuse not set - please compile using ./build.bash]" - -// BuildDate is a date string like "2017-09-06", set by build.bash -var BuildDate = "0000-00-00" - -const ( - ivLen = contentenc.DefaultIVBits / 8 - authTagLen = cryptocore.AuthTagLen - blockSize = contentenc.DefaultBS + ivLen + cryptocore.AuthTagLen - myName = "gocryptfs-xray" -) - -func errExit(err error) { - fmt.Println(err) - os.Exit(1) -} - -func prettyPrintHeader(h *contentenc.FileHeader, aessiv bool) { - id := hex.EncodeToString(h.ID) - msg := "Header: Version: %d, Id: %s" - if aessiv { - msg += ", assuming AES-SIV mode" - } else { - msg += ", assuming AES-GCM mode" - } - fmt.Printf(msg+"\n", h.Version, id) -} - -// printVersion prints a version string like this: -// gocryptfs v1.7-32-gcf99cfd; go-fuse v1.0.0-174-g22a9cb9; 2019-05-12 go1.12 linux/amd64 -func printVersion() { - built := fmt.Sprintf("%s %s", BuildDate, runtime.Version()) - fmt.Printf("%s %s; %s %s/%s\n", - myName, GitVersion, built, - runtime.GOOS, runtime.GOARCH) -} - -func usage() { - printVersion() - fmt.Printf("\n") - fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] FILE\n"+ - "\n"+ - "Options:\n", myName) - flag.PrintDefaults() - fmt.Fprintf(os.Stderr, "\n"+ - "Examples:\n"+ - " gocryptfs-xray myfs/mCXnISiv7nEmyc0glGuhTQ\n"+ - " gocryptfs-xray -dumpmasterkey myfs/gocryptfs.conf\n"+ - " gocryptfs-xray -encrypt-paths myfs.sock\n") -} - -// sum counts the number of true values -func sum(x ...*bool) (s int) { - for _, v := range x { - if *v { - s++ - } - } - return s -} - -func main() { - var args struct { - dumpmasterkey *bool - decryptPaths *bool - encryptPaths *bool - aessiv *bool - sep0 *bool - fido2 *string - version *bool - } - args.dumpmasterkey = flag.Bool("dumpmasterkey", false, "Decrypt and dump the master key") - args.decryptPaths = flag.Bool("decrypt-paths", false, "Decrypt file paths using gocryptfs control socket") - args.encryptPaths = flag.Bool("encrypt-paths", false, "Encrypt file paths using gocryptfs control socket") - args.sep0 = flag.Bool("0", false, "Use \\0 instead of \\n as separator") - args.aessiv = flag.Bool("aessiv", false, "Assume AES-SIV mode instead of AES-GCM") - args.fido2 = flag.String("fido2", "", "Protect the masterkey using a FIDO2 token instead of a password") - args.version = flag.Bool("version", false, "Print version information") - - flag.Usage = usage - flag.Parse() - - if *args.version { - printVersion() - os.Exit(0) - } - - s := sum(args.dumpmasterkey, args.decryptPaths, args.encryptPaths) - if s > 1 { - fmt.Printf("fatal: %d operations were requested\n", s) - os.Exit(1) - } - if flag.NArg() != 1 { - usage() - os.Exit(1) - } - fn := flag.Arg(0) - if *args.decryptPaths { - decryptPaths(fn, *args.sep0) - } - if *args.encryptPaths { - encryptPaths(fn, *args.sep0) - } - fd, err := os.Open(fn) - if err != nil { - errExit(err) - } - defer fd.Close() - if *args.dumpmasterkey { - dumpMasterKey(fn, *args.fido2) - } else { - inspectCiphertext(fd, *args.aessiv) - } -} - -func dumpMasterKey(fn string, fido2Path string) { - tlog.Info.Enabled = false - cf, err := configfile.Load(fn) - if err != nil { - fmt.Fprintln(os.Stderr, err) - exitcodes.Exit(err) - } - var pw []byte - if cf.IsFeatureFlagSet(configfile.FlagFIDO2) { - if fido2Path == "" { - 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) - } else { - pw = readpassword.Once(nil, nil, "") - } - masterkey, err := cf.DecryptMasterKey(pw) - fmt.Println(hex.EncodeToString(masterkey)) - for i := range pw { - pw[i] = 0 - } -} - -func inspectCiphertext(fd *os.File, aessiv bool) { - headerBytes := make([]byte, contentenc.HeaderLen) - n, err := fd.ReadAt(headerBytes, 0) - if err == io.EOF && n == 0 { - 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) - os.Exit(1) - } else if err != nil { - errExit(err) - } - header, err := contentenc.ParseHeader(headerBytes) - if err != nil { - errExit(err) - } - prettyPrintHeader(header, aessiv) - var i int64 - buf := make([]byte, blockSize) - for i = 0; ; i++ { - off := contentenc.HeaderLen + i*blockSize - n, err := fd.ReadAt(buf, off) - if err != nil && err != io.EOF { - errExit(err) - } - if n == 0 && err == io.EOF { - break - } - // A block contains at least the IV, the Auth Tag and 1 data byte - if n < ivLen+authTagLen+1 { - errExit(fmt.Errorf("corrupt block: truncated data, len=%d", n)) - } - data := buf[:n] - // Parse block data - iv := data[:ivLen] - tag := data[len(data)-authTagLen:] - if aessiv { - tag = data[ivLen : ivLen+authTagLen] - } - fmt.Printf("Block %2d: IV: %s, Tag: %s, Offset: %5d Len: %d\n", - i, hex.EncodeToString(iv), hex.EncodeToString(tag), off, len(data)) - } -} diff --git a/gocryptfs-xray/xray_tests/aesgcm_fs.masterkey.txt b/gocryptfs-xray/xray_tests/aesgcm_fs.masterkey.txt deleted file mode 100644 index 8c9cefb..0000000 --- a/gocryptfs-xray/xray_tests/aesgcm_fs.masterkey.txt +++ /dev/null @@ -1,4 +0,0 @@ -Your master key is: - - b4d8b25c-324dd6ea-a328c990-6e8a2a3c- - 6038552a-042ced43-26cfff21-0c62957a diff --git a/gocryptfs-xray/xray_tests/aesgcm_fs.xray.txt b/gocryptfs-xray/xray_tests/aesgcm_fs.xray.txt deleted file mode 100644 index c403b75..0000000 --- a/gocryptfs-xray/xray_tests/aesgcm_fs.xray.txt +++ /dev/null @@ -1,3 +0,0 @@ -Header: Version: 2, Id: 8932adf303fe0289679d47fa84d2b241, assuming AES-GCM mode -Block 0: IV: c8536b4bfd92f5dc3c1e2ac29f116d4a, Tag: 22b20422749b2f4bba67ec7d3bb1ac34, Offset: 18 Len: 4128 -Block 1: IV: 2de68f4965779bb137ef2b3c20453556, Tag: 3e8758d6872234b1fffab2504e623467, Offset: 4146 Len: 936 diff --git a/gocryptfs-xray/xray_tests/aesgcm_fs/VnvoeSetPaOFjZDaZAh0lA b/gocryptfs-xray/xray_tests/aesgcm_fs/VnvoeSetPaOFjZDaZAh0lA deleted file mode 100644 index 420c1c5b5521ee6825ae60af33ae91dbc78ecced..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5082 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 diff --git a/gocryptfs-xray/xray_tests/xray_test.go b/gocryptfs-xray/xray_tests/xray_test.go deleted file mode 100644 index 790d2db..0000000 --- a/gocryptfs-xray/xray_tests/xray_test.go +++ /dev/null @@ -1,110 +0,0 @@ -package xray_tests - -import ( - "bytes" - "fmt" - "io/ioutil" - "os/exec" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func TestAesgcmXray(t *testing.T) { - expected, err := ioutil.ReadFile("aesgcm_fs.xray.txt") - if err != nil { - t.Fatal(err) - } - cmd := exec.Command("../gocryptfs-xray", "aesgcm_fs/VnvoeSetPaOFjZDaZAh0lA") - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - if bytes.Compare(out, expected) != 0 { - t.Errorf("Unexpected output") - fmt.Printf("expected:\n%s", string(expected)) - fmt.Printf("have:\n%s", string(out)) - } -} - -func TestAessivXray(t *testing.T) { - expected, err := ioutil.ReadFile("aessiv_fs.xray.txt") - if err != nil { - t.Fatal(err) - } - cmd := exec.Command("../gocryptfs-xray", "-aessiv", "aessiv_fs/klepPXQJIaEDaIx-yurAqQ") - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - if bytes.Compare(out, expected) != 0 { - t.Errorf("Unexpected output") - fmt.Printf("expected:\n%s", string(expected)) - fmt.Printf("have:\n%s", string(out)) - } -} - -func TestDumpmasterkey(t *testing.T) { - expected := "b4d8b25c324dd6eaa328c9906e8a2a3c6038552a042ced4326cfff210c62957a\n" - cmd := exec.Command("../gocryptfs-xray", "-dumpmasterkey", "aesgcm_fs/gocryptfs.conf") - // Password = "test" - cmd.Stdin = bytes.NewBuffer([]byte("test")) - out1, err := cmd.CombinedOutput() - if err != nil { - t.Fatal(err) - } - out := string(out1) - if out != expected { - t.Errorf("Wrong output") - fmt.Printf("expected: %s\n", expected) - fmt.Printf("have: %s\n", out) - } -} - -func TestEncryptPaths(t *testing.T) { - cDir := test_helpers.InitFS(t) - pDir := cDir + ".mnt" - sock := cDir + ".sock" - test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") - defer test_helpers.UnmountPanic(pDir) - - testCases := []struct { - in []string - sep0 bool - }{ - { - []string{ - "test1", - "test1\n", - "test1\ntest2", - "test1\ntest2\n", - }, - false, - }, - { - []string{ - "test1", - "test1\000", - "test1\000test2", - "test1\000test2\000", - }, - true, - }, - } - - for _, tc := range testCases { - for _, in := range tc.in { - sepArg := "-0=false" - if tc.sep0 { - sepArg = "-0=true" - } - cmd := exec.Command("../gocryptfs-xray", "-encrypt-paths", sepArg, sock) - cmd.Stdin = bytes.NewBuffer([]byte(in)) - out, err := cmd.CombinedOutput() - t.Logf("%q", string(out)) - if err != nil { - t.Fatal(err) - } - } - } -} diff --git a/golint.bash b/golint.bash deleted file mode 100755 index d6fe729..0000000 --- a/golint.bash +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -u - -OUTPUT=$( - golint ./... | \ - grep -v "don't use an underscore in package name" | \ - grep -v "don't use ALL_CAPS in Go names; use CamelCase" | - grep -v "don't use underscores in Go names" -) - -# No output --> all good -if [[ -z $OUTPUT ]] ; then - exit 0 -fi - -echo "golint.bash:" -echo "$OUTPUT" -exit 1 diff --git a/help.go b/help.go deleted file mode 100644 index 5cd35ac..0000000 --- a/help.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -const tUsage = "" + - "Usage: " + tlog.ProgramName + " -init|-passwd|-info [OPTIONS] CIPHERDIR\n" + - " or " + tlog.ProgramName + " [OPTIONS] CIPHERDIR MOUNTPOINT\n" - -// helpShort is what gets displayed when passed "-h" or on syntax error. -func helpShort() { - printVersion() - fmt.Printf("\n") - fmt.Printf(tUsage) - fmt.Printf(` -Common Options (use -hh to show all): - -aessiv Use AES-SIV encryption (with -init) - -allow_other Allow other users to access the mount - -i, -idle Unmount automatically after specified idle duration - -config Custom path to config file - -ctlsock Create control socket at location - -extpass Call external program to prompt for the password - -fg Stay in the foreground - -fsck Check filesystem integrity - -fusedebug Debug FUSE calls - -h, -help This short help text - -hh Long help text with all options - -init Initialize encrypted directory - -info Display information about encrypted directory - -masterkey Mount with explicit master key instead of password - -nonempty Allow mounting over non-empty directory - -nosyslog Do not redirect log messages to syslog - -passfile Read password from plain text file(s) - -passwd Change password - -plaintextnames Do not encrypt file names (with -init) - -q, -quiet Silence informational messages - -reverse Enable reverse mode - -ro Mount read-only - -speed Run crypto speed test - -version Print version information - -- Stop option parsing -`) -} - -// helpLong gets only displayed on "-hh" -func helpLong() { - printVersion() - fmt.Printf("\n") - fmt.Printf(tUsage) - fmt.Printf("\nOptions:\n") - flagSet.PrintDefaults() - fmt.Printf(" --\n Stop option parsing\n") -} diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..3e1169b --- /dev/null +++ b/helpers.go @@ -0,0 +1,183 @@ +package main + +import ( + "path/filepath" + "strings" + "syscall" + + "./internal/configfile" + "./internal/nametransform" + "./internal/syscallcompat" +) + +// isFiltered - check if plaintext "path" should be forbidden +// +// Prevents name clashes with internal files when file names are not encrypted +func (volume *Volume) isFiltered(path string) bool { + if !volume.plainTextNames { + return false + } + // gocryptfs.conf in the root directory is forbidden + if path == configfile.ConfDefaultName { + return true + } + // Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames + // are exclusive + return false +} + +func (volume *Volume) openBackingDir(relPath string) (dirfd int, cName string, err error) { + dirRelPath := nametransform.Dir(relPath) + // With PlaintextNames, we don't need to read DirIVs. Easy. + if volume.plainTextNames { + dirfd, err = syscallcompat.OpenDirNofollow(volume.rootCipherDir, dirRelPath) + if err != nil { + return -1, "", err + } + // If relPath is empty, cName is ".". + cName = filepath.Base(relPath) + return dirfd, cName, nil + } + // Open cipherdir (following symlinks) + dirfd, err = syscallcompat.Open(volume.rootCipherDir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) + if err != nil { + return -1, "", err + } + // If relPath is empty, cName is ".". + if relPath == "" { + return dirfd, ".", nil + } + // Walk the directory tree + parts := strings.Split(relPath, "/") + for i, name := range parts { + iv, err := nametransform.ReadDirIVAt(dirfd) + if err != nil { + syscall.Close(dirfd) + return -1, "", err + } + cName, err = volume.nameTransform.EncryptAndHashName(name, iv) + if err != nil { + syscall.Close(dirfd) + return -1, "", err + } + // Last part? We are done. + if i == len(parts)-1 { + break + } + // Not the last part? Descend into next directory. + dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) + syscall.Close(dirfd) + if err != nil { + return -1, "", err + } + dirfd = dirfd2 + } + return dirfd, cName, nil +} + +func (volume *Volume) prepareAtSyscall(path string) (dirfd int, cName string, err error) { + // root node itself is special + if path == "" { + return volume.openBackingDir(path) + } + + // Cache lookup + // TODO make it work for plaintextnames as well? + if !volume.plainTextNames { + directory, ok := volume.dirCache[path] + if ok { + if directory.fd > 0 { + cName, err := volume.nameTransform.EncryptAndHashName(filepath.Base(path), directory.iv) + if err != nil { + return -1, "", err + } + dirfd, err = syscall.Dup(directory.fd) + if err != nil { + return -1, "", err + } + return dirfd, cName, nil + } + } + } + + // Slowpath + if volume.isFiltered(path) { + return -1, "", syscall.EPERM + } + dirfd, cName, err = volume.openBackingDir(path) + if err != nil { + return -1, "", err + } + + // Cache store + if !volume.plainTextNames { + // TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work? + iv, err := nametransform.ReadDirIVAt(dirfd) + if err != nil { + syscall.Close(dirfd) + return -1, "", err + } + dirfdDup, err := syscall.Dup(dirfd) + if err == nil { + var pathCopy strings.Builder + pathCopy.WriteString(path) + volume.dirCache[pathCopy.String()] = Directory{dirfdDup, iv} + } + } + return +} + +// decryptSymlinkTarget: "cData64" is base64-decoded and decrypted +// like file contents (GCM). +// The empty string decrypts to the empty string. +// +// This function does not do any I/O and is hence symlink-safe. +func (volume *Volume) decryptSymlinkTarget(cData64 string) (string, error) { + if cData64 == "" { + return "", nil + } + cData, err := volume.nameTransform.B64DecodeString(cData64) + if err != nil { + return "", err + } + data, err := volume.contentEnc.DecryptBlock([]byte(cData), 0, nil) + if err != nil { + return "", err + } + return string(data), nil +} + +// readlink reads and decrypts a symlink. Used by Readlink, Getattr, Lookup. +func (volume *Volume) readlink(dirfd int, cName string) []byte { + cTarget, err := syscallcompat.Readlinkat(dirfd, cName) + if err != nil { + return nil + } + if volume.plainTextNames { + return []byte(cTarget) + } + // Symlinks are encrypted like file contents (GCM) and base64-encoded + target, err := volume.decryptSymlinkTarget(cTarget) + if err != nil { + return nil + } + return []byte(target) +} + +func isRegular(mode uint32) bool { return (mode & syscall.S_IFMT) == syscall.S_IFREG } + +func isSymlink(mode uint32) bool { return (mode & syscall.S_IFMT) == syscall.S_IFLNK } + +// translateSize translates the ciphertext size in `out` into plaintext size. +// Handles regular files & symlinks (and finds out what is what by looking at +// `out.Mode`). +func (volume *Volume) translateSize(dirfd int, cName string, st *syscall.Stat_t) uint64 { + var size uint64 + if isRegular(st.Mode) { + size = volume.contentEnc.CipherSizeToPlainSize(uint64(st.Size)) + } else if isSymlink(st.Mode) { + target := volume.readlink(dirfd, cName) + size = uint64(len(target)) + } + return size +} diff --git a/info.go b/info.go deleted file mode 100644 index bbc5a10..0000000 --- a/info.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "strings" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// info pretty-prints the contents of the config file at "filename" for human -// consumption, stripping out sensitive data. -// This is called when you pass the "-info" option. -func info(filename string) { - // Read from disk - js, err := ioutil.ReadFile(filename) - if err != nil { - tlog.Fatal.Printf("Reading config file failed: %v", err) - os.Exit(exitcodes.LoadConf) - } - // Unmarshal - var cf configfile.ConfFile - err = json.Unmarshal(js, &cf) - if err != nil { - tlog.Fatal.Printf("Failed to unmarshal config file") - os.Exit(exitcodes.LoadConf) - } - if cf.Version != contentenc.CurrentVersion { - tlog.Fatal.Printf("Unsupported on-disk format %d", cf.Version) - os.Exit(exitcodes.LoadConf) - } - // Pretty-print - fmt.Printf("Creator: %s\n", cf.Creator) - fmt.Printf("FeatureFlags: %s\n", strings.Join(cf.FeatureFlags, " ")) - fmt.Printf("EncryptedKey: %dB\n", len(cf.EncryptedKey)) - s := cf.ScryptObject - fmt.Printf("ScryptObject: Salt=%dB N=%d R=%d P=%d KeyLen=%d\n", - len(s.Salt), s.N, s.R, s.P, s.KeyLen) -} diff --git a/init_dir.go b/init_dir.go deleted file mode 100644 index 68268a0..0000000 --- a/init_dir.go +++ /dev/null @@ -1,134 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strings" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/fido2" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/readpassword" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// isEmptyDir checks if "dir" exists and is an empty directory. -// Returns an *os.PathError if Stat() on the path fails. -func isEmptyDir(dir string) error { - err := isDir(dir) - if err != nil { - return err - } - entries, err := ioutil.ReadDir(dir) - if err != nil { - return err - } - if len(entries) == 0 { - return nil - } - return fmt.Errorf("directory %s not empty", dir) -} - -// isDir checks if "dir" exists and is a directory. -func isDir(dir string) error { - fi, err := os.Stat(dir) - if err != nil { - return err - } - if !fi.IsDir() { - return fmt.Errorf("%s is not a directory", dir) - } - return nil -} - -// initDir handles "gocryptfs -init". It prepares a directory for use as a -// gocryptfs storage directory. -// In forward mode, this means creating the gocryptfs.conf and gocryptfs.diriv -// files in an empty directory. -// In reverse mode, we create .gocryptfs.reverse.conf and the directory does -// not need to be empty. -func initDir(args *argContainer) { - var err error - if args.reverse { - _, err = os.Stat(args.config) - if err == nil { - tlog.Fatal.Printf("Config file %q already exists", args.config) - os.Exit(exitcodes.Init) - } - } else { - err = isEmptyDir(args.cipherdir) - if err != nil { - tlog.Fatal.Printf("Invalid cipherdir: %v", err) - os.Exit(exitcodes.CipherDir) - } - } - // Choose password for config file - if args.extpass.Empty() && args.fido2 == "" { - tlog.Info.Printf("Choose a password for protecting your files.") - } - { - var password []byte - var fido2CredentialID, fido2HmacSalt []byte - if args.fido2 != "" { - fido2CredentialID = fido2.Register(args.fido2, filepath.Base(args.cipherdir)) - fido2HmacSalt = cryptocore.RandBytes(32) - password = fido2.Secret(args.fido2, fido2CredentialID, fido2HmacSalt) - } else { - // normal password entry - password = readpassword.Twice([]string(args.extpass), []string(args.passfile)) - fido2CredentialID = nil - fido2HmacSalt = nil - } - creator := tlog.ProgramName + " " + GitVersion - err = configfile.Create(args.config, password, args.plaintextnames, - args.scryptn, creator, args.aessiv, args.devrandom, fido2CredentialID, fido2HmacSalt) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.WriteConf) - } - for i := range password { - password[i] = 0 - } - // password runs out of scope here - } - // Forward mode with filename encryption enabled needs a gocryptfs.diriv file - // in the root dir - if !args.plaintextnames && !args.reverse { - // Open cipherdir (following symlinks) - dirfd, err := syscall.Open(args.cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) - if err == nil { - err = nametransform.WriteDirIVAt(dirfd) - syscall.Close(dirfd) - } - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Init) - } - } - mountArgs := "" - fsName := "gocryptfs" - if args.reverse { - mountArgs = " -reverse" - fsName = "gocryptfs-reverse" - } - tlog.Info.Printf(tlog.ColorGreen+"The %s filesystem has been created successfully."+tlog.ColorReset, - fsName) - wd, _ := os.Getwd() - friendlyPath, _ := filepath.Rel(wd, args.cipherdir) - if strings.HasPrefix(friendlyPath, "../") { - // A relative path that starts with "../" is pretty unfriendly, just - // keep the absolute path. - friendlyPath = args.cipherdir - } - if strings.Contains(friendlyPath, " ") { - friendlyPath = "\"" + friendlyPath + "\"" - } - tlog.Info.Printf(tlog.ColorGrey+"You can now mount it using: %s%s %s MOUNTPOINT"+tlog.ColorReset, - tlog.ProgramName, mountArgs, friendlyPath) -} diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index e4921f7..089af07 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -12,10 +12,9 @@ import ( "os" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" + "../contentenc" + "../cryptocore" + "../exitcodes" ) const ( @@ -113,11 +112,10 @@ func Create(filename string, password []byte, plaintextNames bool, } else { key = cryptocore.RandBytes(cryptocore.KeyLen) } - tlog.PrintMasterkeyReminder(key) // Encrypt it using the password // This sets ScryptObject and EncryptedKey // Note: this looks at the FeatureFlags, so call it AFTER setting them. - cf.EncryptKey(key, password, logN) + cf.EncryptKey(key, password, logN, false) for i := range key { key[i] = 0 } @@ -147,7 +145,7 @@ func LoadAndDecrypt(filename string, password []byte) ([]byte, *ConfFile, error) } // Decrypt the masterkey using the password - key, err := cf.DecryptMasterKey(password) + key, _, err := cf.DecryptMasterKey(password, false) if err != nil { return nil, nil, err } @@ -172,7 +170,6 @@ func Load(filename string) (*ConfFile, error) { // Unmarshal err = json.Unmarshal(js, &cf) if err != nil { - tlog.Warn.Printf("Failed to unmarshal config file") return nil, err } @@ -202,17 +199,6 @@ func Load(filename string) (*ConfFile, error) { } } if deprecatedFs { - fmt.Fprintf(os.Stderr, tlog.ColorYellow+` - The filesystem was created by gocryptfs v0.6 or earlier. This version of - gocryptfs can no longer mount the filesystem. - Please download gocryptfs v0.11 and upgrade your filesystem, - see https://github.com/rfjakob/gocryptfs/wiki/Upgrading for instructions. - - If you have trouble upgrading, join the discussion at - https://github.com/rfjakob/gocryptfs/issues/29 . - -`+tlog.ColorReset) - return nil, exitcodes.NewErr("Deprecated filesystem", exitcodes.DeprecatedFS) } @@ -222,38 +208,38 @@ func Load(filename string) (*ConfFile, error) { // DecryptMasterKey decrypts the masterkey stored in cf.EncryptedKey using // password. -func (cf *ConfFile) DecryptMasterKey(password []byte) (masterkey []byte, err error) { +func (cf *ConfFile) DecryptMasterKey(password []byte, giveHash bool) (masterkey, scryptHash []byte, err error) { // Generate derived key from password - scryptHash := cf.ScryptObject.DeriveKey(password) + scryptHash = cf.ScryptObject.DeriveKey(password) // Unlock master key using password-based key useHKDF := cf.IsFeatureFlagSet(FlagHKDF) ce := getKeyEncrypter(scryptHash, useHKDF) - tlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password masterkey, err = ce.DecryptBlock(cf.EncryptedKey, 0, nil) - tlog.Warn.Enabled = true - // Purge scrypt-derived key - for i := range scryptHash { - scryptHash[i] = 0 + if !giveHash { + // Purge scrypt-derived key + for i := range scryptHash { + scryptHash[i] = 0 + } + scryptHash = nil } - scryptHash = nil ce.Wipe() ce = nil if err != nil { - tlog.Warn.Printf("failed to unlock master key: %s", err.Error()) - return nil, exitcodes.NewErr("Password incorrect.", exitcodes.PasswordIncorrect) + return nil, nil, exitcodes.NewErr("Password incorrect.", exitcodes.PasswordIncorrect) } - return masterkey, nil + + return masterkey, scryptHash, nil } // EncryptKey - encrypt "key" using an scrypt hash generated from "password" // and store it in cf.EncryptedKey. // Uses scrypt with cost parameter logN and stores the scrypt parameters in // cf.ScryptObject. -func (cf *ConfFile) EncryptKey(key []byte, password []byte, logN int) { +func (cf *ConfFile) EncryptKey(key []byte, password []byte, logN int, giveHash bool) []byte { // Generate scrypt-derived key from password cf.ScryptObject = NewScryptKDF(logN) scryptHash := cf.ScryptObject.DeriveKey(password) @@ -263,13 +249,45 @@ func (cf *ConfFile) EncryptKey(key []byte, password []byte, logN int) { ce := getKeyEncrypter(scryptHash, useHKDF) cf.EncryptedKey = ce.EncryptBlock(key, 0, nil) - // Purge scrypt-derived key - for i := range scryptHash { - scryptHash[i] = 0 + if !giveHash { + // Purge scrypt-derived key + for i := range scryptHash { + scryptHash[i] = 0 + } + scryptHash = nil } - scryptHash = nil ce.Wipe() ce = nil + + return scryptHash +} + +// DroidFS function to allow masterkey to be decrypted directely using the scrypt hash and return it if requested +func (cf *ConfFile) GetMasterkey(password, givenScryptHash, returnedScryptHashBuff []byte) []byte { + var masterkey []byte + var err error + var scryptHash []byte + if len(givenScryptHash) > 0 { //decrypt with hash + useHKDF := cf.IsFeatureFlagSet(FlagHKDF) + ce := getKeyEncrypter(givenScryptHash, useHKDF) + masterkey, err = ce.DecryptBlock(cf.EncryptedKey, 0, nil) + ce.Wipe() + ce = nil + if err == nil { + return masterkey + } + } else { //decrypt with password + masterkey, scryptHash, err = cf.DecryptMasterKey(password, len(returnedScryptHashBuff) > 0) + //copy and wipe scryptHash + for i := range scryptHash { + returnedScryptHashBuff[i] = scryptHash[i] + scryptHash[i] = 0 + } + if err == nil { + return masterkey + } + } + return nil } // WriteFile - write out config in JSON format to file "filename.tmp" @@ -296,7 +314,6 @@ func (cf *ConfFile) WriteFile() error { if err != nil { // This can happen on network drives: FRITZ.NAS mounted on MacOS returns // "operation not supported": https://github.com/rfjakob/gocryptfs/issues/390 - tlog.Warn.Printf("Warning: fsync failed: %v", err) // Try sync instead syscall.Sync() } diff --git a/internal/configfile/config_test.go b/internal/configfile/config_test.go deleted file mode 100644 index ce35531..0000000 --- a/internal/configfile/config_test.go +++ /dev/null @@ -1,147 +0,0 @@ -package configfile - -import ( - "fmt" - "testing" - "time" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -var testPw = []byte("test") - -func TestLoadV1(t *testing.T) { - _, _, err := LoadAndDecrypt("config_test/v1.conf", testPw) - if err == nil { - t.Errorf("Outdated v1 config file must fail to load but it didn't") - } else if testing.Verbose() { - fmt.Println(err) - } -} - -// Load a known-good config file and verify that it takes at least 100ms -// (brute-force protection) -func TestLoadV2(t *testing.T) { - t1 := time.Now() - - _, _, err := LoadAndDecrypt("config_test/v2.conf", testPw) - if err != nil { - t.Errorf("Could not load v2 config file: %v", err) - } - - elapsed := time.Since(t1) - if elapsed < 100*time.Millisecond { - t.Errorf("scrypt calculation runs too fast: %d ms", elapsed/time.Millisecond) - } -} - -func TestLoadV2PwdError(t *testing.T) { - if !testing.Verbose() { - tlog.Warn.Enabled = false - } - _, _, err := LoadAndDecrypt("config_test/v2.conf", []byte("wrongpassword")) - if err == nil { - t.Errorf("Loading with wrong password must fail but it didn't") - } -} - -func TestLoadV2Feature(t *testing.T) { - _, _, err := LoadAndDecrypt("config_test/PlaintextNames.conf", testPw) - if err != nil { - t.Errorf("Could not load v2 PlaintextNames config file: %v", err) - } -} - -func TestLoadV2StrangeFeature(t *testing.T) { - _, _, err := LoadAndDecrypt("config_test/StrangeFeature.conf", testPw) - if err == nil { - t.Errorf("Loading unknown feature must fail but it didn't") - } else if testing.Verbose() { - fmt.Println(err) - } -} - -func TestCreateConfDefault(t *testing.T) { - err := Create("config_test/tmp.conf", testPw, false, 10, "test", false, false, nil, nil) - if err != nil { - t.Fatal(err) - } - _, c, err := LoadAndDecrypt("config_test/tmp.conf", testPw) - if err != nil { - t.Fatal(err) - } - // Check that all expected feature flags are set - want := []flagIota{ - FlagGCMIV128, FlagDirIV, FlagEMENames, FlagLongNames, - FlagRaw64, FlagHKDF, - } - for _, f := range want { - if !c.IsFeatureFlagSet(f) { - t.Errorf("Feature flag %q should be set but is not", knownFlags[f]) - } - } -} - -func TestCreateConfDevRandom(t *testing.T) { - err := Create("config_test/tmp.conf", testPw, false, 10, "test", false, true, nil, nil) - if err != nil { - t.Fatal(err) - } -} - -func TestCreateConfPlaintextnames(t *testing.T) { - err := Create("config_test/tmp.conf", testPw, true, 10, "test", false, false, nil, nil) - if err != nil { - t.Fatal(err) - } - _, c, err := LoadAndDecrypt("config_test/tmp.conf", testPw) - if err != nil { - t.Fatal(err) - } - // Check that all expected feature flags are set - want := []flagIota{ - FlagGCMIV128, FlagHKDF, - } - for _, f := range want { - if !c.IsFeatureFlagSet(f) { - t.Errorf("Feature flag %q should be set but is not", knownFlags[f]) - } - } -} - -// Reverse mode uses AESSIV -func TestCreateConfFileAESSIV(t *testing.T) { - err := Create("config_test/tmp.conf", testPw, false, 10, "test", true, false, nil, nil) - if err != nil { - t.Fatal(err) - } - _, c, err := LoadAndDecrypt("config_test/tmp.conf", testPw) - if err != nil { - t.Fatal(err) - } - if !c.IsFeatureFlagSet(FlagAESSIV) { - t.Error("AESSIV flag should be set but is not") - } -} - -func TestIsFeatureFlagKnown(t *testing.T) { - // Test a few hardcoded values - testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames", "AESSIV"} - // And also everything in knownFlags (yes, it is likely that we end up with - // some duplicates. Does not matter.) - for _, f := range knownFlags { - testKnownFlags = append(testKnownFlags, f) - } - - var cf ConfFile - for _, f := range testKnownFlags { - if !cf.isFeatureFlagKnown(f) { - t.Errorf("flag %q should be known", f) - } - } - - f := "StrangeFeatureFlag" - if cf.isFeatureFlagKnown(f) { - t.Errorf("flag %q should be NOT known", f) - } -} diff --git a/internal/configfile/config_test/.gitignore b/internal/configfile/config_test/.gitignore deleted file mode 100644 index 0720169..0000000 --- a/internal/configfile/config_test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tmp.conf diff --git a/internal/configfile/config_test/PlaintextNames.conf b/internal/configfile/config_test/PlaintextNames.conf deleted file mode 100644 index 5b9f4f7..0000000 --- a/internal/configfile/config_test/PlaintextNames.conf +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Creator": "gocryptfs v0.11-13-g96750a7-dirty", - "EncryptedKey": "pH6/kgPFrwkuFW/HDN/0UzwC8hLJCMm/upyEnsR1pVTfSJLL/JxfBaVCRyuZhc/S7h2PrxVSMO1xzLrk", - "ScryptObject": { - "Salt": "Hq0BqXXeMGVGfdYE1Y/qcW+pvxJBJymRAVgPUxQiZ8Y=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "PlaintextNames" - ] -} \ No newline at end of file diff --git a/internal/configfile/config_test/StrangeFeature.conf b/internal/configfile/config_test/StrangeFeature.conf deleted file mode 100644 index eadf168..0000000 --- a/internal/configfile/config_test/StrangeFeature.conf +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Creator": "gocryptfs v0.11-13-g96750a7-dirty", - "EncryptedKey": "mfN2FITcsLE+8QlpTb3r/D5rAAqEX5mJQuU655tcdwAotUwHkrIdYiKa2BjoocctQC0grwqPyuWxB7SH", - "ScryptObject": { - "Salt": "9G2knR016guT/AJqOKemjusYhqg+mI177Dz6a5RS7ts=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames", - "StrangeFeatureFlag" - ] -} diff --git a/internal/configfile/config_test/v1.conf b/internal/configfile/config_test/v1.conf deleted file mode 100644 index 588a25a..0000000 --- a/internal/configfile/config_test/v1.conf +++ /dev/null @@ -1,11 +0,0 @@ -{ - "EncryptedKey": "t6YAvFQJvbv46c93bHQ5IZnvNz80DA9cohGoSPL/2M257LuIigow6jbr8b9HhnbDqHTCcz7aKkMDzneF", - "ScryptObject": { - "Salt": "yT4yQmmRmVNx2P0tJrUswk5SQzZaL6Z8kUteAoNJkXM=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 1 -} diff --git a/internal/configfile/config_test/v2.conf b/internal/configfile/config_test/v2.conf deleted file mode 100644 index d1ca609..0000000 --- a/internal/configfile/config_test/v2.conf +++ /dev/null @@ -1,18 +0,0 @@ -{ - "Creator": "gocryptfs v0.11-13-g96750a7-dirty", - "EncryptedKey": "zY06x2JS0Fi7bkZ/3DppjmWZOI6xTjQ/Bf4Nru1rUzHml+stkAtvcoHT8PpHN4eKqL73ymQ86MmdYz+1", - "ScryptObject": { - "Salt": "U5MbvyTmwhEkLqe6XxlrONzPZEf2GLFZCAIixz2Kal0=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames" - ] -} \ No newline at end of file diff --git a/internal/configfile/scrypt.go b/internal/configfile/scrypt.go index 54fe0c6..c1edf4b 100644 --- a/internal/configfile/scrypt.go +++ b/internal/configfile/scrypt.go @@ -7,9 +7,8 @@ import ( "golang.org/x/crypto/scrypt" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" + "../cryptocore" + "../exitcodes" ) const ( @@ -84,23 +83,18 @@ func (s *ScryptKDF) LogN() int { func (s *ScryptKDF) validateParams() { minN := 1 << scryptMinLogN if s.N < minN { - tlog.Fatal.Println("Fatal: scryptn below 10 is too low to make sense") os.Exit(exitcodes.ScryptParams) } if s.R < scryptMinR { - tlog.Fatal.Printf("Fatal: scrypt parameter R below minimum: value=%d, min=%d", s.R, scryptMinR) os.Exit(exitcodes.ScryptParams) } if s.P < scryptMinP { - tlog.Fatal.Printf("Fatal: scrypt parameter P below minimum: value=%d, min=%d", s.P, scryptMinP) os.Exit(exitcodes.ScryptParams) } if len(s.Salt) < scryptMinSaltLen { - tlog.Fatal.Printf("Fatal: scrypt salt length below minimum: value=%d, min=%d", len(s.Salt), scryptMinSaltLen) os.Exit(exitcodes.ScryptParams) } if s.KeyLen < cryptocore.KeyLen { - tlog.Fatal.Printf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.KeyLen) os.Exit(exitcodes.ScryptParams) } } diff --git a/internal/configfile/scrypt_test.go b/internal/configfile/scrypt_test.go deleted file mode 100644 index 8f7a5c8..0000000 --- a/internal/configfile/scrypt_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package configfile - -import ( - "testing" -) - -/* -Results on a 2.7GHz Pentium G630: - -gocryptfs/cryptfs$ go test -bench=. -PASS -BenchmarkScrypt10-2 300 6021435 ns/op ... 6ms -BenchmarkScrypt11-2 100 11861460 ns/op -BenchmarkScrypt12-2 100 23420822 ns/op -BenchmarkScrypt13-2 30 47666518 ns/op -BenchmarkScrypt14-2 20 92561590 ns/op ... 92ms -BenchmarkScrypt15-2 10 183971593 ns/op -BenchmarkScrypt16-2 3 368506365 ns/op -BenchmarkScrypt17-2 2 755502608 ns/op ... 755ms -ok github.com/rfjakob/gocryptfs/cryptfs 18.772s -*/ - -func benchmarkScryptN(n int, b *testing.B) { - kdf := NewScryptKDF(n) - for i := 0; i < b.N; i++ { - kdf.DeriveKey(testPw) - } -} - -func BenchmarkScrypt10(b *testing.B) { - benchmarkScryptN(10, b) -} - -func BenchmarkScrypt11(b *testing.B) { - benchmarkScryptN(11, b) -} - -func BenchmarkScrypt12(b *testing.B) { - benchmarkScryptN(12, b) -} - -func BenchmarkScrypt13(b *testing.B) { - benchmarkScryptN(13, b) -} - -func BenchmarkScrypt14(b *testing.B) { - benchmarkScryptN(14, b) -} - -func BenchmarkScrypt15(b *testing.B) { - benchmarkScryptN(15, b) -} - -func BenchmarkScrypt16(b *testing.B) { - benchmarkScryptN(16, b) -} - -func BenchmarkScrypt17(b *testing.B) { - benchmarkScryptN(17, b) -} diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index 747bb4c..c9b3c60 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -4,23 +4,22 @@ package contentenc import ( "bytes" "encoding/binary" - "encoding/hex" "errors" "log" "runtime" "sync" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/internal/tlog" + "../cryptocore" + "../stupidgcm" ) // NonceMode determines how nonces are created. type NonceMode int const ( + //value from FUSE doc + MAX_KERNEL_WRITE = 128 * 1024 + // DefaultBS is the default plaintext block size DefaultBS = 4096 // DefaultIVBits is the default length of IV, in bits. @@ -73,17 +72,17 @@ type ContentEnc struct { // New returns an initialized ContentEnc instance. func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc { - if fuse.MAX_KERNEL_WRITE%plainBS != 0 { - log.Panicf("unaligned MAX_KERNEL_WRITE=%d", fuse.MAX_KERNEL_WRITE) + if MAX_KERNEL_WRITE%plainBS != 0 { + log.Panicf("unaligned MAX_KERNEL_WRITE=%d", MAX_KERNEL_WRITE) } cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen // Take IV and GHASH overhead into account. - cReqSize := int(fuse.MAX_KERNEL_WRITE / plainBS * cipherBS) + cReqSize := int(MAX_KERNEL_WRITE / plainBS * cipherBS) // Unaligned reads (happens during fsck, could also happen with O_DIRECT?) // touch one additional ciphertext and plaintext block. Reserve space for the // extra block. cReqSize += int(cipherBS) - pReqSize := fuse.MAX_KERNEL_WRITE + int(plainBS) + pReqSize := MAX_KERNEL_WRITE + int(plainBS) c := &ContentEnc{ cryptoCore: cc, plainBS: plainBS, @@ -120,9 +119,7 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file var pBlock []byte pBlock, err = be.DecryptBlock(cBlock, blockNo, fileID) if err != nil { - if be.forceDecode && err == stupidgcm.ErrAuth { - tlog.Warn.Printf("DecryptBlocks: authentication failure in block #%d, overridden by forcedecode", firstBlockNo) - } else { + if !(be.forceDecode && err == stupidgcm.ErrAuth) { break } } @@ -163,12 +160,10 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b // All-zero block? if bytes.Equal(ciphertext, be.allZeroBlock) { - tlog.Debug.Printf("DecryptBlock: file hole encountered") return make([]byte, be.plainBS), nil } if len(ciphertext) < be.cryptoCore.IVLen { - tlog.Warn.Printf("DecryptBlock: Block is too short: %d bytes", len(ciphertext)) return nil, errors.New("Block is too short") } @@ -180,7 +175,6 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b // http://www.spinics.net/lists/kernel/msg2370127.html return nil, errors.New("all-zero nonce") } - ciphertextOrig := ciphertext ciphertext = ciphertext[be.cryptoCore.IVLen:] // Decrypt @@ -190,8 +184,6 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData) if err != nil { - tlog.Debug.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig)) - tlog.Debug.Println(hex.Dump(ciphertextOrig)) if be.forceDecode && err == stupidgcm.ErrAuth { return plaintext, err } diff --git a/internal/contentenc/content_test.go b/internal/contentenc/content_test.go deleted file mode 100644 index 998e9b8..0000000 --- a/internal/contentenc/content_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package contentenc - -import ( - "testing" - - "github.com/rfjakob/gocryptfs/internal/cryptocore" -) - -type testRange struct { - offset uint64 - length uint64 -} - -func TestSplitRange(t *testing.T) { - var ranges []testRange - - ranges = append(ranges, testRange{0, 70000}, - testRange{0, 10}, - testRange{234, 6511}, - testRange{65444, 54}, - testRange{0, 1024 * 1024}, - testRange{0, 65536}, - testRange{6654, 8945}) - - key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true, false) - f := New(cc, DefaultBS, false) - - for _, r := range ranges { - parts := f.ExplodePlainRange(r.offset, r.length) - var lastBlockNo uint64 = 1 << 63 - for _, p := range parts { - if p.BlockNo == lastBlockNo { - t.Errorf("Duplicate block number %d", p.BlockNo) - } - lastBlockNo = p.BlockNo - if p.Length > DefaultBS || p.Skip >= DefaultBS { - t.Errorf("Test fail: n=%d, length=%d, offset=%d\n", p.BlockNo, p.Length, p.Skip) - } - } - } -} - -func TestCiphertextRange(t *testing.T) { - var ranges []testRange - - ranges = append(ranges, testRange{0, 70000}, - testRange{0, 10}, - testRange{234, 6511}, - testRange{65444, 54}, - testRange{6654, 8945}) - - key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true, false) - f := New(cc, DefaultBS, false) - - for _, r := range ranges { - - blocks := f.ExplodePlainRange(r.offset, r.length) - alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) - skipBytes := blocks[0].Skip - - if alignedLength < r.length { - t.Errorf("alignedLength=%d is smaller than length=%d", alignedLength, r.length) - } - if (alignedOffset-HeaderLen)%f.cipherBS != 0 { - t.Errorf("alignedOffset=%d is not aligned", alignedOffset) - } - if r.offset%f.plainBS != 0 && skipBytes == 0 { - t.Errorf("skipBytes=0") - } - } -} - -func TestBlockNo(t *testing.T) { - key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true, false) - f := New(cc, DefaultBS, false) - - b := f.CipherOffToBlockNo(788) - if b != 0 { - t.Errorf("actual: %d", b) - } - b = f.CipherOffToBlockNo(HeaderLen + f.cipherBS) - if b != 1 { - t.Errorf("actual: %d", b) - } - b = f.PlainOffToBlockNo(788) - if b != 0 { - t.Errorf("actual: %d", b) - } - b = f.PlainOffToBlockNo(f.plainBS) - if b != 1 { - t.Errorf("actual: %d", b) - } -} diff --git a/internal/contentenc/file_header.go b/internal/contentenc/file_header.go index 6ce2e3b..921e835 100644 --- a/internal/contentenc/file_header.go +++ b/internal/contentenc/file_header.go @@ -11,7 +11,7 @@ import ( "fmt" "log" - "github.com/rfjakob/gocryptfs/internal/cryptocore" + "../cryptocore" ) const ( diff --git a/internal/contentenc/offsets.go b/internal/contentenc/offsets.go index 3a0abf3..1f0a70c 100644 --- a/internal/contentenc/offsets.go +++ b/internal/contentenc/offsets.go @@ -2,8 +2,6 @@ package contentenc import ( "log" - - "github.com/rfjakob/gocryptfs/internal/tlog" ) // Contentenc methods that translate offsets between ciphertext and plaintext @@ -44,12 +42,10 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { if cipherSize == HeaderLen { // This can happen between createHeader() and Write() and is harmless. - tlog.Debug.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize) return 0 } if cipherSize < HeaderLen { - tlog.Warn.Printf("cipherSize %d < header size %d: corrupt file\n", cipherSize, HeaderLen) return 0 } @@ -58,7 +54,6 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { lastBlockSize := (cipherSize - HeaderLen) % be.cipherBS if lastBlockSize > 0 && lastBlockSize <= be.BlockOverhead() { tmp := cipherSize - lastBlockSize + be.BlockOverhead() + 1 - tlog.Warn.Printf("cipherSize %d: incomplete last block (%d bytes), padding to %d bytes", cipherSize, lastBlockSize, tmp) cipherSize = tmp } @@ -69,7 +64,6 @@ func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 { overhead := be.BlockOverhead()*blockCount + HeaderLen if overhead > cipherSize { - tlog.Warn.Printf("cipherSize %d < overhead %d: corrupt file\n", cipherSize, overhead) return 0 } diff --git a/internal/contentenc/offsets_test.go b/internal/contentenc/offsets_test.go deleted file mode 100644 index c3b8fcd..0000000 --- a/internal/contentenc/offsets_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package contentenc - -import ( - "fmt" - "testing" - - "github.com/rfjakob/gocryptfs/internal/cryptocore" -) - -// TestSizeToSize tests CipherSizeToPlainSize and PlainSizeToCipherSize -func TestSizeToSize(t *testing.T) { - key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true, false) - ce := New(cc, DefaultBS, false) - - const rangeMax = 10000 - - // y values in this order: - // 0 ... CipherSizeToPlainSize - // 1 ... PlainSizeToCipherSize - // 2 ... PlainOffToCipherOff - var yTable [rangeMax][3]uint64 - - // Calculate values - for x := range yTable { - yTable[x][0] = ce.CipherSizeToPlainSize(uint64(x)) - yTable[x][1] = ce.PlainSizeToCipherSize(uint64(x)) - yTable[x][2] = ce.PlainOffToCipherOff(uint64(x)) - } - - // Print data table - fmt.Print("x\tCipherSizeToPlainSize\tPlainSizeToCipherSize\tPlainOffToCipherOff\n") - for x := range yTable { - if x > 1 && x < rangeMax-1 { - // If the point before has value-1 and the point after has value+1, - // it is not interesting. Don't print it out. - interesting := false - for i := 0; i <= 2; i++ { - if yTable[x-1][i]+1 != yTable[x][i] && yTable[x][i]+1 != yTable[x+1][i]+1 { - interesting = true - } - // Monotonicity check - if yTable[x][i] < yTable[x-1][i] { - t.Errorf("column %d is non-monotonic!", i) - } - } - if !interesting { - continue - } - } - fmt.Printf("%d\t%d\t%d\t%d\n", x, yTable[x][0], yTable[x][1], yTable[x][2]) - } -} diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index d66f390..8ea11d4 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -12,9 +12,8 @@ import ( "github.com/rfjakob/eme" - "github.com/rfjakob/gocryptfs/internal/siv_aead" - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/internal/tlog" + "../siv_aead" + "../stupidgcm" ) const ( @@ -157,13 +156,10 @@ type wiper interface { func (c *CryptoCore) Wipe() { be := c.AEADBackend if be == BackendOpenSSL || be == BackendAESSIV { - tlog.Debug.Printf("CryptoCore.Wipe: Wiping AEADBackend %d key", be) // We don't use "x, ok :=" because we *want* to crash loudly if the // type assertion fails. w := c.AEADCipher.(wiper) w.Wipe() - } else { - tlog.Debug.Printf("CryptoCore.Wipe: Only nil'ing stdlib refs") } // We have no access to the keys (or key-equivalents) stored inside the // Go stdlib. Best we can is to nil the references and force a GC. diff --git a/internal/cryptocore/cryptocore_test.go b/internal/cryptocore/cryptocore_test.go deleted file mode 100644 index e595ef6..0000000 --- a/internal/cryptocore/cryptocore_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package cryptocore - -import ( - "testing" - - "github.com/rfjakob/gocryptfs/internal/stupidgcm" -) - -// "New" should accept at least these param combinations -func TestCryptoCoreNew(t *testing.T) { - key := make([]byte, 32) - for _, useHKDF := range []bool{true, false} { - c := New(key, BackendGoGCM, 96, useHKDF, false) - if c.IVLen != 12 { - t.Fail() - } - c = New(key, BackendGoGCM, 128, useHKDF, false) - if c.IVLen != 16 { - t.Fail() - } - if stupidgcm.BuiltWithoutOpenssl { - continue - } - c = New(key, BackendOpenSSL, 128, useHKDF, false) - if c.IVLen != 16 { - t.Fail() - } - } -} - -// "New" should panic on any key not 32 bytes long -func TestNewPanic(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Errorf("The code did not panic") - } - }() - - key := make([]byte, 16) - New(key, BackendOpenSSL, 128, true, false) -} diff --git a/internal/cryptocore/hkdf_test.go b/internal/cryptocore/hkdf_test.go deleted file mode 100644 index 96ee01f..0000000 --- a/internal/cryptocore/hkdf_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package cryptocore - -import ( - "bytes" - "encoding/hex" - "testing" -) - -type hkdfTestCase struct { - masterkey []byte - info string - out []byte -} - -// TestHkdfDerive verifies that we get the expected values from hkdfDerive. They -// must not change because this would change the on-disk format. -func TestHkdfDerive(t *testing.T) { - master0 := bytes.Repeat([]byte{0x00}, 32) - master1 := bytes.Repeat([]byte{0x01}, 32) - out1, _ := hex.DecodeString("9ba3cddd48c6339c6e56ebe85f0281d6e9051be4104176e65cb0f8a6f77ae6b4") - out2, _ := hex.DecodeString("e8a2499f48700b954f31de732efd04abce822f5c948e7fbc0896607be0d36d12") - out3, _ := hex.DecodeString("9137f2e67a842484137f3c458f357f204c30d7458f94f432fa989be96854a649") - out4, _ := hex.DecodeString("0bfa5da7d9724d4753269940d36898e2c0f3717c0fee86ada58b5fd6c08cc26c") - - testCases := []hkdfTestCase{ - {master0, "EME filename encryption", out1}, - {master0, hkdfInfoEMENames, out1}, - {master1, "EME filename encryption", out2}, - {master1, hkdfInfoEMENames, out2}, - {master1, "AES-GCM file content encryption", out3}, - {master1, hkdfInfoGCMContent, out3}, - {master1, "AES-SIV file content encryption", out4}, - {master1, hkdfInfoSIVContent, out4}, - } - - for i, v := range testCases { - out := hkdfDerive(v.masterkey, v.info, 32) - if !bytes.Equal(out, v.out) { - want := hex.EncodeToString(v.out) - have := hex.EncodeToString(out) - t.Errorf("testcase %d error:\n"+ - "want=%s\n"+ - "have=%s", i, want, have) - } - } -} diff --git a/internal/cryptocore/randprefetch_test.go b/internal/cryptocore/randprefetch_test.go deleted file mode 100644 index b263ef3..0000000 --- a/internal/cryptocore/randprefetch_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package cryptocore - -import ( - "bytes" - "compress/flate" - "runtime" - "sync" - "testing" -) - -// TestRandPrefetch hammers the randPrefetcher with 100 goroutines and verifies -// that the result is incompressible -func TestRandPrefetch(t *testing.T) { - runtime.GOMAXPROCS(10) - p := 100 - l := 200 - vec := make([][]byte, p) - var wg sync.WaitGroup - for i := 0; i < p; i++ { - wg.Add(1) - go func(i int) { - var tmp []byte - for x := 0; x < l; x++ { - tmp = append(tmp, randPrefetcher.read(l)...) - } - vec[i] = tmp - wg.Done() - }(i) - } - wg.Wait() - var b bytes.Buffer - fw, _ := flate.NewWriter(&b, flate.BestCompression) - for _, v := range vec { - fw.Write(v) - } - fw.Close() - if b.Len() < p*l*l { - t.Errorf("random data should be incompressible, but: in=%d compressed=%d\n", p*l*l, b.Len()) - } -} - -func BenchmarkRandPrefetch(b *testing.B) { - // 16-byte nonces are default since gocryptfs v0.7 - b.SetBytes(16) - for i := 0; i < b.N; i++ { - randPrefetcher.read(16) - } -} diff --git a/internal/cryptocore/randsize_test.go b/internal/cryptocore/randsize_test.go deleted file mode 100644 index 1db4745..0000000 --- a/internal/cryptocore/randsize_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// +build go1.7 - -// ^^^^^^^^^^^^ we use the "sub-benchmark" feature that was added in Go 1.7 - -package cryptocore - -import ( - "fmt" - "testing" -) - -/* -The troughput we get from /dev/urandom / getentropy depends a lot on the used -block size. Results on my Pentium G630 running Linux 4.11: - -BenchmarkRandSize/16-2 3000000 571 ns/op 27.98 MB/s -BenchmarkRandSize/32-2 3000000 585 ns/op 54.66 MB/s -BenchmarkRandSize/64-2 2000000 860 ns/op 74.36 MB/s -BenchmarkRandSize/128-2 1000000 1197 ns/op 106.90 MB/s -BenchmarkRandSize/256-2 1000000 1867 ns/op 137.06 MB/s -BenchmarkRandSize/512-2 500000 3187 ns/op 160.61 MB/s -BenchmarkRandSize/1024-2 200000 5888 ns/op 173.91 MB/s -BenchmarkRandSize/2048-2 100000 11554 ns/op 177.25 MB/s -BenchmarkRandSize/4096-2 100000 22523 ns/op 181.86 MB/s -BenchmarkRandSize/8192-2 30000 43111 ns/op 190.02 MB/s - -Results are similar when testing with dd, so this is not due to Go allocation -overhead: dd if=/dev/urandom bs=16 count=100000 of=/dev/null -*/ -func BenchmarkUrandomBlocksize(b *testing.B) { - for s := 16; s <= 8192; s *= 2 { - title := fmt.Sprintf("%d", s) - b.Run(title, func(b *testing.B) { - b.SetBytes(int64(s)) - for i := 0; i < b.N; i++ { - RandBytes(s) - } - }) - } -} diff --git a/internal/ctlsocksrv/ctlsock_serve.go b/internal/ctlsocksrv/ctlsock_serve.go deleted file mode 100644 index b63759e..0000000 --- a/internal/ctlsocksrv/ctlsock_serve.go +++ /dev/null @@ -1,163 +0,0 @@ -// Package ctlsocksrv implements the control socket interface that can be -// activated by passing "-ctlsock" on the command line. -package ctlsocksrv - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net" - "os" - "syscall" - - "github.com/rfjakob/gocryptfs/ctlsock" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Interface should be implemented by fusefrontend[_reverse] -type Interface interface { - EncryptPath(string) (string, error) - DecryptPath(string) (string, error) -} - -type ctlSockHandler struct { - fs Interface - socket *net.UnixListener -} - -// Serve serves incoming connections on "sock". This call blocks so you -// probably want to run it in a new goroutine. -func Serve(sock net.Listener, fs Interface) { - handler := ctlSockHandler{ - fs: fs, - socket: sock.(*net.UnixListener), - } - handler.acceptLoop() -} - -func (ch *ctlSockHandler) acceptLoop() { - for { - conn, err := ch.socket.Accept() - if err != nil { - // This can trigger on program exit with "use of closed network connection". - // Special-casing this is hard due to https://github.com/golang/go/issues/4373 - // so just don't use tlog.Warn to not cause panics in the tests. - tlog.Info.Printf("ctlsock: Accept error: %v", err) - break - } - go ch.handleConnection(conn.(*net.UnixConn)) - } -} - -// ReadBufSize is the size of the request read buffer. -// The longest possible path is 4096 bytes on Linux and 1024 on Mac OS X so -// 5000 bytes should be enough to hold the whole JSON request. This -// assumes that the path does not contain too many characters that had to be -// be escaped in JSON (for example, a null byte blows up to "\u0000"). -// We abort the connection if the request is bigger than this. -const ReadBufSize = 5000 - -// handleConnection reads and parses JSON requests from "conn" -func (ch *ctlSockHandler) handleConnection(conn *net.UnixConn) { - buf := make([]byte, ReadBufSize) - for { - n, err := conn.Read(buf) - if err == io.EOF { - conn.Close() - return - } else if err != nil { - tlog.Warn.Printf("ctlsock: Read error: %#v", err) - conn.Close() - return - } - if n == ReadBufSize { - tlog.Warn.Printf("ctlsock: request too big (max = %d bytes)", ReadBufSize-1) - conn.Close() - return - } - data := buf[:n] - var in ctlsock.RequestStruct - err = json.Unmarshal(data, &in) - if err != nil { - tlog.Warn.Printf("ctlsock: JSON Unmarshal error: %#v", err) - err = errors.New("JSON Unmarshal error: " + err.Error()) - sendResponse(conn, err, "", "") - continue - } - ch.handleRequest(&in, conn) - } -} - -// handleRequest handles an already-unmarshaled JSON request -func (ch *ctlSockHandler) handleRequest(in *ctlsock.RequestStruct, conn *net.UnixConn) { - var err error - var inPath, outPath, clean, warnText string - // You cannot perform both decryption and encryption in one request - if in.DecryptPath != "" && in.EncryptPath != "" { - err = errors.New("Ambiguous") - sendResponse(conn, err, "", "") - return - } - // Neither encryption nor encryption has been requested, makes no sense - if in.DecryptPath == "" && in.EncryptPath == "" { - err = errors.New("Empty input") - sendResponse(conn, err, "", "") - return - } - // Canonicalize input path - if in.EncryptPath != "" { - inPath = in.EncryptPath - } else { - inPath = in.DecryptPath - } - clean = SanitizePath(inPath) - // Warn if a non-canonical path was passed - if inPath != clean { - warnText = fmt.Sprintf("Non-canonical input path '%s' has been interpreted as '%s'.", inPath, clean) - } - // Error out if the canonical path is now empty - if clean == "" { - err = errors.New("Empty input after canonicalization") - sendResponse(conn, err, "", warnText) - return - } - // Actual encrypt or decrypt operation - if in.EncryptPath != "" { - outPath, err = ch.fs.EncryptPath(clean) - } else { - outPath, err = ch.fs.DecryptPath(clean) - } - sendResponse(conn, err, outPath, warnText) -} - -// sendResponse sends a JSON response message -func sendResponse(conn *net.UnixConn, err error, result string, warnText string) { - msg := ctlsock.ResponseStruct{ - Result: result, - WarnText: warnText, - } - if err != nil { - msg.ErrText = err.Error() - msg.ErrNo = -1 - // Try to extract the actual error number - if pe, ok := err.(*os.PathError); ok { - if se, ok := pe.Err.(syscall.Errno); ok { - msg.ErrNo = int32(se) - } - } else if err == syscall.ENOENT { - msg.ErrNo = int32(syscall.ENOENT) - } - } - jsonMsg, err := json.Marshal(msg) - if err != nil { - tlog.Warn.Printf("ctlsock: Marshal failed: %v", err) - return - } - // For convenience for the user, add a newline at the end. - jsonMsg = append(jsonMsg, '\n') - _, err = conn.Write(jsonMsg) - if err != nil { - tlog.Warn.Printf("ctlsock: Write failed: %v", err) - } -} diff --git a/internal/ctlsocksrv/sanitize.go b/internal/ctlsocksrv/sanitize.go deleted file mode 100644 index 4333872..0000000 --- a/internal/ctlsocksrv/sanitize.go +++ /dev/null @@ -1,32 +0,0 @@ -package ctlsocksrv - -import ( - "path/filepath" - "strings" -) - -// SanitizePath adapts filepath.Clean for FUSE paths. -// 1) Leading slash(es) are dropped -// 2) It returns "" instead of "." -// 3) If the cleaned path points above CWD (start with ".."), an empty string -// is returned -// See the TestSanitizePath testcases for examples. -func SanitizePath(path string) string { - // (1) - for len(path) > 0 && path[0] == '/' { - path = path[1:] - } - if len(path) == 0 { - return "" - } - clean := filepath.Clean(path) - // (2) - if clean == "." { - return "" - } - // (3) - if clean == ".." || strings.HasPrefix(clean, "../") { - return "" - } - return clean -} diff --git a/internal/ctlsocksrv/sanitize_test.go b/internal/ctlsocksrv/sanitize_test.go deleted file mode 100644 index 2462d5d..0000000 --- a/internal/ctlsocksrv/sanitize_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package ctlsocksrv - -import ( - "testing" -) - -func TestSanitizePath(t *testing.T) { - testCases := [][]string{ - {"", ""}, - {".", ""}, - {"/", ""}, - {"foo", "foo"}, - {"/foo", "foo"}, - {"foo/", "foo"}, - {"/foo/", "foo"}, - {"/foo/./foo", "foo/foo"}, - {"./", ""}, - {"..", ""}, - {"foo/../..", ""}, - {"foo/../../aaaaaa", ""}, - {"/foo/../../aaaaaa", ""}, - {"/////", ""}, - } - for _, tc := range testCases { - res := SanitizePath(tc[0]) - if res != tc[1] { - t.Errorf("%q: got %q, want %q", tc[0], res, tc[1]) - } - } -} diff --git a/internal/ensurefds012/ensurefds012.go b/internal/ensurefds012/ensurefds012.go deleted file mode 100644 index 7872eb2..0000000 --- a/internal/ensurefds012/ensurefds012.go +++ /dev/null @@ -1,52 +0,0 @@ -package ensurefds012 - -// Package ensurefds012 ensures that file descriptors 0,1,2 are open. It opens -// multiple copies of /dev/null as required. -// The Go stdlib as well as the gocryptfs code rely on the fact that -// fds 0,1,2 are always open. -// -// Use like this: -// -// import _ "github.com/rfjakob/gocryptfs/internal/ensurefds012" -// -// The import line MUST be in the alphabitcally first source code file of -// package main! -// -// You can test if it works as expected by inserting a long sleep into main, -// startings gocryptfs with all fds closed like this, -// -// $ ./gocryptfs 0<&- 1>&- 2>&- -// -// and then checking the open fds. It should look like this: -// -// $ ls -l /proc/$(pgrep gocryptfs)/fd -// total 0 -// lrwx------. 1 jakob jakob 64 Jan 5 15:54 0 -> /dev/null -// lrwx------. 1 jakob jakob 64 Jan 5 15:54 1 -> /dev/null -// lrwx------. 1 jakob jakob 64 Jan 5 15:54 2 -> /dev/null -// l-wx------. 1 jakob jakob 64 Jan 5 15:54 3 -> /dev/null -// lrwx------. 1 jakob jakob 64 Jan 5 15:54 4 -> 'anon_inode:[eventpoll]' -// -// See https://github.com/rfjakob/gocryptfs/issues/320 for details. - -import ( - "os" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" -) - -func init() { - fd, err := syscall.Open("/dev/null", syscall.O_RDWR, 0) - if err != nil { - os.Exit(exitcodes.DevNull) - } - for fd <= 2 { - fd, err = syscall.Dup(fd) - if err != nil { - os.Exit(exitcodes.DevNull) - } - } - // Close excess fd (usually fd 3) - syscall.Close(fd) -} diff --git a/internal/fido2/fido2.go b/internal/fido2/fido2.go deleted file mode 100644 index cd63483..0000000 --- a/internal/fido2/fido2.go +++ /dev/null @@ -1,110 +0,0 @@ -package fido2 - -import ( - "bytes" - "encoding/base64" - "fmt" - "io" - "os" - "os/exec" - "strings" - - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -type fidoCommand int - -const ( - cred fidoCommand = iota - assert fidoCommand = iota - assertWithPIN fidoCommand = iota -) - -const relyingPartyID = "gocryptfs" - -func callFidoCommand(command fidoCommand, device string, stdin []string) ([]string, error) { - var cmd *exec.Cmd - switch command { - case cred: - cmd = exec.Command("fido2-cred", "-M", "-h", "-v", device) - case assert: - cmd = exec.Command("fido2-assert", "-G", "-h", device) - case assertWithPIN: - cmd = exec.Command("fido2-assert", "-G", "-h", "-v", device) - } - tlog.Debug.Printf("callFidoCommand: executing %q with args %q", cmd.Path, cmd.Args) - cmd.Stderr = os.Stderr - in, err := cmd.StdinPipe() - if err != nil { - return nil, err - } - for _, s := range stdin { - // This does not deadlock because the pipe buffer is big enough (64kiB on Linux) - io.WriteString(in, s+"\n") - } - in.Close() - out, err := cmd.Output() - if err != nil { - return nil, fmt.Errorf("%s failed with %v", cmd.Args[0], err) - } - return strings.Split(string(out), "\n"), nil -} - -// Register registers a credential using a FIDO2 token -func Register(device string, userName string) (credentialID []byte) { - tlog.Info.Printf("FIDO2 Register: interact with your device ...") - 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) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.FIDO2Error) - } - credentialID, err = base64.StdEncoding.DecodeString(out[4]) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.FIDO2Error) - } - return credentialID -} - -// Secret generates a HMAC secret using a FIDO2 token -func Secret(device 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} - // try asserting without PIN first - out, err := callFidoCommand(assert, device, stdin) - if err != nil { - // if that fails, let's assert with PIN - out, err = callFidoCommand(assertWithPIN, device, stdin) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.FIDO2Error) - } - } - secret, err = base64.StdEncoding.DecodeString(out[4]) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.FIDO2Error) - } - - // sanity checks - secretLen := len(secret) - if secretLen < 32 { - tlog.Fatal.Printf("FIDO2 HMACSecret too short (%d)!\n", secretLen) - os.Exit(exitcodes.FIDO2Error) - } - zero := make([]byte, secretLen) - if bytes.Equal(zero, secret) { - tlog.Fatal.Printf("FIDO2 HMACSecret is all zero!") - os.Exit(exitcodes.FIDO2Error) - } - - return secret -} diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go deleted file mode 100644 index ae1c30c..0000000 --- a/internal/fusefrontend/args.go +++ /dev/null @@ -1,52 +0,0 @@ -package fusefrontend - -import ( - "github.com/hanwen/go-fuse/v2/fuse" -) - -// Args is a container for arguments that are passed from main() to fusefrontend -type Args struct { - // Cipherdir is the backing storage directory (absolute path). - // For reverse mode, Cipherdir actually contains *plaintext* files. - Cipherdir string - PlaintextNames bool - LongNames bool - // Should we chown a file after it has been created? - // This only makes sense if (1) allow_other is set and (2) we run as root. - PreserveOwner bool - // Should we force ownership to be presented with a given user and group? - // This only makes sense if allow_other is set. In *most* cases, it also - // only makes sense with PreserveOwner set, but can also make sense without - // PreserveOwner if the underlying filesystem acting as backing store - // enforces ownership itself. - ForceOwner *fuse.Owner - // ConfigCustom is true when the user select a non-default config file - // location. If it is false, reverse mode maps ".gocryptfs.reverse.conf" - // to "gocryptfs.conf" in the plaintext dir. - ConfigCustom bool - // NoPrealloc disables automatic preallocation before writing - NoPrealloc bool - // Try to serialize read operations, "-serialize_reads" - SerializeReads bool - // Force decode even if integrity check fails (openSSL only) - ForceDecode bool - // Exclude is a list of paths to make inaccessible, starting match at - // the filesystem root - Exclude []string - // ExcludeWildcards is a list of paths to make inaccessible, matched - // anywhere, and supporting wildcards - ExcludeWildcard []string - // ExcludeFrom is a list of files from which to read exclusion patterns - // (with wildcard syntax) - ExcludeFrom []string - // Suid is true if the filesystem has been mounted with the "-suid" flag. - // If it is false, we can ignore the GETXATTR "security.capability" calls, - // which are a performance problem for writes. See - // https://github.com/rfjakob/gocryptfs/issues/515 for details. - Suid bool - // Enable the FUSE kernel_cache option - KernelCache bool - // SharedStorage disables caching & hard link tracking, - // enabled via cli flag "-sharedstorage" - SharedStorage bool -} diff --git a/internal/fusefrontend/ctlsock_interface.go b/internal/fusefrontend/ctlsock_interface.go deleted file mode 100644 index f34739a..0000000 --- a/internal/fusefrontend/ctlsock_interface.go +++ /dev/null @@ -1,105 +0,0 @@ -package fusefrontend - -import ( - "fmt" - "path" - "path/filepath" - "strings" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/ctlsocksrv" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -var _ ctlsocksrv.Interface = &RootNode{} // Verify that interface is implemented. - -// EncryptPath implements ctlsock.Backend -// -// Symlink-safe through openBackingDir(). -func (rn *RootNode) EncryptPath(plainPath string) (string, error) { - if plainPath == "" { - // Empty string gets encrypted as empty string - return plainPath, nil - } - if rn.args.PlaintextNames { - return plainPath, nil - } - // Encrypt path level by level using openBackingDir. Pretty inefficient, - // but does not matter here. - parts := strings.Split(plainPath, "/") - wd := "" - cPath := "" - for _, part := range parts { - wd = filepath.Join(wd, part) - dirfd, cName, err := rn.openBackingDir(wd) - if err != nil { - return "", err - } - syscall.Close(dirfd) - cPath = filepath.Join(cPath, cName) - } - tlog.Debug.Printf("encryptPath '%s' -> '%s'", plainPath, cPath) - return cPath, nil -} - -// DecryptPath implements ctlsock.Backend -// -// DecryptPath is symlink-safe because openBackingDir() and decryptPathAt() -// are symlink-safe. -func (rn *RootNode) DecryptPath(cipherPath string) (plainPath string, err error) { - dirfd, _, err := rn.openBackingDir("") - if err != nil { - return "", err - } - defer syscall.Close(dirfd) - return rn.decryptPathAt(dirfd, cipherPath) -} - -// decryptPathAt decrypts a ciphertext path relative to dirfd. -// -// Symlink-safe through ReadDirIVAt() and ReadLongNameAt(). -func (rn *RootNode) decryptPathAt(dirfd int, cipherPath string) (plainPath string, err error) { - if rn.args.PlaintextNames || cipherPath == "" { - return cipherPath, nil - } - parts := strings.Split(cipherPath, "/") - wd := dirfd - for i, part := range parts { - dirIV, err := nametransform.ReadDirIVAt(wd) - if err != nil { - fmt.Printf("ReadDirIV: %v\n", err) - return "", err - } - longPart := part - if nametransform.IsLongContent(part) { - longPart, err = nametransform.ReadLongNameAt(wd, part) - if err != nil { - fmt.Printf("ReadLongName: %v\n", err) - return "", err - } - } - name, err := rn.nameTransform.DecryptName(longPart, dirIV) - if err != nil { - fmt.Printf("DecryptName: %v\n", err) - return "", err - } - plainPath = path.Join(plainPath, name) - // Last path component? We are done. - if i == len(parts)-1 { - break - } - // Descend into next directory - wd, err = syscallcompat.Openat(wd, part, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) - if err != nil { - return "", err - } - // Yes this is somewhat wasteful in terms of used file descriptors: - // we keep them all open until the function returns. But it is simple - // and reliable. - defer syscall.Close(wd) - } - - return plainPath, nil -} diff --git a/internal/fusefrontend/dircache.go b/internal/fusefrontend/dircache.go deleted file mode 100644 index 6732de1..0000000 --- a/internal/fusefrontend/dircache.go +++ /dev/null @@ -1,180 +0,0 @@ -package fusefrontend - -import ( - "fmt" - "log" - "sync" - "syscall" - "time" - - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -const ( - // Number of entries in the dirCache. - // 20 entries work well for "git stat" on a small git repo on sshfs. - // Keep in sync with test_helpers.maxCacheFds ! - // TODO: How to share this constant without causing an import cycle? - dirCacheSize = 20 - // Enable Lookup/Store/Clear debug messages - enableDebugMessages = false - // Enable hit rate statistics printing - enableStats = false -) - -type dirCacheEntry struct { - // pointer to the Node this entry belongs to - node *Node - // fd to the directory (opened with O_PATH!) - fd int - // content of gocryptfs.diriv in this directory - iv []byte -} - -func (e *dirCacheEntry) Clear() { - // An earlier clear may have already closed the fd, or the cache - // has never been filled (fd is 0 in that case). - // Note: package ensurefds012, imported from main, guarantees that dirCache - // can never get fds 0,1,2. - if e.fd > 0 { - err := syscall.Close(e.fd) - if err != nil { - tlog.Warn.Printf("dirCache.Clear: Close failed: %v", err) - } - } - e.fd = -1 - e.node = nil - e.iv = nil -} - -type dirCache struct { - sync.Mutex - // Cache entries - entries [dirCacheSize]dirCacheEntry - // Where to store the next entry (index into entries) - nextIndex int - // On the first Lookup(), the expire thread is started, and this flag is set - // to true. - expireThreadRunning bool - // Hit rate stats. Evaluated and reset by the expire thread. - lookups uint64 - hits uint64 -} - -// Clear clears the cache contents. -func (d *dirCache) Clear() { - d.dbg("Clear\n") - d.Lock() - defer d.Unlock() - for i := range d.entries { - d.entries[i].Clear() - } -} - -// Store the entry in the cache. The passed "fd" will be Dup()ed, and the caller -// can close their copy at will. -func (d *dirCache) Store(node *Node, fd int, iv []byte) { - // Note: package ensurefds012, imported from main, guarantees that dirCache - // can never get fds 0,1,2. - if fd <= 0 || len(iv) != nametransform.DirIVLen { - log.Panicf("Store sanity check failed: fd=%d len=%d", fd, len(iv)) - } - d.Lock() - defer d.Unlock() - e := &d.entries[d.nextIndex] - // Round-robin works well enough - d.nextIndex = (d.nextIndex + 1) % dirCacheSize - // Close the old fd - e.Clear() - fd2, err := syscall.Dup(fd) - if err != nil { - tlog.Warn.Printf("dirCache.Store: Dup failed: %v", err) - return - } - d.dbg("dirCache.Store %p fd=%d iv=%x\n", node, fd2, iv) - e.fd = fd2 - e.node = node - e.iv = iv - // expireThread is started on the first Lookup() - if !d.expireThreadRunning { - d.expireThreadRunning = true - go d.expireThread() - } -} - -// Lookup checks if relPath is in the cache, and returns an (fd, iv) pair. -// It returns (-1, nil) if not found. The fd is internally Dup()ed and the -// caller must close it when done. -func (d *dirCache) Lookup(node *Node) (fd int, iv []byte) { - d.Lock() - defer d.Unlock() - if enableStats { - d.lookups++ - } - var e *dirCacheEntry - for i := range d.entries { - e = &d.entries[i] - if e.fd <= 0 { - // Cache slot is empty - continue - } - if node != e.node { - // Not the right path - continue - } - var err error - fd, err = syscall.Dup(e.fd) - if err != nil { - tlog.Warn.Printf("dirCache.Lookup: Dup failed: %v", err) - return -1, nil - } - iv = e.iv - break - } - if fd == 0 { - d.dbg("dirCache.Lookup %p miss\n", node) - return -1, nil - } - if enableStats { - d.hits++ - } - if fd <= 0 || len(iv) != nametransform.DirIVLen { - log.Panicf("Lookup sanity check failed: fd=%d len=%d", fd, len(iv)) - } - d.dbg("dirCache.Lookup %p hit fd=%d dup=%d iv=%x\n", node, e.fd, fd, iv) - return fd, iv -} - -// expireThread is started on the first Lookup() -func (d *dirCache) expireThread() { - for { - time.Sleep(60 * time.Second) - d.Clear() - d.stats() - } -} - -// stats prints hit rate statistics and resets the counters. No-op if -// enableStats == false. -func (d *dirCache) stats() { - if !enableStats { - return - } - d.Lock() - lookups := d.lookups - hits := d.hits - d.lookups = 0 - d.hits = 0 - d.Unlock() - if lookups > 0 { - fmt.Printf("dirCache: hits=%3d lookups=%3d, rate=%3d%%\n", hits, lookups, (hits*100)/lookups) - } -} - -// dbg prints a debug message. Usually disabled. -func (d *dirCache) dbg(format string, a ...interface{}) { - if enableDebugMessages { - fmt.Printf(format, a...) - } -} diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go deleted file mode 100644 index cbf78e9..0000000 --- a/internal/fusefrontend/file.go +++ /dev/null @@ -1,457 +0,0 @@ -package fusefrontend - -// FUSE operations on file handles - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "io" - "log" - "math" - "os" - "sync" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/inomap" - "github.com/rfjakob/gocryptfs/internal/openfiletable" - "github.com/rfjakob/gocryptfs/internal/serialize_reads" - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// File implements the go-fuse v2 API (github.com/hanwen/go-fuse/v2/fs) -type File struct { - fd *os.File - // Has Release() already been called on this file? This also means that the - // wlock entry has been freed, so let's not crash trying to access it. - // Due to concurrency, Release can overtake other operations. These will - // return EBADF in that case. - released bool - // fdLock prevents the fd to be closed while we are in the middle of - // an operation. - // Every FUSE entrypoint should RLock(). The only user of Lock() is - // Release(), which closes the fd and sets "released" to true. - fdLock sync.RWMutex - // Content encryption helper - contentEnc *contentenc.ContentEnc - // Device and inode number uniquely identify the backing file - qIno inomap.QIno - // Entry in the open file table - fileTableEntry *openfiletable.Entry - // Store where the last byte was written - lastWrittenOffset int64 - // The opCount is used to judge whether "lastWrittenOffset" is still - // guaranteed to be correct. - lastOpCount uint64 - // Parent filesystem - rootNode *RootNode -} - -// NewFile returns a new go-fuse File instance based on an already-open file -// descriptor. NewFile internally calls Fstat() on the fd. The resulting Stat_t -// is returned because node.Create() needs it. -// -// `cName` is only used for error logging and may be left blank. -func NewFile(fd int, cName string, rn *RootNode) (f *File, st *syscall.Stat_t, errno syscall.Errno) { - // Need device number and inode number for openfiletable locking - st = &syscall.Stat_t{} - if err := syscall.Fstat(fd, st); err != nil { - errno = fs.ToErrno(err) - return - } - qi := inomap.QInoFromStat(st) - e := openfiletable.Register(qi) - - osFile := os.NewFile(uintptr(fd), cName) - - f = &File{ - fd: osFile, - contentEnc: rn.contentEnc, - qIno: qi, - fileTableEntry: e, - rootNode: rn, - } - return f, st, 0 -} - -// intFd - return the backing file descriptor as an integer. -func (f *File) intFd() int { - return int(f.fd.Fd()) -} - -// readFileID loads the file header from disk and extracts the file ID. -// Returns io.EOF if the file is empty. -func (f *File) readFileID() ([]byte, error) { - // We read +1 byte to determine if the file has actual content - // and not only the header. A header-only file will be considered empty. - // This makes File ID poisoning more difficult. - readLen := contentenc.HeaderLen + 1 - buf := make([]byte, readLen) - n, err := f.fd.ReadAt(buf, 0) - if err != nil { - if err == io.EOF && n != 0 { - tlog.Warn.Printf("readFileID %d: incomplete file, got %d instead of %d bytes", - f.qIno.Ino, n, readLen) - f.rootNode.reportMitigatedCorruption(fmt.Sprint(f.qIno.Ino)) - } - return nil, err - } - buf = buf[:contentenc.HeaderLen] - h, err := contentenc.ParseHeader(buf) - if err != nil { - return nil, err - } - return h.ID, nil -} - -// createHeader creates a new random header and writes it to disk. -// Returns the new file ID. -// The caller must hold fileIDLock.Lock(). -func (f *File) createHeader() (fileID []byte, err error) { - h := contentenc.RandomHeader() - buf := h.Pack() - // Prevent partially written (=corrupt) header by preallocating the space beforehand - if !f.rootNode.args.NoPrealloc { - err = syscallcompat.EnospcPrealloc(f.intFd(), 0, contentenc.HeaderLen) - if err != nil { - if !syscallcompat.IsENOSPC(err) { - tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.qIno.Ino, err.Error()) - } - return nil, err - } - } - // Actually write header - _, err = f.fd.WriteAt(buf, 0) - if err != nil { - return nil, err - } - return h.ID, err -} - -// doRead - read "length" plaintext bytes from plaintext offset "off" and append -// to "dst". -// Arguments "length" and "off" do not have to be block-aligned. -// -// doRead reads the corresponding ciphertext blocks from disk, decrypts them and -// returns the requested part of the plaintext. -// -// Called by Read() for normal reading, -// by Write() and Truncate() via doWrite() for Read-Modify-Write. -func (f *File) doRead(dst []byte, off uint64, length uint64) ([]byte, syscall.Errno) { - // Get the file ID, either from the open file table, or from disk. - var fileID []byte - f.fileTableEntry.IDLock.Lock() - if f.fileTableEntry.ID != nil { - // Use the cached value in the file table - fileID = f.fileTableEntry.ID - } else { - // Not cached, we have to read it from disk. - var err error - fileID, err = f.readFileID() - if err != nil { - f.fileTableEntry.IDLock.Unlock() - if err == io.EOF { - // Empty file - return nil, 0 - } - buf := make([]byte, 100) - n, _ := f.fd.ReadAt(buf, 0) - buf = buf[:n] - hexdump := hex.EncodeToString(buf) - tlog.Warn.Printf("doRead %d: corrupt header: %v\nFile hexdump (%d bytes): %s", - f.qIno.Ino, err, n, hexdump) - return nil, syscall.EIO - } - // Save into the file table - f.fileTableEntry.ID = fileID - } - f.fileTableEntry.IDLock.Unlock() - if fileID == nil { - log.Panicf("fileID=%v", fileID) - } - // Read the backing ciphertext in one go - blocks := f.contentEnc.ExplodePlainRange(off, length) - alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks) - // f.fd.ReadAt takes an int64! - if alignedOffset > math.MaxInt64 { - return nil, syscall.EFBIG - } - skip := blocks[0].Skip - tlog.Debug.Printf("doRead: off=%d len=%d -> off=%d len=%d skip=%d\n", - off, length, alignedOffset, alignedLength, skip) - - ciphertext := f.rootNode.contentEnc.CReqPool.Get() - ciphertext = ciphertext[:int(alignedLength)] - n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset)) - if err != nil && err != io.EOF { - tlog.Warn.Printf("read: ReadAt: %s", err.Error()) - return nil, fs.ToErrno(err) - } - // The ReadAt came back empty. We can skip all the decryption and return early. - if n == 0 { - f.rootNode.contentEnc.CReqPool.Put(ciphertext) - return dst, 0 - } - // Truncate ciphertext buffer down to actually read bytes - ciphertext = ciphertext[0:n] - - firstBlockNo := blocks[0].BlockNo - tlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n) - - // Decrypt it - plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, fileID) - f.rootNode.contentEnc.CReqPool.Put(ciphertext) - if err != nil { - if f.rootNode.args.ForceDecode && err == stupidgcm.ErrAuth { - // We do not have the information which block was corrupt here anymore, - // but DecryptBlocks() has already logged it anyway. - tlog.Warn.Printf("doRead %d: off=%d len=%d: returning corrupt data due to forcedecode", - f.qIno.Ino, off, length) - } else { - curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) - tlog.Warn.Printf("doRead %d: corrupt block #%d: %v", f.qIno.Ino, curruptBlockNo, err) - return nil, syscall.EIO - } - } - - // Crop down to the relevant part - var out []byte - lenHave := len(plaintext) - lenWant := int(skip + length) - if lenHave > lenWant { - out = plaintext[skip:lenWant] - } else if lenHave > int(skip) { - out = plaintext[skip:lenHave] - } - // else: out stays empty, file was smaller than the requested offset - - out = append(dst, out...) - f.rootNode.contentEnc.PReqPool.Put(plaintext) - - return out, 0 -} - -// Read - FUSE call -func (f *File) Read(ctx context.Context, buf []byte, off int64) (resultData fuse.ReadResult, errno syscall.Errno) { - if len(buf) > fuse.MAX_KERNEL_WRITE { - // This would crash us due to our fixed-size buffer pool - tlog.Warn.Printf("Read: rejecting oversized request with EMSGSIZE, len=%d", len(buf)) - return nil, syscall.EMSGSIZE - } - f.fdLock.RLock() - defer f.fdLock.RUnlock() - - f.fileTableEntry.ContentLock.RLock() - defer f.fileTableEntry.ContentLock.RUnlock() - - tlog.Debug.Printf("ino%d: FUSE Read: offset=%d length=%d", f.qIno.Ino, off, len(buf)) - if f.rootNode.args.SerializeReads { - serialize_reads.Wait(off, len(buf)) - } - out, errno := f.doRead(buf[:0], uint64(off), uint64(len(buf))) - if f.rootNode.args.SerializeReads { - serialize_reads.Done() - } - if errno != 0 { - return nil, errno - } - tlog.Debug.Printf("ino%d: Read: errno=%d, returning %d bytes", f.qIno.Ino, errno, len(out)) - return fuse.ReadResultData(out), errno -} - -// doWrite - encrypt "data" and write it to plaintext offset "off" -// -// Arguments do not have to be block-aligned, read-modify-write is -// performed internally as necessary -// -// Called by Write() for normal writing, -// and by Truncate() to rewrite the last file block. -// -// Empty writes do nothing and are allowed. -func (f *File) doWrite(data []byte, off int64) (uint32, syscall.Errno) { - fileWasEmpty := false - // Get the file ID, create a new one if it does not exist yet. - var fileID []byte - // The caller has exclusively locked ContentLock, which blocks all other - // readers and writers. No need to take IDLock. - if f.fileTableEntry.ID != nil { - fileID = f.fileTableEntry.ID - } else { - // If the file ID is not cached, read it from disk - var err error - fileID, err = f.readFileID() - // Write a new file header if the file is empty - if err == io.EOF { - fileID, err = f.createHeader() - fileWasEmpty = true - } - if err != nil { - return 0, fs.ToErrno(err) - } - f.fileTableEntry.ID = fileID - } - // Handle payload data - dataBuf := bytes.NewBuffer(data) - blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data))) - toEncrypt := make([][]byte, len(blocks)) - for i, b := range blocks { - blockData := dataBuf.Next(int(b.Length)) - // Incomplete block -> Read-Modify-Write - if b.IsPartial() { - // Read - oldData, errno := f.doRead(nil, b.BlockPlainOff(), f.contentEnc.PlainBS()) - if errno != 0 { - tlog.Warn.Printf("ino%d fh%d: RMW read failed: errno=%d", f.qIno.Ino, f.intFd(), errno) - return 0, errno - } - // Modify - blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip)) - tlog.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData)) - } - tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d", - f.qIno.Ino, len(blockData), b.BlockNo) - // Write into the to-encrypt list - toEncrypt[i] = blockData - } - // Encrypt all blocks - ciphertext := f.contentEnc.EncryptBlocks(toEncrypt, blocks[0].BlockNo, f.fileTableEntry.ID) - // Preallocate so we cannot run out of space in the middle of the write. - // This prevents partially written (=corrupt) blocks. - var err error - cOff := blocks[0].BlockCipherOff() - // f.fd.WriteAt & syscallcompat.EnospcPrealloc take int64 offsets! - if cOff > math.MaxInt64 { - return 0, syscall.EFBIG - } - if !f.rootNode.args.NoPrealloc { - err = syscallcompat.EnospcPrealloc(f.intFd(), int64(cOff), int64(len(ciphertext))) - if err != nil { - if !syscallcompat.IsENOSPC(err) { - tlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %v", f.qIno.Ino, f.intFd(), err) - } - if fileWasEmpty { - // Kill the file header again - f.fileTableEntry.ID = nil - err2 := syscall.Ftruncate(f.intFd(), 0) - if err2 != nil { - tlog.Warn.Printf("ino%d fh%d: doWrite: rollback failed: %v", f.qIno.Ino, f.intFd(), err2) - } - } - return 0, fs.ToErrno(err) - } - } - // Write - _, err = f.fd.WriteAt(ciphertext, int64(cOff)) - // Return memory to CReqPool - f.rootNode.contentEnc.CReqPool.Put(ciphertext) - if err != nil { - tlog.Warn.Printf("ino%d fh%d: doWrite: WriteAt off=%d len=%d failed: %v", - f.qIno.Ino, f.intFd(), cOff, len(ciphertext), err) - return 0, fs.ToErrno(err) - } - return uint32(len(data)), 0 -} - -// isConsecutiveWrite returns true if the current write -// directly (in time and space) follows the last write. -// This is an optimisation for streaming writes on NFS where a -// Stat() call is very expensive. -// The caller must "wlock.lock(f.devIno.ino)" otherwise this check would be racy. -func (f *File) isConsecutiveWrite(off int64) bool { - opCount := openfiletable.WriteOpCount() - return opCount == f.lastOpCount+1 && off == f.lastWrittenOffset+1 -} - -// Write - FUSE call -// -// If the write creates a hole, pads the file to the next block boundary. -func (f *File) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) { - if len(data) > fuse.MAX_KERNEL_WRITE { - // This would crash us due to our fixed-size buffer pool - tlog.Warn.Printf("Write: rejecting oversized request with EMSGSIZE, len=%d", len(data)) - return 0, syscall.EMSGSIZE - } - f.fdLock.RLock() - defer f.fdLock.RUnlock() - if f.released { - // The file descriptor has been closed concurrently - tlog.Warn.Printf("ino%d fh%d: Write on released file", f.qIno.Ino, f.intFd()) - return 0, syscall.EBADF - } - f.fileTableEntry.ContentLock.Lock() - defer f.fileTableEntry.ContentLock.Unlock() - tlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.qIno.Ino, off, len(data)) - // If the write creates a file hole, we have to zero-pad the last block. - // But if the write directly follows an earlier write, it cannot create a - // hole, and we can save one Stat() call. - if !f.isConsecutiveWrite(off) { - errno := f.writePadHole(off) - if errno != 0 { - return 0, errno - } - } - n, errno := f.doWrite(data, off) - if errno != 0 { - f.lastOpCount = openfiletable.WriteOpCount() - f.lastWrittenOffset = off + int64(len(data)) - 1 - } - return n, errno -} - -// Release - FUSE call, close file -func (f *File) Release(ctx context.Context) syscall.Errno { - f.fdLock.Lock() - if f.released { - log.Panicf("ino%d fh%d: double release", f.qIno.Ino, f.intFd()) - } - f.released = true - openfiletable.Unregister(f.qIno) - err := f.fd.Close() - f.fdLock.Unlock() - return fs.ToErrno(err) -} - -// Flush - FUSE call -func (f *File) Flush(ctx context.Context) syscall.Errno { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - - err := syscallcompat.Flush(f.intFd()) - return fs.ToErrno(err) -} - -// Fsync FUSE call -func (f *File) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - - return fs.ToErrno(syscall.Fsync(f.intFd())) -} - -// Getattr FUSE call (like stat) -func (f *File) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - - tlog.Debug.Printf("file.GetAttr()") - st := syscall.Stat_t{} - err := syscall.Fstat(f.intFd(), &st) - if err != nil { - return fs.ToErrno(err) - } - f.rootNode.inoMap.TranslateStat(&st) - a.FromStat(&st) - a.Size = f.contentEnc.CipherSizeToPlainSize(a.Size) - if f.rootNode.args.ForceOwner != nil { - a.Owner = *f.rootNode.args.ForceOwner - } - - return 0 -} diff --git a/internal/fusefrontend/file_allocate_truncate.go b/internal/fusefrontend/file_allocate_truncate.go deleted file mode 100644 index f4e6099..0000000 --- a/internal/fusefrontend/file_allocate_truncate.go +++ /dev/null @@ -1,218 +0,0 @@ -package fusefrontend - -// FUSE operations Truncate and Allocate on file handles -// i.e. ftruncate and fallocate - -import ( - "context" - "log" - "sync" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// FALLOC_DEFAULT is a "normal" fallocate operation -const FALLOC_DEFAULT = 0x00 - -// FALLOC_FL_KEEP_SIZE allocates disk space while not modifying the file size -const FALLOC_FL_KEEP_SIZE = 0x01 - -// Only warn once -var allocateWarnOnce sync.Once - -// Allocate - FUSE call for fallocate(2) -// -// mode=FALLOC_FL_KEEP_SIZE is implemented directly. -// -// mode=FALLOC_DEFAULT is implemented as a two-step process: -// -// (1) Allocate the space using FALLOC_FL_KEEP_SIZE -// (2) Set the file size using ftruncate (via truncateGrowFile) -// -// This allows us to reuse the file grow mechanics from Truncate as they are -// complicated and hard to get right. -// -// Other modes (hole punching, zeroing) are not supported. -func (f *File) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno { - if mode != FALLOC_DEFAULT && mode != FALLOC_FL_KEEP_SIZE { - f := func() { - tlog.Info.Printf("fallocate: only mode 0 (default) and 1 (keep size) are supported") - } - allocateWarnOnce.Do(f) - return syscall.EOPNOTSUPP - } - - f.fdLock.RLock() - defer f.fdLock.RUnlock() - if f.released { - return syscall.EBADF - } - f.fileTableEntry.ContentLock.Lock() - defer f.fileTableEntry.ContentLock.Unlock() - - blocks := f.contentEnc.ExplodePlainRange(off, sz) - firstBlock := blocks[0] - lastBlock := blocks[len(blocks)-1] - - // Step (1): Allocate the space the user wants using FALLOC_FL_KEEP_SIZE. - // This will fill file holes and/or allocate additional space past the end of - // the file. - cipherOff := firstBlock.BlockCipherOff() - cipherSz := lastBlock.BlockCipherOff() - cipherOff + - f.contentEnc.BlockOverhead() + lastBlock.Skip + lastBlock.Length - err := syscallcompat.Fallocate(f.intFd(), FALLOC_FL_KEEP_SIZE, int64(cipherOff), int64(cipherSz)) - tlog.Debug.Printf("Allocate off=%d sz=%d mode=%x cipherOff=%d cipherSz=%d\n", - off, sz, mode, cipherOff, cipherSz) - if err != nil { - return fs.ToErrno(err) - } - if mode == FALLOC_FL_KEEP_SIZE { - // The user did not want to change the apparent size. We are done. - return 0 - } - // Step (2): Grow the apparent file size - // We need the old file size to determine if we are growing the file at all. - newPlainSz := off + sz - oldPlainSz, err := f.statPlainSize() - if err != nil { - return fs.ToErrno(err) - } - if newPlainSz <= oldPlainSz { - // The new size is smaller (or equal). Fallocate with mode = 0 never - // truncates a file, so we are done. - return 0 - } - // The file grows. The space has already been allocated in (1), so what is - // left to do is to pad the first and last block and call truncate. - // truncateGrowFile does just that. - return f.truncateGrowFile(oldPlainSz, newPlainSz) -} - -// truncate - called from Setattr. -func (f *File) truncate(newSize uint64) (errno syscall.Errno) { - var err error - // Common case first: Truncate to zero - if newSize == 0 { - err = syscall.Ftruncate(int(f.fd.Fd()), 0) - if err != nil { - tlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.qIno.Ino, f.intFd(), err) - return fs.ToErrno(err) - } - // Truncate to zero kills the file header - f.fileTableEntry.ID = nil - return 0 - } - // We need the old file size to determine if we are growing or shrinking - // the file - oldSize, err := f.statPlainSize() - if err != nil { - return fs.ToErrno(err) - } - - oldB := float32(oldSize) / float32(f.contentEnc.PlainBS()) - newB := float32(newSize) / float32(f.contentEnc.PlainBS()) - tlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.qIno.Ino, oldB, newB, oldSize, newSize) - - // File size stays the same - nothing to do - if newSize == oldSize { - return 0 - } - // File grows - if newSize > oldSize { - return f.truncateGrowFile(oldSize, newSize) - } - - // File shrinks - blockNo := f.contentEnc.PlainOffToBlockNo(newSize) - cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) - plainOff := f.contentEnc.BlockNoToPlainOff(blockNo) - lastBlockLen := newSize - plainOff - var data []byte - if lastBlockLen > 0 { - data, errno = f.doRead(nil, plainOff, lastBlockLen) - if errno != 0 { - tlog.Warn.Printf("Truncate: shrink doRead returned error: %v", err) - return errno - } - } - // Truncate down to the last complete block - err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff)) - if err != nil { - tlog.Warn.Printf("Truncate: shrink Ftruncate returned error: %v", err) - return fs.ToErrno(err) - } - // Append partial block - if lastBlockLen > 0 { - _, status := f.doWrite(data, int64(plainOff)) - return status - } - return 0 -} - -// statPlainSize stats the file and returns the plaintext size -func (f *File) statPlainSize() (uint64, error) { - fi, err := f.fd.Stat() - if err != nil { - tlog.Warn.Printf("ino%d fh%d: statPlainSize: %v", f.qIno.Ino, f.intFd(), err) - return 0, err - } - cipherSz := uint64(fi.Size()) - plainSz := uint64(f.contentEnc.CipherSizeToPlainSize(cipherSz)) - return plainSz, nil -} - -// truncateGrowFile extends a file using seeking or ftruncate performing RMW on -// the first and last block as necessary. New blocks in the middle become -// file holes unless they have been fallocate()'d beforehand. -func (f *File) truncateGrowFile(oldPlainSz uint64, newPlainSz uint64) syscall.Errno { - if newPlainSz <= oldPlainSz { - log.Panicf("BUG: newSize=%d <= oldSize=%d", newPlainSz, oldPlainSz) - } - newEOFOffset := newPlainSz - 1 - if oldPlainSz > 0 { - n1 := f.contentEnc.PlainOffToBlockNo(oldPlainSz - 1) - n2 := f.contentEnc.PlainOffToBlockNo(newEOFOffset) - // The file is grown within one block, no need to pad anything. - // Write a single zero to the last byte and let doWrite figure out the RMW. - if n1 == n2 { - buf := make([]byte, 1) - _, errno := f.doWrite(buf, int64(newEOFOffset)) - return errno - } - } - // The truncate creates at least one new block. - // - // Make sure the old last block is padded to the block boundary. This call - // is a no-op if it is already block-aligned. - errno := f.zeroPad(oldPlainSz) - if errno != 0 { - return errno - } - // The new size is block-aligned. In this case we can do everything ourselves - // and avoid the call to doWrite. - if newPlainSz%f.contentEnc.PlainBS() == 0 { - // The file was empty, so it did not have a header. Create one. - if oldPlainSz == 0 { - id, err := f.createHeader() - if err != nil { - return fs.ToErrno(err) - } - f.fileTableEntry.ID = id - } - cSz := int64(f.contentEnc.PlainSizeToCipherSize(newPlainSz)) - err := syscall.Ftruncate(f.intFd(), cSz) - if err != nil { - tlog.Warn.Printf("Truncate: grow Ftruncate returned error: %v", err) - } - return fs.ToErrno(err) - } - // The new size is NOT aligned, so we need to write a partial block. - // Write a single zero to the last byte and let doWrite figure it out. - buf := make([]byte, 1) - _, errno = f.doWrite(buf, int64(newEOFOffset)) - return errno -} diff --git a/internal/fusefrontend/file_api_check.go b/internal/fusefrontend/file_api_check.go deleted file mode 100644 index 019e97f..0000000 --- a/internal/fusefrontend/file_api_check.go +++ /dev/null @@ -1,22 +0,0 @@ -package fusefrontend - -import ( - "github.com/hanwen/go-fuse/v2/fs" -) - -// Check that we have implemented the fs.File* interfaces -var _ = (fs.FileGetattrer)((*File)(nil)) -var _ = (fs.FileSetattrer)((*File)(nil)) -var _ = (fs.FileReleaser)((*File)(nil)) -var _ = (fs.FileReader)((*File)(nil)) -var _ = (fs.FileWriter)((*File)(nil)) -var _ = (fs.FileFsyncer)((*File)(nil)) -var _ = (fs.FileFlusher)((*File)(nil)) -var _ = (fs.FileAllocater)((*File)(nil)) -var _ = (fs.FileLseeker)((*File)(nil)) - -/* TODO -var _ = (fs.FileGetlker)((*File)(nil)) -var _ = (fs.FileSetlker)((*File)(nil)) -var _ = (fs.FileSetlkwer)((*File)(nil)) -*/ diff --git a/internal/fusefrontend/file_holes.go b/internal/fusefrontend/file_holes.go deleted file mode 100644 index cb44803..0000000 --- a/internal/fusefrontend/file_holes.go +++ /dev/null @@ -1,128 +0,0 @@ -package fusefrontend - -// Helper functions for sparse files (files with holes) - -import ( - "context" - "runtime" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Will a write to plaintext offset "targetOff" create a file hole in the -// ciphertext? If yes, zero-pad the last ciphertext block. -func (f *File) writePadHole(targetOff int64) syscall.Errno { - // Get the current file size. - fi, err := f.fd.Stat() - if err != nil { - tlog.Warn.Printf("checkAndPadHole: Fstat failed: %v", err) - return fs.ToErrno(err) - } - plainSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size())) - // Appending a single byte to the file (equivalent to writing to - // offset=plainSize) would write to "nextBlock". - nextBlock := f.contentEnc.PlainOffToBlockNo(plainSize) - // targetBlock is the block the user wants to write to. - targetBlock := f.contentEnc.PlainOffToBlockNo(uint64(targetOff)) - // The write goes into an existing block or (if the last block was full) - // starts a new one directly after the last block. Nothing to do. - if targetBlock <= nextBlock { - return 0 - } - // The write goes past the next block. nextBlock has - // to be zero-padded to the block boundary and (at least) nextBlock+1 - // will contain a file hole in the ciphertext. - errno := f.zeroPad(plainSize) - if errno != 0 { - return errno - } - return 0 -} - -// Zero-pad the file of size plainSize to the next block boundary. This is a no-op -// if the file is already block-aligned. -func (f *File) zeroPad(plainSize uint64) syscall.Errno { - lastBlockLen := plainSize % f.contentEnc.PlainBS() - if lastBlockLen == 0 { - // Already block-aligned - return 0 - } - missing := f.contentEnc.PlainBS() - lastBlockLen - pad := make([]byte, missing) - tlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing) - _, errno := f.doWrite(pad, int64(plainSize)) - return errno -} - -// Lseek - FUSE call. -// -// Looking at -// fuse_file_llseek @ https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/fs/fuse/file.c?h=v5.12.7#n2634 -// this function is only called for SEEK_HOLE & SEEK_DATA. -func (f *File) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) { - const ( - SEEK_DATA = 3 // find next data segment at or above `off` - SEEK_HOLE = 4 // find next hole at or above `off` - - // On error, we return -1 as the offset as per man lseek. - MinusOne = ^uint64(0) - ) - if whence != SEEK_DATA && whence != SEEK_HOLE { - tlog.Warn.Printf("BUG: Lseek was called with whence=%d. This is not supported!", whence) - return 0, syscall.EINVAL - } - if runtime.GOOS != "linux" { - // MacOS has broken (different?) SEEK_DATA / SEEK_HOLE semantics, see - // https://lists.gnu.org/archive/html/bug-gnulib/2018-09/msg00051.html - tlog.Warn.Printf("buggy on non-linux platforms, disabling SEEK_DATA & SEEK_HOLE") - return MinusOne, syscall.ENOSYS - } - - // We will need the file size - var st syscall.Stat_t - err := syscall.Fstat(f.intFd(), &st) - if err != nil { - return 0, fs.ToErrno(err) - } - fileSize := st.Size - // Better safe than sorry. The logic is only tested for 4k blocks. - if st.Blksize != 4096 { - tlog.Warn.Printf("unsupported block size of %d bytes, disabling SEEK_DATA & SEEK_HOLE", st.Blksize) - return MinusOne, syscall.ENOSYS - } - - // man lseek: offset beyond end of file -> ENXIO - if f.rootNode.contentEnc.PlainOffToCipherOff(off) >= uint64(fileSize) { - return MinusOne, syscall.ENXIO - } - - // Round down to start of block: - cipherOff := f.rootNode.contentEnc.BlockNoToCipherOff(f.rootNode.contentEnc.PlainOffToBlockNo(off)) - newCipherOff, err := syscall.Seek(f.intFd(), int64(cipherOff), int(whence)) - if err != nil { - return MinusOne, fs.ToErrno(err) - } - // already in data/hole => return original offset - if newCipherOff == int64(cipherOff) { - return off, 0 - } - // If there is no further hole, SEEK_HOLE returns the file size - // (SEEK_DATA returns ENXIO in this case). - if whence == SEEK_HOLE { - fi, err := f.fd.Stat() - if err != nil { - return MinusOne, fs.ToErrno(err) - } - if newCipherOff == fi.Size() { - return f.rootNode.contentEnc.CipherSizeToPlainSize(uint64(newCipherOff)), 0 - } - } - // syscall.Seek gave us the beginning of the next ext4 data/hole section. - // The next gocryptfs data/hole block starts at the next block boundary, - // so we have to round up: - newBlockNo := f.rootNode.contentEnc.CipherOffToBlockNo(uint64(newCipherOff) + f.rootNode.contentEnc.CipherBS() - 1) - return f.rootNode.contentEnc.BlockNoToPlainOff(newBlockNo), 0 -} diff --git a/internal/fusefrontend/file_setattr.go b/internal/fusefrontend/file_setattr.go deleted file mode 100644 index 0d6dc48..0000000 --- a/internal/fusefrontend/file_setattr.go +++ /dev/null @@ -1,85 +0,0 @@ -package fusefrontend - -import ( - "context" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -func (f *File) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { - errno = f.setAttr(ctx, in) - if errno != 0 { - return errno - } - return f.Getattr(ctx, out) -} - -func (f *File) setAttr(ctx context.Context, in *fuse.SetAttrIn) (errno syscall.Errno) { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - if f.released { - tlog.Warn.Printf("ino%d fh%d: Truncate on released file", f.qIno.Ino, f.intFd()) - return syscall.EBADF - } - f.fileTableEntry.ContentLock.Lock() - defer f.fileTableEntry.ContentLock.Unlock() - - // fchmod(2) - if mode, ok := in.GetMode(); ok { - errno = fs.ToErrno(syscall.Fchmod(f.intFd(), mode)) - if errno != 0 { - return errno - } - } - - // fchown(2) - uid32, uOk := in.GetUID() - gid32, gOk := in.GetGID() - if uOk || gOk { - uid := -1 - gid := -1 - - if uOk { - uid = int(uid32) - } - if gOk { - gid = int(gid32) - } - errno = fs.ToErrno(syscall.Fchown(f.intFd(), uid, gid)) - if errno != 0 { - return errno - } - } - - // utimens(2) - mtime, mok := in.GetMTime() - atime, aok := in.GetATime() - if mok || aok { - ap := &atime - mp := &mtime - if !aok { - ap = nil - } - if !mok { - mp = nil - } - errno = fs.ToErrno(syscallcompat.FutimesNano(f.intFd(), ap, mp)) - if errno != 0 { - return errno - } - } - - // truncate(2) - if sz, ok := in.GetSize(); ok { - errno = syscall.Errno(f.truncate(sz)) - if errno != 0 { - return errno - } - } - return 0 -} diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go deleted file mode 100644 index 8370a22..0000000 --- a/internal/fusefrontend/node.go +++ /dev/null @@ -1,449 +0,0 @@ -package fusefrontend - -import ( - "context" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Node is a file or directory in the filesystem tree -// in a gocryptfs mount. -type Node struct { - fs.Inode -} - -// Lookup - FUSE call for discovering a file. -func (n *Node) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // Get device number and inode number into `st` - st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - return nil, fs.ToErrno(err) - } - - // Create new inode and fill `out` - ch = n.newChild(ctx, st, out) - - // Translate ciphertext size in `out.Attr.Size` to plaintext size - n.translateSize(dirfd, cName, &out.Attr) - - return ch, 0 -} - -// GetAttr - FUSE call for stat()ing a file. -// -// GetAttr is symlink-safe through use of openBackingDir() and Fstatat(). -func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) (errno syscall.Errno) { - // If the kernel gives us a file handle, use it. - if f != nil { - return f.(fs.FileGetattrer).Getattr(ctx, out) - } - - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - return fs.ToErrno(err) - } - - // Fix inode number - rn := n.rootNode() - rn.inoMap.TranslateStat(st) - out.Attr.FromStat(st) - - // Translate ciphertext size in `out.Attr.Size` to plaintext size - n.translateSize(dirfd, cName, &out.Attr) - - if rn.args.ForceOwner != nil { - out.Owner = *rn.args.ForceOwner - } - return 0 -} - -// Unlink - FUSE call. Delete a file. -// -// Symlink-safe through use of Unlinkat(). -func (n *Node) Unlink(ctx context.Context, name string) (errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // Delete content - err := syscallcompat.Unlinkat(dirfd, cName, 0) - if err != nil { - return fs.ToErrno(err) - } - // Delete ".name" file - if !n.rootNode().args.PlaintextNames && nametransform.IsLongContent(cName) { - err = nametransform.DeleteLongNameAt(dirfd, cName) - if err != nil { - tlog.Warn.Printf("Unlink: could not delete .name file: %v", err) - } - } - return fs.ToErrno(err) -} - -// Readlink - FUSE call. -// -// Symlink-safe through openBackingDir() + Readlinkat(). -func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - return n.readlink(dirfd, cName) -} - -// Setattr - FUSE call. Called for chmod, truncate, utimens, ... -func (n *Node) Setattr(ctx context.Context, f fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { - // Use the fd if the kernel gave us one - if f != nil { - f2 := f.(*File) - return f2.Setattr(ctx, in, out) - } - - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // chmod(2) - // - // gocryptfs.diriv & gocryptfs.longname.[sha256].name files do NOT get chmod'ed - // or chown'ed with their parent file/dir for simplicity. - // See nametransform/perms.go for details. - if mode, ok := in.GetMode(); ok { - errno = fs.ToErrno(syscallcompat.FchmodatNofollow(dirfd, cName, mode)) - if errno != 0 { - return errno - } - } - - // chown(2) - uid32, uOk := in.GetUID() - gid32, gOk := in.GetGID() - if uOk || gOk { - uid := -1 - gid := -1 - - if uOk { - uid = int(uid32) - } - if gOk { - gid = int(gid32) - } - errno = fs.ToErrno(syscallcompat.Fchownat(dirfd, cName, uid, gid, unix.AT_SYMLINK_NOFOLLOW)) - if errno != 0 { - return errno - } - } - - // utimens(2) - mtime, mok := in.GetMTime() - atime, aok := in.GetATime() - if mok || aok { - ap := &atime - mp := &mtime - if !aok { - ap = nil - } - if !mok { - mp = nil - } - errno = fs.ToErrno(syscallcompat.UtimesNanoAtNofollow(dirfd, cName, ap, mp)) - if errno != 0 { - return errno - } - } - - // For truncate, the user has to have write permissions. That means we can - // depend on opening a RDWR fd and letting the File handle truncate. - if sz, ok := in.GetSize(); ok { - f, _, errno := n.Open(ctx, syscall.O_RDWR) - if errno != 0 { - return errno - } - f2 := f.(*File) - defer f2.Release(ctx) - errno = syscall.Errno(f2.truncate(sz)) - if errno != 0 { - return errno - } - return f2.Getattr(ctx, out) - } - - return n.Getattr(ctx, nil, out) -} - -// StatFs - FUSE call. Returns information about the filesystem. -// -// Symlink-safe because the path is ignored. -func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { - p := n.rootNode().args.Cipherdir - var st syscall.Statfs_t - err := syscall.Statfs(p, &st) - if err != nil { - return fs.ToErrno(err) - } - out.FromStatfsT(&st) - return 0 -} - -// Mknod - FUSE call. Create a device file. -// -// Symlink-safe through use of Mknodat(). -func (n *Node) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (inode *fs.Inode, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // Make sure context is nil if we don't want to preserve the owner - rn := n.rootNode() - if !rn.args.PreserveOwner { - ctx = nil - } - - // Create ".name" file to store long file name (except in PlaintextNames mode) - var err error - ctx2 := toFuseCtx(ctx) - if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { - err := rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - errno = fs.ToErrno(err) - return - } - // Create "gocryptfs.longfile." device node - err = syscallcompat.MknodatUser(dirfd, cName, mode, int(rdev), ctx2) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create regular device node - err = syscallcompat.MknodatUser(dirfd, cName, mode, int(rdev), ctx2) - } - if err != nil { - errno = fs.ToErrno(err) - return - } - - st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - errno = fs.ToErrno(err) - return - } - inode = n.newChild(ctx, st, out) - return inode, 0 -} - -// Link - FUSE call. Creates a hard link at "newPath" pointing to file -// "oldPath". -// -// Symlink-safe through use of Linkat(). -func (n *Node) Link(ctx context.Context, target fs.InodeEmbedder, name string, out *fuse.EntryOut) (inode *fs.Inode, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - n2 := toNode(target) - dirfd2, cName2, errno := n2.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd2) - - // Handle long file name (except in PlaintextNames mode) - rn := n.rootNode() - var err error - if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { - err = rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - errno = fs.ToErrno(err) - return - } - // Create "gocryptfs.longfile." link - err = unix.Linkat(dirfd2, cName2, dirfd, cName, 0) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create regular link - err = unix.Linkat(dirfd2, cName2, dirfd, cName, 0) - } - if err != nil { - errno = fs.ToErrno(err) - return - } - - st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - errno = fs.ToErrno(err) - return - } - inode = n.newChild(ctx, st, out) - return inode, 0 -} - -// Symlink - FUSE call. Create a symlink. -// -// Symlink-safe through use of Symlinkat. -func (n *Node) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (inode *fs.Inode, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // Make sure context is nil if we don't want to preserve the owner - rn := n.rootNode() - if !rn.args.PreserveOwner { - ctx = nil - } - - cTarget := target - if !rn.args.PlaintextNames { - // Symlinks are encrypted like file contents (GCM) and base64-encoded - cTarget = rn.encryptSymlinkTarget(target) - } - // Create ".name" file to store long file name (except in PlaintextNames mode) - var err error - ctx2 := toFuseCtx(ctx) - if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { - err = rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - errno = fs.ToErrno(err) - return - } - // Create "gocryptfs.longfile." symlink - err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, ctx2) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create symlink - err = syscallcompat.SymlinkatUser(cTarget, dirfd, cName, ctx2) - } - - st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - errno = fs.ToErrno(err) - return - } - // Report the plaintext size, not the encrypted blob size - st.Size = int64(len(target)) - - inode = n.newChild(ctx, st, out) - return inode, 0 -} - -// xfstests generic/013 now also exercises RENAME_EXCHANGE and RENAME_WHITEOUT, -// uncovering lots of problems with longnames -// -// Reject those flags with syscall.EINVAL. -// If we can handle the flags, this function returns 0. -func rejectRenameFlags(flags uint32) syscall.Errno { - // Normal rename, we can handle that - if flags == 0 { - return 0 - } - // We also can handle RENAME_NOREPLACE - if flags == syscallcompat.RENAME_NOREPLACE { - return 0 - } - // We cannot handle RENAME_EXCHANGE and RENAME_WHITEOUT yet. - // Needs extra code for .name files. - return syscall.EINVAL -} - -// Rename - FUSE call. -// This function is called on the PARENT DIRECTORY of `name`. -// -// Symlink-safe through Renameat(). -func (n *Node) Rename(ctx context.Context, name string, newParent fs.InodeEmbedder, newName string, flags uint32) (errno syscall.Errno) { - if errno = rejectRenameFlags(flags); errno != 0 { - return errno - } - - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - n2 := toNode(newParent) - dirfd2, cName2, errno := n2.prepareAtSyscall(newName) - if errno != 0 { - return - } - defer syscall.Close(dirfd2) - - // Easy case. - rn := n.rootNode() - if rn.args.PlaintextNames { - return fs.ToErrno(syscallcompat.Renameat2(dirfd, cName, dirfd2, cName2, uint(flags))) - } - // Long destination file name: create .name file - nameFileAlreadyThere := false - var err error - if nametransform.IsLongContent(cName2) { - err = rn.nameTransform.WriteLongNameAt(dirfd2, cName2, newName) - // Failure to write the .name file is expected when the target path already - // exists. Since hashes are pretty unique, there is no need to modify the - // .name file in this case, and we ignore the error. - if err == syscall.EEXIST { - nameFileAlreadyThere = true - } else if err != nil { - return fs.ToErrno(err) - } - } - // Actual rename - tlog.Debug.Printf("Renameat %d/%s -> %d/%s\n", dirfd, cName, dirfd2, cName2) - err = syscallcompat.Renameat2(dirfd, cName, dirfd2, cName2, uint(flags)) - if (flags&syscallcompat.RENAME_NOREPLACE == 0) && (err == syscall.ENOTEMPTY || err == syscall.EEXIST) { - // If an empty directory is overwritten we will always get an error as - // the "empty" directory will still contain gocryptfs.diriv. - // Interestingly, ext4 returns ENOTEMPTY while xfs returns EEXIST. - // We handle that by trying to fs.Rmdir() the target directory and trying - // again. - tlog.Debug.Printf("Rename: Handling ENOTEMPTY") - if n2.Rmdir(ctx, newName) == 0 { - err = syscallcompat.Renameat2(dirfd, cName, dirfd2, cName2, uint(flags)) - } - } - if err != nil { - if nametransform.IsLongContent(cName2) && nameFileAlreadyThere == false { - // Roll back .name creation unless the .name file was already there - nametransform.DeleteLongNameAt(dirfd2, cName2) - } - return fs.ToErrno(err) - } - if nametransform.IsLongContent(cName) { - nametransform.DeleteLongNameAt(dirfd, cName) - } - return 0 -} diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go deleted file mode 100644 index 0f60c74..0000000 --- a/internal/fusefrontend/node_api_check.go +++ /dev/null @@ -1,31 +0,0 @@ -package fusefrontend - -import ( - "github.com/hanwen/go-fuse/v2/fs" -) - -// Check that we have implemented the fs.Node* interfaces -var _ = (fs.NodeGetattrer)((*Node)(nil)) -var _ = (fs.NodeLookuper)((*Node)(nil)) -var _ = (fs.NodeReaddirer)((*Node)(nil)) -var _ = (fs.NodeCreater)((*Node)(nil)) -var _ = (fs.NodeMkdirer)((*Node)(nil)) -var _ = (fs.NodeRmdirer)((*Node)(nil)) -var _ = (fs.NodeUnlinker)((*Node)(nil)) -var _ = (fs.NodeReadlinker)((*Node)(nil)) -var _ = (fs.NodeOpener)((*Node)(nil)) -var _ = (fs.NodeOpendirer)((*Node)(nil)) -var _ = (fs.NodeSetattrer)((*Node)(nil)) -var _ = (fs.NodeStatfser)((*Node)(nil)) -var _ = (fs.NodeMknoder)((*Node)(nil)) -var _ = (fs.NodeLinker)((*Node)(nil)) -var _ = (fs.NodeSymlinker)((*Node)(nil)) -var _ = (fs.NodeRenamer)((*Node)(nil)) -var _ = (fs.NodeGetxattrer)((*Node)(nil)) -var _ = (fs.NodeSetxattrer)((*Node)(nil)) -var _ = (fs.NodeRemovexattrer)((*Node)(nil)) -var _ = (fs.NodeListxattrer)((*Node)(nil)) - -/* TODO -var _ = (fs.NodeCopyFileRanger)((*Node)(nil)) -*/ diff --git a/internal/fusefrontend/node_dir_ops.go b/internal/fusefrontend/node_dir_ops.go deleted file mode 100644 index 001c23a..0000000 --- a/internal/fusefrontend/node_dir_ops.go +++ /dev/null @@ -1,376 +0,0 @@ -package fusefrontend - -import ( - "context" - "fmt" - "io" - "path/filepath" - "runtime" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -const dsStoreName = ".DS_Store" - -// haveDsstore return true if one of the entries in "names" is ".DS_Store". -func haveDsstore(entries []fuse.DirEntry) bool { - for _, e := range entries { - if e.Name == dsStoreName { - return true - } - } - return false -} - -// mkdirWithIv - create a new directory and corresponding diriv file. dirfd -// should be a handle to the parent directory, cName is the name of the new -// directory and mode specifies the access permissions to use. -func (n *Node) mkdirWithIv(dirfd int, cName string, mode uint32, context *fuse.Context) error { - rn := n.rootNode() - // Between the creation of the directory and the creation of gocryptfs.diriv - // the directory is inconsistent. Take the lock to prevent other readers - // from seeing it. - rn.dirIVLock.Lock() - defer rn.dirIVLock.Unlock() - err := syscallcompat.MkdiratUser(dirfd, cName, mode, context) - if err != nil { - return err - } - dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_DIRECTORY|syscall.O_NOFOLLOW|syscallcompat.O_PATH, 0) - if err == nil { - // Create gocryptfs.diriv - err = nametransform.WriteDirIVAt(dirfd2) - syscall.Close(dirfd2) - } - if err != nil { - // Delete inconsistent directory (missing gocryptfs.diriv!) - err2 := syscallcompat.Unlinkat(dirfd, cName, unix.AT_REMOVEDIR) - if err2 != nil { - tlog.Warn.Printf("mkdirWithIv: rollback failed: %v", err2) - } - } - return err -} - -// Mkdir - FUSE call. Create a directory at "newPath" with permissions "mode". -// -// Symlink-safe through use of Mkdirat(). -func (n *Node) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return nil, errno - } - defer syscall.Close(dirfd) - - rn := n.rootNode() - var context *fuse.Context - if rn.args.PreserveOwner { - context = toFuseCtx(ctx) - } - - var st syscall.Stat_t - if rn.args.PlaintextNames { - err := syscallcompat.MkdiratUser(dirfd, cName, mode, context) - if err != nil { - return nil, fs.ToErrno(err) - } - var ust unix.Stat_t - err = syscallcompat.Fstatat(dirfd, cName, &ust, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - return nil, fs.ToErrno(err) - } - st = syscallcompat.Unix2syscall(ust) - } else { - // We need write and execute permissions to create gocryptfs.diriv. - // Also, we need read permissions to open the directory (to avoid - // race-conditions between getting and setting the mode). - origMode := mode - mode = mode | 0700 - - // Handle long file name - if nametransform.IsLongContent(cName) { - // Create ".name" - err := rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - return nil, fs.ToErrno(err) - } - - // Create directory - err = rn.mkdirWithIv(dirfd, cName, mode, context) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - return nil, fs.ToErrno(err) - } - } else { - err := rn.mkdirWithIv(dirfd, cName, mode, context) - if err != nil { - return nil, fs.ToErrno(err) - } - } - - fd, err := syscallcompat.Openat(dirfd, cName, - syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - tlog.Warn.Printf("Mkdir %q: Openat failed: %v", cName, err) - return nil, fs.ToErrno(err) - } - defer syscall.Close(fd) - - err = syscall.Fstat(fd, &st) - if err != nil { - tlog.Warn.Printf("Mkdir %q: Fstat failed: %v", cName, err) - return nil, fs.ToErrno(err) - } - - // Fix permissions - if origMode != mode { - // Preserve SGID bit if it was set due to inheritance. - origMode = uint32(st.Mode&^0777) | origMode - err = syscall.Fchmod(fd, origMode) - if err != nil { - tlog.Warn.Printf("Mkdir %q: Fchmod %#o -> %#o failed: %v", cName, mode, origMode, err) - } - } - } - - // Create child node - ch := n.newChild(ctx, &st, out) - - return ch, 0 -} - -// Readdir - FUSE call. -// -// This function is symlink-safe through use of openBackingDir() and -// ReadDirIVAt(). -func (n *Node) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { - parentDirFd, cDirName, errno := n.prepareAtSyscall("") - if errno != 0 { - return nil, errno - } - defer syscall.Close(parentDirFd) - - // Read ciphertext directory - fd, err := syscallcompat.Openat(parentDirFd, cDirName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - return nil, fs.ToErrno(err) - } - defer syscall.Close(fd) - cipherEntries, specialEntries, err := syscallcompat.GetdentsSpecial(fd) - if err != nil { - return nil, fs.ToErrno(err) - } - // Get DirIV (stays nil if PlaintextNames is used) - var cachedIV []byte - rn := n.rootNode() - if !rn.args.PlaintextNames { - // Read the DirIV from disk - cachedIV, err = nametransform.ReadDirIVAt(fd) - if err != nil { - tlog.Warn.Printf("OpenDir %q: could not read %s: %v", cDirName, nametransform.DirIVFilename, err) - return nil, syscall.EIO - } - } - // Decrypted directory entries - var plain []fuse.DirEntry - // Add "." and ".." - plain = append(plain, specialEntries...) - // Filter and decrypt filenames - for i := range cipherEntries { - cName := cipherEntries[i].Name - if n.IsRoot() && cName == configfile.ConfDefaultName { - // silently ignore "gocryptfs.conf" in the top level dir - continue - } - if rn.args.PlaintextNames { - plain = append(plain, cipherEntries[i]) - continue - } - if cName == nametransform.DirIVFilename { - // silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled - continue - } - // Handle long file name - isLong := nametransform.LongNameNone - if rn.args.LongNames { - isLong = nametransform.NameType(cName) - } - if isLong == nametransform.LongNameContent { - cNameLong, err := nametransform.ReadLongNameAt(fd, cName) - if err != nil { - tlog.Warn.Printf("OpenDir %q: invalid entry %q: Could not read .name: %v", - cDirName, cName, err) - rn.reportMitigatedCorruption(cName) - continue - } - cName = cNameLong - } else if isLong == nametransform.LongNameFilename { - // ignore "gocryptfs.longname.*.name" - continue - } - name, err := rn.nameTransform.DecryptName(cName, cachedIV) - if err != nil { - tlog.Warn.Printf("OpenDir %q: invalid entry %q: %v", - cDirName, cName, err) - rn.reportMitigatedCorruption(cName) - continue - } - // Override the ciphertext name with the plaintext name but reuse the rest - // of the structure - cipherEntries[i].Name = name - plain = append(plain, cipherEntries[i]) - } - - return fs.NewListDirStream(plain), 0 -} - -// Rmdir - FUSE call. -// -// Symlink-safe through Unlinkat() + AT_REMOVEDIR. -func (n *Node) Rmdir(ctx context.Context, name string) (code syscall.Errno) { - rn := n.rootNode() - p := filepath.Join(n.Path(), name) - parentDirFd, cName, err := rn.openBackingDir(p) - if err != nil { - return fs.ToErrno(err) - } - defer syscall.Close(parentDirFd) - if rn.args.PlaintextNames { - // Unlinkat with AT_REMOVEDIR is equivalent to Rmdir - err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) - return fs.ToErrno(err) - } - // Unless we are running as root, we need read, write and execute permissions - // to handle gocryptfs.diriv. - permWorkaround := false - var origMode uint32 - if !rn.args.PreserveOwner { - var st unix.Stat_t - err = syscallcompat.Fstatat(parentDirFd, cName, &st, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - return fs.ToErrno(err) - } - if st.Mode&0700 != 0700 { - tlog.Debug.Printf("Rmdir: permWorkaround") - permWorkaround = true - // This cast is needed on Darwin, where st.Mode is uint16. - origMode = uint32(st.Mode) - err = syscallcompat.FchmodatNofollow(parentDirFd, cName, origMode|0700) - if err != nil { - tlog.Debug.Printf("Rmdir: permWorkaround: chmod failed: %v", err) - return fs.ToErrno(err) - } - } - } - dirfd, err := syscallcompat.Openat(parentDirFd, cName, - syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - tlog.Debug.Printf("Rmdir: Open: %v", err) - return fs.ToErrno(err) - } - defer syscall.Close(dirfd) - // Undo the chmod if removing the directory failed. This must run before - // closing dirfd, so defer it after (defer is LIFO). - if permWorkaround { - defer func() { - if code != 0 { - err = unix.Fchmod(dirfd, origMode) - if err != nil { - tlog.Warn.Printf("Rmdir: permWorkaround: rollback failed: %v", err) - } - } - }() - } -retry: - // Check directory contents - children, err := syscallcompat.Getdents(dirfd) - if err == io.EOF { - // The directory is empty - tlog.Warn.Printf("Rmdir: %q: %s is missing", cName, nametransform.DirIVFilename) - err = unix.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) - return fs.ToErrno(err) - } - if err != nil { - tlog.Warn.Printf("Rmdir: Getdents: %v", err) - return fs.ToErrno(err) - } - // MacOS sprinkles .DS_Store files everywhere. This is hard to avoid for - // users, so handle it transparently here. - if runtime.GOOS == "darwin" && len(children) <= 2 && haveDsstore(children) { - err = unix.Unlinkat(dirfd, dsStoreName, 0) - if err != nil { - tlog.Warn.Printf("Rmdir: failed to delete blocking file %q: %v", dsStoreName, err) - return fs.ToErrno(err) - } - tlog.Warn.Printf("Rmdir: had to delete blocking file %q", dsStoreName) - goto retry - } - // If the directory is not empty besides gocryptfs.diriv, do not even - // attempt the dance around gocryptfs.diriv. - if len(children) > 1 { - return fs.ToErrno(syscall.ENOTEMPTY) - } - // Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ" - tmpName := fmt.Sprintf("%s.rmdir.%d", nametransform.DirIVFilename, cryptocore.RandUint64()) - tlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpName) - // The directory is in an inconsistent state between rename and rmdir. - // Protect against concurrent readers. - rn.dirIVLock.Lock() - defer rn.dirIVLock.Unlock() - err = syscallcompat.Renameat(dirfd, nametransform.DirIVFilename, - parentDirFd, tmpName) - if err != nil { - tlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v", - nametransform.DirIVFilename, tmpName, err) - return fs.ToErrno(err) - } - // Actual Rmdir - err = syscallcompat.Unlinkat(parentDirFd, cName, unix.AT_REMOVEDIR) - if err != nil { - // This can happen if another file in the directory was created in the - // meantime, undo the rename - err2 := syscallcompat.Renameat(parentDirFd, tmpName, - dirfd, nametransform.DirIVFilename) - if err2 != nil { - tlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2) - } - return fs.ToErrno(err) - } - // Delete "gocryptfs.diriv.rmdir.XYZ" - err = syscallcompat.Unlinkat(parentDirFd, tmpName, 0) - if err != nil { - tlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err) - } - // Delete .name file - if nametransform.IsLongContent(cName) { - nametransform.DeleteLongNameAt(parentDirFd, cName) - } - return 0 -} - -// Opendir is a FUSE call to check if the directory can be opened. -func (n *Node) Opendir(ctx context.Context) (errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // Open backing directory - fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - return fs.ToErrno(err) - } - syscall.Close(fd) - return 0 -} diff --git a/internal/fusefrontend/node_helpers.go b/internal/fusefrontend/node_helpers.go deleted file mode 100644 index b2f1d4a..0000000 --- a/internal/fusefrontend/node_helpers.go +++ /dev/null @@ -1,177 +0,0 @@ -package fusefrontend - -import ( - "context" - "log" - "path/filepath" - "sync/atomic" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// toFuseCtx tries to extract a fuse.Context from a generic context.Context. -func toFuseCtx(ctx context.Context) (ctx2 *fuse.Context) { - if ctx == nil { - return nil - } - if caller, ok := fuse.FromContext(ctx); ok { - ctx2 = &fuse.Context{ - Caller: *caller, - } - } - return ctx2 -} - -// toNode casts a generic fs.InodeEmbedder into *Node. Also handles *RootNode -// by return rn.Node. -func toNode(op fs.InodeEmbedder) *Node { - if r, ok := op.(*RootNode); ok { - return &r.Node - } - return op.(*Node) -} - -// readlink reads and decrypts a symlink. Used by Readlink, Getattr, Lookup. -func (n *Node) readlink(dirfd int, cName string) (out []byte, errno syscall.Errno) { - cTarget, err := syscallcompat.Readlinkat(dirfd, cName) - if err != nil { - return nil, fs.ToErrno(err) - } - rn := n.rootNode() - if rn.args.PlaintextNames { - return []byte(cTarget), 0 - } - // Symlinks are encrypted like file contents (GCM) and base64-encoded - target, err := rn.decryptSymlinkTarget(cTarget) - if err != nil { - tlog.Warn.Printf("Readlink %q: decrypting target failed: %v", cName, err) - return nil, syscall.EIO - } - return []byte(target), 0 -} - -// translateSize translates the ciphertext size in `out` into plaintext size. -// Handles regular files & symlinks (and finds out what is what by looking at -// `out.Mode`). -func (n *Node) translateSize(dirfd int, cName string, out *fuse.Attr) { - if out.IsRegular() { - rn := n.rootNode() - out.Size = rn.contentEnc.CipherSizeToPlainSize(out.Size) - } else if out.IsSymlink() { - target, _ := n.readlink(dirfd, cName) - out.Size = uint64(len(target)) - } -} - -// Path returns the relative plaintext path of this node -func (n *Node) Path() string { - return n.Inode.Path(n.Root()) -} - -// rootNode returns the Root Node of the filesystem. -func (n *Node) rootNode() *RootNode { - return n.Root().Operations().(*RootNode) -} - -// prepareAtSyscall returns a (dirfd, cName) pair that can be used -// with the "___at" family of system calls (openat, fstatat, unlinkat...) to -// access the backing encrypted directory. -// -// If you pass a `child` file name, the (dirfd, cName) pair will refer to -// a child of this node. -// If `child` is empty, the (dirfd, cName) pair refers to this node itself. For -// the root node, that means (dirfd, "."). -func (n *Node) prepareAtSyscall(child string) (dirfd int, cName string, errno syscall.Errno) { - rn := n.rootNode() - // all filesystem operations go through prepareAtSyscall(), so this is a - // good place to reset the idle marker. - atomic.StoreUint32(&rn.IsIdle, 0) - - // root node itself is special - if child == "" && n.IsRoot() { - var err error - dirfd, cName, err = rn.openBackingDir("") - if err != nil { - errno = fs.ToErrno(err) - } - return - } - - // normal node itself can be converted to child of parent node - if child == "" { - name, p1 := n.Parent() - if p1 == nil || name == "" { - return -1, "", syscall.ENOENT - } - p2 := toNode(p1.Operations()) - return p2.prepareAtSyscall(name) - } - - // Cache lookup - // TODO make it work for plaintextnames as well? - cacheable := (!rn.args.PlaintextNames) - if cacheable { - var iv []byte - dirfd, iv = rn.dirCache.Lookup(n) - if dirfd > 0 { - cName, err := rn.nameTransform.EncryptAndHashName(child, iv) - if err != nil { - return -1, "", fs.ToErrno(err) - } - return dirfd, cName, 0 - } - } - - // Slowpath - if child == "" { - log.Panicf("BUG: child name is empty - this cannot happen") - } - p := filepath.Join(n.Path(), child) - if rn.isFiltered(p) { - errno = syscall.EPERM - return - } - dirfd, cName, err := rn.openBackingDir(p) - if err != nil { - errno = fs.ToErrno(err) - return - } - - // Cache store - if cacheable { - // TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work? - iv, err := nametransform.ReadDirIVAt(dirfd) - if err != nil { - syscall.Close(dirfd) - return -1, "", fs.ToErrno(err) - } - rn.dirCache.Store(n, dirfd, iv) - } - return -} - -// newChild attaches a new child inode to n. -// The passed-in `st` will be modified to get a unique inode number -// (or, in `-sharedstorage` mode, the inode number will be set to zero). -func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.EntryOut) *fs.Inode { - rn := n.rootNode() - // Get stable inode number based on underlying (device,ino) pair - // (or set to zero in case of `-sharestorage`) - rn.inoMap.TranslateStat(st) - out.Attr.FromStat(st) - // Create child node - id := fs.StableAttr{ - Mode: uint32(st.Mode), - Gen: 1, - Ino: st.Ino, - } - node := &Node{} - return n.NewInode(ctx, node, id) -} diff --git a/internal/fusefrontend/node_open_create.go b/internal/fusefrontend/node_open_create.go deleted file mode 100644 index 8b31932..0000000 --- a/internal/fusefrontend/node_open_create.go +++ /dev/null @@ -1,108 +0,0 @@ -package fusefrontend - -import ( - "context" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Open - FUSE call. Open already-existing file. -// -// Symlink-safe through Openat(). -func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - rn := n.rootNode() - newFlags := rn.mangleOpenFlags(flags) - // Taking this lock makes sure we don't race openWriteOnlyFile() - rn.openWriteOnlyLock.RLock() - defer rn.openWriteOnlyLock.RUnlock() - - if rn.args.KernelCache { - fuseFlags = fuse.FOPEN_KEEP_CACHE - } - - // Open backing file - fd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0) - // Handle a few specific errors - if err != nil { - if err == syscall.EMFILE { - var lim syscall.Rlimit - syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) - tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur) - } - if err == syscall.EACCES && (int(flags)&syscall.O_ACCMODE) == syscall.O_WRONLY { - fd, err = rn.openWriteOnlyFile(dirfd, cName, newFlags) - } - } - // Could not handle the error? Bail out - if err != nil { - errno = fs.ToErrno(err) - return - } - fh, _, errno = NewFile(fd, cName, rn) - return fh, fuseFlags, errno -} - -// Create - FUSE call. Creates a new file. -// -// Symlink-safe through the use of Openat(). -func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *fs.Inode, fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - var err error - fd := -1 - // Make sure context is nil if we don't want to preserve the owner - rn := n.rootNode() - if !rn.args.PreserveOwner { - ctx = nil - } - newFlags := rn.mangleOpenFlags(flags) - // Handle long file name - ctx2 := toFuseCtx(ctx) - if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { - // Create ".name" - err = rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - return nil, nil, 0, fs.ToErrno(err) - } - // Create content - fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, ctx2) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create content, normal (short) file name - fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, ctx2) - } - if err != nil { - // xfstests generic/488 triggers this - if err == syscall.EMFILE { - var lim syscall.Rlimit - syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) - tlog.Warn.Printf("Create %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur) - } - return nil, nil, 0, fs.ToErrno(err) - } - - fh, st, errno := NewFile(fd, cName, rn) - if errno != 0 { - return - } - inode = n.newChild(ctx, st, out) - return inode, fh, fuseFlags, errno -} diff --git a/internal/fusefrontend/node_xattr.go b/internal/fusefrontend/node_xattr.go deleted file mode 100644 index ceb10f1..0000000 --- a/internal/fusefrontend/node_xattr.go +++ /dev/null @@ -1,170 +0,0 @@ -// Package fusefrontend interfaces directly with the go-fuse library. -package fusefrontend - -import ( - "bytes" - "context" - "strings" - "syscall" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// -1 as uint32 -const minus1 = ^uint32(0) - -// xattr names are encrypted like file names, but with a fixed IV. -// Padded with "_xx" for length 16. -var xattrNameIV = []byte("xattr_name_iv_xx") - -// We store encrypted xattrs under this prefix plus the base64-encoded -// encrypted original name. -var xattrStorePrefix = "user.gocryptfs." - -// We get one read of this xattr for each write - -// see https://github.com/rfjakob/gocryptfs/issues/515 for details. -var xattrCapability = "security.capability" - -// isAcl returns true if the attribute name is for storing ACLs -// -// ACLs are passed through without encryption -func isAcl(attr string) bool { - return attr == "system.posix_acl_access" || attr == "system.posix_acl_default" -} - -// GetXAttr - FUSE call. Reads the value of extended attribute "attr". -// -// This function is symlink-safe through Fgetxattr. -func (n *Node) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) { - rn := n.rootNode() - // If we are not mounted with -suid, reading the capability xattr does not - // make a lot of sense, so reject the request and gain a massive speedup. - // See https://github.com/rfjakob/gocryptfs/issues/515 . - if !rn.args.Suid && attr == xattrCapability { - // Returning EOPNOTSUPP is what we did till - // ca9e912a28b901387e1dbb85f6c531119f2d5ef2 "fusefrontend: drop xattr user namespace restriction" - // and it did not cause trouble. Seems cleaner than saying ENODATA. - return 0, syscall.EOPNOTSUPP - } - var data []byte - // ACLs are passed through without encryption - if isAcl(attr) { - var errno syscall.Errno - data, errno = n.getXAttr(attr) - if errno != 0 { - return minus1, errno - } - } else { - // encrypted user xattr - cAttr, err := rn.encryptXattrName(attr) - if err != nil { - return minus1, syscall.EIO - } - cData, errno := n.getXAttr(cAttr) - if errno != 0 { - return 0, errno - } - data, err = rn.decryptXattrValue(cData) - if err != nil { - tlog.Warn.Printf("GetXAttr: %v", err) - return minus1, syscall.EIO - } - } - // Caller passes size zero to find out how large their buffer should be - if len(dest) == 0 { - return uint32(len(data)), 0 - } - if len(dest) < len(data) { - return minus1, syscall.ERANGE - } - l := copy(dest, data) - return uint32(l), 0 -} - -// SetXAttr - FUSE call. Set extended attribute. -// -// This function is symlink-safe through Fsetxattr. -func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno { - rn := n.rootNode() - flags = uint32(filterXattrSetFlags(int(flags))) - - // ACLs are passed through without encryption - if isAcl(attr) { - // result of setting an acl depends on the user doing it - var context *fuse.Context - if rn.args.PreserveOwner { - context = toFuseCtx(ctx) - } - return n.setXAttr(context, attr, data, flags) - } - - cAttr, err := rn.encryptXattrName(attr) - if err != nil { - return syscall.EINVAL - } - cData := rn.encryptXattrValue(data) - return n.setXAttr(nil, cAttr, cData, flags) -} - -// RemoveXAttr - FUSE call. -// -// This function is symlink-safe through Fremovexattr. -func (n *Node) Removexattr(ctx context.Context, attr string) syscall.Errno { - rn := n.rootNode() - - // ACLs are passed through without encryption - if isAcl(attr) { - return n.removeXAttr(attr) - } - - cAttr, err := rn.encryptXattrName(attr) - if err != nil { - return syscall.EINVAL - } - return n.removeXAttr(cAttr) -} - -// ListXAttr - FUSE call. Lists extended attributes on the file at "relPath". -// -// This function is symlink-safe through Flistxattr. -func (n *Node) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) { - cNames, errno := n.listXAttr() - if errno != 0 { - return 0, errno - } - rn := n.rootNode() - var buf bytes.Buffer - for _, curName := range cNames { - // ACLs are passed through without encryption - if isAcl(curName) { - buf.WriteString(curName + "\000") - continue - } - if !strings.HasPrefix(curName, xattrStorePrefix) { - continue - } - name, err := rn.decryptXattrName(curName) - if err != nil { - tlog.Warn.Printf("ListXAttr: invalid xattr name %q: %v", curName, err) - rn.reportMitigatedCorruption(curName) - continue - } - // We *used to* encrypt ACLs, which caused a lot of problems. - if isAcl(name) { - tlog.Warn.Printf("ListXAttr: ignoring deprecated encrypted ACL %q = %q", curName, name) - rn.reportMitigatedCorruption(curName) - continue - } - buf.WriteString(name + "\000") - } - // Caller passes size zero to find out how large their buffer should be - if len(dest) == 0 { - return uint32(buf.Len()), 0 - } - if buf.Len() > len(dest) { - return minus1, syscall.ERANGE - } - return uint32(copy(dest, buf.Bytes())), 0 -} diff --git a/internal/fusefrontend/node_xattr_darwin.go b/internal/fusefrontend/node_xattr_darwin.go deleted file mode 100644 index 0f61a5d..0000000 --- a/internal/fusefrontend/node_xattr_darwin.go +++ /dev/null @@ -1,107 +0,0 @@ -package fusefrontend - -import ( - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/syscallcompat" -) - -// On Darwin we have to unset XATTR_NOSECURITY 0x0008 -func filterXattrSetFlags(flags int) int { - // See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html - const XATTR_NOSECURITY = 0x0008 - - return flags &^ XATTR_NOSECURITY -} - -func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // O_NONBLOCK to not block on FIFOs. - fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) - if err != nil { - return nil, fs.ToErrno(err) - } - defer syscall.Close(fd) - - cData, err := syscallcompat.Fgetxattr(fd, cAttr) - if err != nil { - return nil, fs.ToErrno(err) - } - - return cData, 0 -} - -func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // O_NONBLOCK to not block on FIFOs. - fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) - // Directories cannot be opened read-write. Retry. - if err == syscall.EISDIR { - fd, err = syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK, 0) - } - if err != nil { - fs.ToErrno(err) - } - defer syscall.Close(fd) - - err = unix.Fsetxattr(fd, cAttr, cData, int(flags)) - return fs.ToErrno(err) -} - -func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // O_NONBLOCK to not block on FIFOs. - fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) - // Directories cannot be opened read-write. Retry. - if err == syscall.EISDIR { - fd, err = syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK, 0) - } - if err != nil { - return fs.ToErrno(err) - } - defer syscall.Close(fd) - - err = unix.Fremovexattr(fd, cAttr) - return fs.ToErrno(err) -} - -func (n *Node) listXAttr() (out []string, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - // O_NONBLOCK to not block on FIFOs. - fd, err := syscallcompat.Openat(dirfd, cName, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) - if err != nil { - return nil, fs.ToErrno(err) - } - defer syscall.Close(fd) - - cNames, err := syscallcompat.Flistxattr(fd) - if err != nil { - return nil, fs.ToErrno(err) - } - return cNames, 0 -} diff --git a/internal/fusefrontend/node_xattr_linux.go b/internal/fusefrontend/node_xattr_linux.go deleted file mode 100644 index 9698282..0000000 --- a/internal/fusefrontend/node_xattr_linux.go +++ /dev/null @@ -1,70 +0,0 @@ -package fusefrontend - -import ( - "fmt" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/syscallcompat" -) - -func filterXattrSetFlags(flags int) int { - return flags -} - -func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName) - cData, err := syscallcompat.Lgetxattr(procPath, cAttr) - if err != nil { - return nil, fs.ToErrno(err) - } - return cData, 0 -} - -func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName) - - return fs.ToErrno(syscallcompat.LsetxattrUser(procPath, cAttr, cData, int(flags), context)) -} - -func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName) - return fs.ToErrno(unix.Lremovexattr(procPath, cAttr)) -} - -func (n *Node) listXAttr() (out []string, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName) - cNames, err := syscallcompat.Llistxattr(procPath) - if err != nil { - return nil, fs.ToErrno(err) - } - return cNames, 0 -} diff --git a/internal/fusefrontend/openbackingdir_test.go b/internal/fusefrontend/openbackingdir_test.go deleted file mode 100644 index ba4014f..0000000 --- a/internal/fusefrontend/openbackingdir_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package fusefrontend - -import ( - "strings" - "syscall" - "testing" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func TestOpenBackingDir(t *testing.T) { - cipherdir := test_helpers.InitFS(t) - t.Logf("cipherdir = %q", cipherdir) - args := Args{ - Cipherdir: cipherdir, - } - rn := newTestFS(args) - out := &fuse.EntryOut{} - - child, errno := rn.Mkdir(nil, "dir1", 0700, out) - if errno != 0 { - t.Fatal(errno) - } - rn.AddChild("dir1", child, false) - dir1 := toNode(child.Operations()) - _, errno = dir1.Mkdir(nil, "dir2", 0700, out) - if errno != 0 { - t.Fatal(errno) - } - - dirfd, cName, err := rn.openBackingDir("") - if err != nil { - t.Fatal(err) - } - if cName != "." { - t.Fatal("cName should be .") - } - syscall.Close(dirfd) - - // Again, but populate the cache for "" by looking up a non-existing file - rn.Lookup(nil, "xyz1234", &fuse.EntryOut{}) - dirfd, cName, err = rn.openBackingDir("") - if err != nil { - t.Fatal(err) - } - if cName != "." { - t.Fatal("cName should be .") - } - - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Error(err) - } - err = syscallcompat.Faccessat(dirfd, ".", unix.R_OK) - if err != nil { - t.Error(err) - } - syscall.Close(dirfd) - - dirfd, cName, err = rn.openBackingDir("dir1") - if err != nil { - t.Fatal(err) - } - if cName == "" { - t.Fatal("cName should not be empty") - } - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Error(err) - } - syscall.Close(dirfd) - - dirfd, cName, err = rn.openBackingDir("dir1/dir2") - if err != nil { - t.Fatal(err) - } - if cName == "" { - t.Fatal("cName should not be empty") - } - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Errorf("Faccessat(%d, %q): %v", dirfd, cName, err) - } - syscall.Close(dirfd) - - n255 := strings.Repeat("n", 255) - dir1.Mkdir(nil, n255, 0700, out) - dirfd, cName, err = rn.openBackingDir("dir1/" + n255) - if err != nil { - t.Fatal(err) - } - if cName == "" { - t.Fatal("cName should not be empty") - } - if len(cName) >= 255 { - t.Fatalf("cName is too long: %q", cName) - } - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Errorf("Faccessat(%d, %q): %v", dirfd, cName, err) - } - syscall.Close(dirfd) -} - -func TestOpenBackingDirPlaintextNames(t *testing.T) { - cipherdir := test_helpers.InitFS(t, "-plaintextnames") - args := Args{ - Cipherdir: cipherdir, - PlaintextNames: true, - } - fs := newTestFS(args) - out := &fuse.EntryOut{} - - _, errno := fs.Mkdir(nil, "dir1", 0700, out) - if errno != 0 { - t.Fatal(errno) - } - _, errno = fs.Mkdir(nil, "dir1/dir2", 0700, out) - if errno != 0 { - t.Fatal(errno) - } - - dirfd, cName, err := fs.openBackingDir("") - if err != nil { - t.Fatal(err) - } - if cName != "." { - t.Fatal("cName should be .") - } - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Error(err) - } - err = syscallcompat.Faccessat(dirfd, ".", unix.R_OK) - if err != nil { - t.Error(err) - } - syscall.Close(dirfd) - - dirfd, cName, err = fs.openBackingDir("dir1") - if err != nil { - t.Fatal(err) - } - if cName != "dir1" { - t.Fatalf("wrong cName: %q", cName) - } - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Error(err) - } - syscall.Close(dirfd) - - dirfd, cName, err = fs.openBackingDir("dir1/dir2") - if err != nil { - t.Fatal(err) - } - if cName != "dir2" { - t.Fatalf("wrong cName: %q", cName) - } - err = syscallcompat.Faccessat(dirfd, cName, unix.R_OK) - if err != nil { - t.Error(err) - } - syscall.Close(dirfd) -} diff --git a/internal/fusefrontend/root_node.go b/internal/fusefrontend/root_node.go deleted file mode 100644 index a830cc4..0000000 --- a/internal/fusefrontend/root_node.go +++ /dev/null @@ -1,335 +0,0 @@ -package fusefrontend - -import ( - "os" - "path/filepath" - "strings" - "sync" - "syscall" - "time" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/inomap" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/serialize_reads" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// RootNode is the root of the filesystem tree of Nodes. -type RootNode struct { - Node - // args stores configuration arguments - args Args - // dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified - // Readers must RLock() it to prevent them from seeing intermediate - // states - dirIVLock sync.RWMutex - // Filename encryption helper - nameTransform nametransform.NameTransformer - // Content encryption helper - contentEnc *contentenc.ContentEnc - // This lock is used by openWriteOnlyFile() to block concurrent opens while - // it relaxes the permissions on a file. - openWriteOnlyLock sync.RWMutex - // MitigatedCorruptions is used to report data corruption that is internally - // mitigated by ignoring the corrupt item. For example, when OpenDir() finds - // a corrupt filename, we still return the other valid filenames. - // The corruption is logged to syslog to inform the user, and in addition, - // the corrupt filename is logged to this channel via - // reportMitigatedCorruption(). - // "gocryptfs -fsck" reads from the channel to also catch these transparently- - // mitigated corruptions. - MitigatedCorruptions chan string - // IsIdle flag is set to zero each time fs.isFiltered() is called - // (uint32 so that it can be reset with CompareAndSwapUint32). - // When -idle was used when mounting, idleMonitor() sets it to 1 - // periodically. - IsIdle uint32 - // dirCache caches directory fds - dirCache dirCache - // inoMap translates inode numbers from different devices to unique inode - // numbers. - inoMap inomap.TranslateStater -} - -func NewRootNode(args Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *RootNode { - if args.SerializeReads { - serialize_reads.InitSerializer() - } - if len(args.Exclude) > 0 { - tlog.Warn.Printf("Forward mode does not support -exclude") - } - rn := &RootNode{ - args: args, - nameTransform: n, - contentEnc: c, - inoMap: inomap.New(), - } - // In `-sharedstorage` mode we always set the inode number to zero. - // This makes go-fuse generate a new inode number for each lookup. - if args.SharedStorage { - rn.inoMap = &inomap.TranslateStatZero{} - } - return rn -} - -// main.doMount() calls this after unmount -func (rn *RootNode) AfterUnmount() { - // print stats before we exit - rn.dirCache.stats() -} - -// mangleOpenFlags is used by Create() and Open() to convert the open flags the user -// wants to the flags we internally use to open the backing file. -// The returned flags always contain O_NOFOLLOW. -func (rn *RootNode) mangleOpenFlags(flags uint32) (newFlags int) { - newFlags = int(flags) - // Convert WRONLY to RDWR. We always need read access to do read-modify-write cycles. - if (newFlags & syscall.O_ACCMODE) == syscall.O_WRONLY { - newFlags = newFlags ^ os.O_WRONLY | os.O_RDWR - } - // We also cannot open the file in append mode, we need to seek back for RMW - newFlags = newFlags &^ os.O_APPEND - // O_DIRECT accesses must be aligned in both offset and length. Due to our - // crypto header, alignment will be off, even if userspace makes aligned - // accesses. Running xfstests generic/013 on ext4 used to trigger lots of - // EINVAL errors due to missing alignment. Just fall back to buffered IO. - newFlags = newFlags &^ syscallcompat.O_DIRECT - // Create and Open are two separate FUSE operations, so O_CREAT should not - // be part of the open flags. - newFlags = newFlags &^ syscall.O_CREAT - // We always want O_NOFOLLOW to be safe against symlink races - newFlags |= syscall.O_NOFOLLOW - return newFlags -} - -// reportMitigatedCorruption is used to report a corruption that was transparently -// mitigated and did not return an error to the user. Pass the name of the corrupt -// item (filename for OpenDir(), xattr name for ListXAttr() etc). -// See the MitigatedCorruptions channel for more info. -func (rn *RootNode) reportMitigatedCorruption(item string) { - if rn.MitigatedCorruptions == nil { - return - } - select { - case rn.MitigatedCorruptions <- item: - case <-time.After(1 * time.Second): - tlog.Warn.Printf("BUG: reportCorruptItem: timeout") - //debug.PrintStack() - return - } -} - -// isFiltered - check if plaintext "path" should be forbidden -// -// Prevents name clashes with internal files when file names are not encrypted -func (rn *RootNode) isFiltered(path string) bool { - if !rn.args.PlaintextNames { - return false - } - // gocryptfs.conf in the root directory is forbidden - if path == configfile.ConfDefaultName { - tlog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n", - configfile.ConfDefaultName) - return true - } - // Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames - // are exclusive - return false -} - -// decryptSymlinkTarget: "cData64" is base64-decoded and decrypted -// like file contents (GCM). -// The empty string decrypts to the empty string. -// -// This function does not do any I/O and is hence symlink-safe. -func (rn *RootNode) decryptSymlinkTarget(cData64 string) (string, error) { - if cData64 == "" { - return "", nil - } - cData, err := rn.nameTransform.B64DecodeString(cData64) - if err != nil { - return "", err - } - data, err := rn.contentEnc.DecryptBlock([]byte(cData), 0, nil) - if err != nil { - return "", err - } - return string(data), nil -} - -// Due to RMW, we always need read permissions on the backing file. This is a -// problem if the file permissions do not allow reading (i.e. 0200 permissions). -// This function works around that problem by chmod'ing the file, obtaining a fd, -// and chmod'ing it back. -func (rn *RootNode) openWriteOnlyFile(dirfd int, cName string, newFlags int) (rwFd int, err error) { - woFd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0) - if err != nil { - return - } - defer syscall.Close(woFd) - var st syscall.Stat_t - err = syscall.Fstat(woFd, &st) - if err != nil { - return - } - // The cast to uint32 fixes a build failure on Darwin, where st.Mode is uint16. - perms := uint32(st.Mode) - // Verify that we don't have read permissions - if perms&0400 != 0 { - tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms) - err = syscall.EPERM - return - } - // Upgrade the lock to block other Open()s and downgrade again on return - rn.openWriteOnlyLock.RUnlock() - rn.openWriteOnlyLock.Lock() - defer func() { - rn.openWriteOnlyLock.Unlock() - rn.openWriteOnlyLock.RLock() - }() - // Relax permissions and revert on return - err = syscall.Fchmod(woFd, perms|0400) - if err != nil { - tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err) - return - } - defer func() { - err2 := syscall.Fchmod(woFd, perms) - if err2 != nil { - tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2) - } - }() - return syscallcompat.Openat(dirfd, cName, newFlags, 0) -} - -// openBackingDir opens the parent ciphertext directory of plaintext path -// "relPath". It returns the dirfd (opened with O_PATH) and the encrypted -// basename. -// -// The caller should then use Openat(dirfd, cName, ...) and friends. -// For convenience, if relPath is "", cName is going to be ".". -// -// openBackingDir is secure against symlink races by using Openat and -// ReadDirIVAt. -// -// Retries on EINTR. -func (rn *RootNode) openBackingDir(relPath string) (dirfd int, cName string, err error) { - dirRelPath := nametransform.Dir(relPath) - // With PlaintextNames, we don't need to read DirIVs. Easy. - if rn.args.PlaintextNames { - dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, dirRelPath) - if err != nil { - return -1, "", err - } - // If relPath is empty, cName is ".". - cName = filepath.Base(relPath) - return dirfd, cName, nil - } - // Open cipherdir (following symlinks) - dirfd, err = syscallcompat.Open(rn.args.Cipherdir, syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) - if err != nil { - return -1, "", err - } - // If relPath is empty, cName is ".". - if relPath == "" { - return dirfd, ".", nil - } - // Walk the directory tree - parts := strings.Split(relPath, "/") - for i, name := range parts { - iv, err := nametransform.ReadDirIVAt(dirfd) - if err != nil { - syscall.Close(dirfd) - return -1, "", err - } - cName, err = rn.nameTransform.EncryptAndHashName(name, iv) - if err != nil { - syscall.Close(dirfd) - return -1, "", err - } - // Last part? We are done. - if i == len(parts)-1 { - break - } - // Not the last part? Descend into next directory. - dirfd2, err := syscallcompat.Openat(dirfd, cName, syscall.O_NOFOLLOW|syscall.O_DIRECTORY|syscallcompat.O_PATH, 0) - syscall.Close(dirfd) - if err != nil { - return -1, "", err - } - dirfd = dirfd2 - } - return dirfd, cName, nil -} - -// encryptSymlinkTarget: "data" is encrypted like file contents (GCM) -// and base64-encoded. -// The empty string encrypts to the empty string. -// -// Symlink-safe because it does not do any I/O. -func (rn *RootNode) encryptSymlinkTarget(data string) (cData64 string) { - if data == "" { - return "" - } - cData := rn.contentEnc.EncryptBlock([]byte(data), 0, nil) - cData64 = rn.nameTransform.B64EncodeToString(cData) - return cData64 -} - -// encryptXattrValue encrypts the xattr value "data". -// The data is encrypted like a file content block, but without binding it to -// a file location (block number and file id are set to zero). -// Special case: an empty value is encrypted to an empty value. -func (rn *RootNode) encryptXattrValue(data []byte) (cData []byte) { - if len(data) == 0 { - return []byte{} - } - return rn.contentEnc.EncryptBlock(data, 0, nil) -} - -// decryptXattrValue decrypts the xattr value "cData". -func (rn *RootNode) decryptXattrValue(cData []byte) (data []byte, err error) { - if len(cData) == 0 { - return []byte{}, nil - } - data, err1 := rn.contentEnc.DecryptBlock([]byte(cData), 0, nil) - if err1 == nil { - return data, nil - } - // This backward compatibility is needed to support old - // file systems having xattr values base64-encoded. - cData, err2 := rn.nameTransform.B64DecodeString(string(cData)) - if err2 != nil { - // Looks like the value was not base64-encoded, but just corrupt. - // Return the original decryption error: err1 - return nil, err1 - } - return rn.contentEnc.DecryptBlock([]byte(cData), 0, nil) -} - -// encryptXattrName transforms "user.foo" to "user.gocryptfs.a5sAd4XAa47f5as6dAf" -func (rn *RootNode) encryptXattrName(attr string) (string, error) { - // xattr names are encrypted like file names, but with a fixed IV. - cAttr, err := rn.nameTransform.EncryptName(attr, xattrNameIV) - if err != nil { - return "", err - } - return xattrStorePrefix + cAttr, nil -} - -func (rn *RootNode) decryptXattrName(cAttr string) (attr string, err error) { - // Reject anything that does not start with "user.gocryptfs." - if !strings.HasPrefix(cAttr, xattrStorePrefix) { - return "", syscall.EINVAL - } - // Strip "user.gocryptfs." prefix - cAttr = cAttr[len(xattrStorePrefix):] - attr, err = rn.nameTransform.DecryptName(cAttr, xattrNameIV) - if err != nil { - return "", err - } - return attr, nil -} diff --git a/internal/fusefrontend/xattr_unit_test.go b/internal/fusefrontend/xattr_unit_test.go deleted file mode 100644 index a0cf4c8..0000000 --- a/internal/fusefrontend/xattr_unit_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package fusefrontend - -// This file is named "xattr_unit_test.go" because there is also a -// "xattr_integration_test.go" in the test/xattr package. - -import ( - "testing" - "time" - - "github.com/hanwen/go-fuse/v2/fs" - - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/nametransform" -) - -func newTestFS(args Args) *RootNode { - // Init crypto backend - key := make([]byte, cryptocore.KeyLen) - cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true, false) - cEnc := contentenc.New(cCore, contentenc.DefaultBS, false) - n := nametransform.New(cCore.EMECipher, true, true) - rn := NewRootNode(args, cEnc, n) - oneSec := time.Second - options := &fs.Options{ - EntryTimeout: &oneSec, - AttrTimeout: &oneSec, - } - fs.NewNodeFS(rn, options) - return rn -} - -func TestEncryptDecryptXattrName(t *testing.T) { - fs := newTestFS(Args{}) - attr1 := "user.foo123456789" - cAttr, err := fs.encryptXattrName(attr1) - if err != nil { - t.Fatal(err) - } - t.Logf("cAttr=%v", cAttr) - attr2, err := fs.decryptXattrName(cAttr) - if attr1 != attr2 || err != nil { - t.Fatalf("Decrypt mismatch: %v != %v", attr1, attr2) - } -} diff --git a/internal/fusefrontend_reverse/ctlsock_interface.go b/internal/fusefrontend_reverse/ctlsock_interface.go deleted file mode 100644 index 2157044..0000000 --- a/internal/fusefrontend_reverse/ctlsock_interface.go +++ /dev/null @@ -1,42 +0,0 @@ -package fusefrontend_reverse - -import ( - "path/filepath" - "strings" - - "golang.org/x/sys/unix" - - "github.com/rfjakob/gocryptfs/internal/ctlsocksrv" - "github.com/rfjakob/gocryptfs/internal/pathiv" -) - -// Verify that the interface is implemented. -var _ ctlsocksrv.Interface = &RootNode{} - -// EncryptPath implements ctlsock.Backend. -// This is used for the control socket and for the "-exclude" logic. -func (rn *RootNode) EncryptPath(plainPath string) (string, error) { - if rn.args.PlaintextNames || plainPath == "" { - return plainPath, nil - } - cipherPath := "" - parts := strings.Split(plainPath, "/") - for _, part := range parts { - dirIV := pathiv.Derive(cipherPath, pathiv.PurposeDirIV) - encryptedPart, err := rn.nameTransform.EncryptName(part, dirIV) - if err != nil { - return "", err - } - if rn.args.LongNames && len(encryptedPart) > unix.NAME_MAX { - encryptedPart = rn.nameTransform.HashLongName(encryptedPart) - } - cipherPath = filepath.Join(cipherPath, encryptedPart) - } - return cipherPath, nil -} - -// DecryptPath implements ctlsock.Backend -func (rn *RootNode) DecryptPath(cipherPath string) (string, error) { - p, err := rn.decryptPath(cipherPath) - return p, err -} diff --git a/internal/fusefrontend_reverse/excluder.go b/internal/fusefrontend_reverse/excluder.go deleted file mode 100644 index 8a13fa7..0000000 --- a/internal/fusefrontend_reverse/excluder.go +++ /dev/null @@ -1,63 +0,0 @@ -package fusefrontend_reverse - -import ( - "io/ioutil" - "log" - "os" - "strings" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/fusefrontend" - "github.com/rfjakob/gocryptfs/internal/tlog" - - "github.com/sabhiram/go-gitignore" -) - -// prepareExcluder creates an object to check if paths are excluded -// based on the patterns specified in the command line. -func prepareExcluder(args fusefrontend.Args) *ignore.GitIgnore { - patterns := getExclusionPatterns(args) - if len(patterns) == 0 { - log.Panic(patterns) - } - excluder, err := ignore.CompileIgnoreLines(patterns...) - if err != nil { - tlog.Fatal.Printf("Error compiling exclusion rules: %v", err) - os.Exit(exitcodes.ExcludeError) - } - return excluder -} - -// getExclusionPatters prepares a list of patterns to be excluded. -// Patterns passed in the -exclude command line option are prefixed -// with a leading '/' to preserve backwards compatibility (before -// wildcard matching was implemented, exclusions always were matched -// against the full path). -func getExclusionPatterns(args fusefrontend.Args) []string { - patterns := make([]string, len(args.Exclude)+len(args.ExcludeWildcard)) - // add -exclude - for i, p := range args.Exclude { - patterns[i] = "/" + p - } - // add -exclude-wildcard - copy(patterns[len(args.Exclude):], args.ExcludeWildcard) - // add -exclude-from - for _, file := range args.ExcludeFrom { - lines, err := getLines(file) - if err != nil { - tlog.Fatal.Printf("Error reading exclusion patterns: %q", err) - os.Exit(exitcodes.ExcludeError) - } - patterns = append(patterns, lines...) - } - return patterns -} - -// getLines reads a file and splits it into lines -func getLines(file string) ([]string, error) { - buffer, err := ioutil.ReadFile(file) - if err != nil { - return nil, err - } - return strings.Split(string(buffer), "\n"), nil -} diff --git a/internal/fusefrontend_reverse/excluder_test.go b/internal/fusefrontend_reverse/excluder_test.go deleted file mode 100644 index d6cfef3..0000000 --- a/internal/fusefrontend_reverse/excluder_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package fusefrontend_reverse - -import ( - "io/ioutil" - "os" - "reflect" - "testing" - - "github.com/rfjakob/gocryptfs/internal/fusefrontend" -) - -func TestShouldPrefixExcludeValuesWithSlash(t *testing.T) { - var args fusefrontend.Args - args.Exclude = []string{"file1", "dir1/file2.txt"} - args.ExcludeWildcard = []string{"*~", "build/*.o"} - - expected := []string{"/file1", "/dir1/file2.txt", "*~", "build/*.o"} - - patterns := getExclusionPatterns(args) - if !reflect.DeepEqual(patterns, expected) { - t.Errorf("expected %q, got %q", expected, patterns) - } -} - -func TestShouldReadExcludePatternsFromFiles(t *testing.T) { - tmpfile1, err := ioutil.TempFile("", "excludetest") - if err != nil { - t.Fatal(err) - } - exclude1 := tmpfile1.Name() - defer os.Remove(exclude1) - defer tmpfile1.Close() - - tmpfile2, err := ioutil.TempFile("", "excludetest") - if err != nil { - t.Fatal(err) - } - exclude2 := tmpfile2.Name() - defer os.Remove(exclude2) - defer tmpfile2.Close() - - tmpfile1.WriteString("file1.1\n") - tmpfile1.WriteString("file1.2\n") - tmpfile2.WriteString("file2.1\n") - tmpfile2.WriteString("file2.2\n") - - var args fusefrontend.Args - args.ExcludeWildcard = []string{"cmdline1"} - args.ExcludeFrom = []string{exclude1, exclude2} - - // An empty string is returned for the last empty line - // It's ignored when the patterns are actually compiled - expected := []string{"cmdline1", "file1.1", "file1.2", "", "file2.1", "file2.2", ""} - - patterns := getExclusionPatterns(args) - if !reflect.DeepEqual(patterns, expected) { - t.Errorf("expected %q, got %q", expected, patterns) - } -} - -func TestShouldReturnFalseIfThereAreNoExclusions(t *testing.T) { - var rfs RootNode - if rfs.isExcludedPlain("any/path") { - t.Error("Should not exclude any path if no exclusions were specified") - } -} - -func TestShouldCallIgnoreParserToCheckExclusion(t *testing.T) { - rfs, ignorerMock := createRFSWithMocks() - - rfs.isExcludedPlain("some/path") - if ignorerMock.calledWith != "some/path" { - t.Error("Failed to call IgnoreParser") - } -} diff --git a/internal/fusefrontend_reverse/file.go b/internal/fusefrontend_reverse/file.go deleted file mode 100644 index 294872f..0000000 --- a/internal/fusefrontend_reverse/file.go +++ /dev/null @@ -1,81 +0,0 @@ -package fusefrontend_reverse - -import ( - "bytes" - "context" - "os" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/contentenc" -) - -type File struct { - // Backing FD - fd *os.File - // File header (contains the IV) - header contentenc.FileHeader - // IV for block 0 - block0IV []byte - // Content encryption helper - contentEnc *contentenc.ContentEnc -} - -// Read - FUSE call -func (f *File) Read(ctx context.Context, buf []byte, ioff int64) (resultData fuse.ReadResult, errno syscall.Errno) { - length := uint64(len(buf)) - off := uint64(ioff) - out := bytes.NewBuffer(buf[:0]) - var header []byte - - // Synthesize file header - if off < contentenc.HeaderLen { - header = f.header.Pack() - // Truncate to requested part - end := int(off) + len(buf) - if end > len(header) { - end = len(header) - } - header = header[off:end] - // Write into output buffer and adjust offsets - out.Write(header) - hLen := uint64(len(header)) - off += hLen - length -= hLen - } - - // Read actual file data - if length > 0 { - fileData, err := f.readBackingFile(off, length) - if err != nil { - return nil, fs.ToErrno(err) - } - if len(fileData) == 0 { - // If we could not read any actual data, we also don't want to - // return the file header. An empty file stays empty in encrypted - // form. - return nil, 0 - } - out.Write(fileData) - } - - return fuse.ReadResultData(out.Bytes()), 0 -} - -// Release - FUSE call, close file -func (f *File) Release(context.Context) syscall.Errno { - return fs.ToErrno(f.fd.Close()) -} - -// Lseek - FUSE call. -func (f *File) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) { - plainOff := f.contentEnc.CipherSizeToPlainSize(off) - newPlainOff, err := syscall.Seek(int(f.fd.Fd()), int64(plainOff), int(whence)) - if err != nil { - return 0, fs.ToErrno(err) - } - newOff := f.contentEnc.PlainSizeToCipherSize(uint64(newPlainOff)) - return newOff, 0 -} diff --git a/internal/fusefrontend_reverse/file_api_check.go b/internal/fusefrontend_reverse/file_api_check.go deleted file mode 100644 index b7e59d4..0000000 --- a/internal/fusefrontend_reverse/file_api_check.go +++ /dev/null @@ -1,25 +0,0 @@ -package fusefrontend_reverse - -import ( - "github.com/hanwen/go-fuse/v2/fs" -) - -// Check that we have implemented the fs.File* interfaces -var _ = (fs.FileReader)((*File)(nil)) -var _ = (fs.FileReleaser)((*File)(nil)) -var _ = (fs.FileLseeker)((*File)(nil)) - -/* Not needed -var _ = (fs.FileGetattrer)((*File)(nil)) -var _ = (fs.FileGetlker)((*File)(nil)) -var _ = (fs.FileSetlker)((*File)(nil)) -var _ = (fs.FileSetlkwer)((*File)(nil)) -*/ - -/* Will not implement these - reverse mode is read-only! -var _ = (fs.FileSetattrer)((*File)(nil)) -var _ = (fs.FileWriter)((*File)(nil)) -var _ = (fs.FileFsyncer)((*File)(nil)) -var _ = (fs.FileFlusher)((*File)(nil)) -var _ = (fs.FileAllocater)((*File)(nil)) -*/ diff --git a/internal/fusefrontend_reverse/file_helpers.go b/internal/fusefrontend_reverse/file_helpers.go deleted file mode 100644 index f024e69..0000000 --- a/internal/fusefrontend_reverse/file_helpers.go +++ /dev/null @@ -1,62 +0,0 @@ -package fusefrontend_reverse - -import ( - "bytes" - "io" - "sync" - - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/pathiv" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -var inodeTable sync.Map - -// encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks. -// "plaintext" must already be block-aligned. -func (rf *File) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileID []byte, block0IV []byte) []byte { - inBuf := bytes.NewBuffer(plaintext) - var outBuf bytes.Buffer - bs := int(rf.contentEnc.PlainBS()) - for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ { - inBlock := inBuf.Next(bs) - iv := pathiv.BlockIV(block0IV, blockNo) - outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileID, iv) - outBuf.Write(outBlock) - } - return outBuf.Bytes() -} - -// readBackingFile: read from the backing plaintext file, encrypt it, return the -// ciphertext. -// "off" ... ciphertext offset (must be >= HEADER_LEN) -// "length" ... ciphertext length -func (f *File) readBackingFile(off uint64, length uint64) (out []byte, err error) { - blocks := f.contentEnc.ExplodeCipherRange(off, length) - - // Read the backing plaintext in one go - alignedOffset, alignedLength := contentenc.JointPlaintextRange(blocks) - plaintext := make([]byte, int(alignedLength)) - n, err := f.fd.ReadAt(plaintext, int64(alignedOffset)) - if err != nil && err != io.EOF { - tlog.Warn.Printf("readBackingFile: ReadAt: %s", err.Error()) - return nil, err - } - // Truncate buffer down to actually read bytes - plaintext = plaintext[0:n] - - // Encrypt blocks - ciphertext := f.encryptBlocks(plaintext, blocks[0].BlockNo, f.header.ID, f.block0IV) - - // Crop down to the relevant part - lenHave := len(ciphertext) - skip := blocks[0].Skip - endWant := int(skip + length) - if lenHave > endWant { - out = ciphertext[skip:endWant] - } else if lenHave > int(skip) { - out = ciphertext[skip:lenHave] - } // else: out stays empty, file was smaller than the requested offset - - return out, nil -} diff --git a/internal/fusefrontend_reverse/mocks_test.go b/internal/fusefrontend_reverse/mocks_test.go deleted file mode 100644 index 2d14c1d..0000000 --- a/internal/fusefrontend_reverse/mocks_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package fusefrontend_reverse - -import ( - "github.com/rfjakob/gocryptfs/internal/nametransform" -) - -type IgnoreParserMock struct { - toExclude string - calledWith string -} - -func (parser *IgnoreParserMock) MatchesPath(f string) bool { - parser.calledWith = f - return f == parser.toExclude -} - -type NameTransformMock struct { - nametransform.NameTransform -} - -func (n *NameTransformMock) DecryptName(cipherName string, iv []byte) (string, error) { - return "mockdecrypt_" + cipherName, nil -} - -func createRFSWithMocks() (*RootNode, *IgnoreParserMock) { - ignorerMock := &IgnoreParserMock{} - nameTransformMock := &NameTransformMock{} - var rfs RootNode - rfs.excluder = ignorerMock - rfs.nameTransform = nameTransformMock - return &rfs, ignorerMock -} diff --git a/internal/fusefrontend_reverse/node.go b/internal/fusefrontend_reverse/node.go deleted file mode 100644 index 787b99b..0000000 --- a/internal/fusefrontend_reverse/node.go +++ /dev/null @@ -1,188 +0,0 @@ -package fusefrontend_reverse - -import ( - "context" - "fmt" - "os" - "path/filepath" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/pathiv" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Node is a file or directory in the filesystem tree -// in a `gocryptfs -reverse` mount. -type Node struct { - fs.Inode -} - -// Lookup - FUSE call for discovering a file. -func (n *Node) Lookup(ctx context.Context, cName string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { - var d *dirfdPlus - t := n.lookupFileType(cName) - if t == typeDiriv { - // gocryptfs.diriv - return n.lookupDiriv(ctx, out) - } else if t == typeName { - // gocryptfs.longname.*.name - return n.lookupLongnameName(ctx, cName, out) - } else if t == typeConfig { - // gocryptfs.conf - return n.lookupConf(ctx, out) - } else if t == typeReal { - // real file - d, errno = n.prepareAtSyscall(cName) - //fmt.Printf("Lookup: prepareAtSyscall -> d=%#v, errno=%d\n", d, errno) - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - } - // Get device number and inode number into `st` - st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - return nil, fs.ToErrno(err) - } - // Create new inode and fill `out` - ch = n.newChild(ctx, st, out) - // Translate ciphertext size in `out.Attr.Size` to plaintext size - if t == typeReal { - n.translateSize(d.dirfd, cName, d.pName, &out.Attr) - } - return ch, 0 -} - -// GetAttr - FUSE call for stat()ing a file. -// -// GetAttr is symlink-safe through use of openBackingDir() and Fstatat(). -func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) (errno syscall.Errno) { - d, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - - st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - return fs.ToErrno(err) - } - - // Fix inode number - rn := n.rootNode() - rn.inoMap.TranslateStat(st) - out.Attr.FromStat(st) - - // Translate ciphertext size in `out.Attr.Size` to plaintext size - cName := filepath.Base(n.Path()) - n.translateSize(d.dirfd, cName, d.pName, &out.Attr) - - if rn.args.ForceOwner != nil { - out.Owner = *rn.args.ForceOwner - } - return 0 -} - -// Readlink - FUSE call. -// -// Symlink-safe through openBackingDir() + Readlinkat(). -func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) { - d, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - - return n.readlink(d.dirfd, d.cName, d.pName) -} - -// Open - FUSE call. Open already-existing file. -// -// Symlink-safe through Openat(). -func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { - d, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - - fd, err := syscallcompat.Openat(d.dirfd, d.pName, syscall.O_RDONLY|syscall.O_NOFOLLOW, 0) - if err != nil { - errno = fs.ToErrno(err) - return - } - - // Reject access if the file descriptor does not refer to a regular file. - var st syscall.Stat_t - err = syscall.Fstat(fd, &st) - if err != nil { - tlog.Warn.Printf("Open: Fstat error: %v", err) - syscall.Close(fd) - errno = fs.ToErrno(err) - return - } - var a fuse.Attr - a.FromStat(&st) - if !a.IsRegular() { - tlog.Warn.Printf("ino%d: newFile: not a regular file", st.Ino) - syscall.Close(fd) - errno = syscall.EACCES - return - } - // See if we have that inode number already in the table - // (even if Nlink has dropped to 1) - var derivedIVs pathiv.FileIVs - v, found := inodeTable.Load(st.Ino) - if found { - tlog.Debug.Printf("ino%d: newFile: found in the inode table", st.Ino) - derivedIVs = v.(pathiv.FileIVs) - } else { - p := n.Path() - derivedIVs = pathiv.DeriveFile(p) - // Nlink > 1 means there is more than one path to this file. - // Store the derived values so we always return the same data, - // regardless of the path that is used to access the file. - // This means that the first path wins. - if st.Nlink > 1 { - v, found = inodeTable.LoadOrStore(st.Ino, derivedIVs) - if found { - // Another thread has stored a different value before we could. - derivedIVs = v.(pathiv.FileIVs) - } else { - tlog.Debug.Printf("ino%d: newFile: Nlink=%d, stored in the inode table", st.Ino, st.Nlink) - } - } - } - header := contentenc.FileHeader{ - Version: contentenc.CurrentVersion, - ID: derivedIVs.ID, - } - fh = &File{ - fd: os.NewFile(uintptr(fd), fmt.Sprintf("fd%d", fd)), - header: header, - block0IV: derivedIVs.Block0IV, - contentEnc: n.rootNode().contentEnc, - } - return -} - -// StatFs - FUSE call. Returns information about the filesystem. -// -// Symlink-safe because the path is ignored. -func (n *Node) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { - p := n.rootNode().args.Cipherdir - var st syscall.Statfs_t - err := syscall.Statfs(p, &st) - if err != nil { - return fs.ToErrno(err) - } - out.FromStatfsT(&st) - return 0 -} diff --git a/internal/fusefrontend_reverse/node_api_check.go b/internal/fusefrontend_reverse/node_api_check.go deleted file mode 100644 index f8ec9ce..0000000 --- a/internal/fusefrontend_reverse/node_api_check.go +++ /dev/null @@ -1,40 +0,0 @@ -package fusefrontend_reverse - -import ( - "github.com/hanwen/go-fuse/v2/fs" -) - -// Check that we have implemented the fs.Node* interfaces -var _ = (fs.NodeGetattrer)((*Node)(nil)) -var _ = (fs.NodeLookuper)((*Node)(nil)) -var _ = (fs.NodeReaddirer)((*Node)(nil)) -var _ = (fs.NodeReadlinker)((*Node)(nil)) -var _ = (fs.NodeOpener)((*Node)(nil)) -var _ = (fs.NodeStatfser)((*Node)(nil)) - -/* -TODO but low prio. reverse mode in gocryptfs v1 did not have xattr support -either. - -var _ = (fs.NodeGetxattrer)((*Node)(nil)) -var _ = (fs.NodeListxattrer)((*Node)(nil)) -*/ - -/* Not needed -var _ = (fs.NodeOpendirer)((*Node)(nil)) -*/ - -/* Will not implement these - reverse mode is read-only! -var _ = (fs.NodeMknoder)((*Node)(nil)) -var _ = (fs.NodeCreater)((*Node)(nil)) -var _ = (fs.NodeMkdirer)((*Node)(nil)) -var _ = (fs.NodeRmdirer)((*Node)(nil)) -var _ = (fs.NodeUnlinker)((*Node)(nil)) -var _ = (fs.NodeSetattrer)((*Node)(nil)) -var _ = (fs.NodeLinker)((*Node)(nil)) -var _ = (fs.NodeSymlinker)((*Node)(nil)) -var _ = (fs.NodeRenamer)((*Node)(nil)) -var _ = (fs.NodeSetxattrer)((*Node)(nil)) -var _ = (fs.NodeRemovexattrer)((*Node)(nil)) -var _ = (fs.NodeCopyFileRanger)((*Node)(nil)) -*/ diff --git a/internal/fusefrontend_reverse/node_dir_ops.go b/internal/fusefrontend_reverse/node_dir_ops.go deleted file mode 100644 index c287284..0000000 --- a/internal/fusefrontend_reverse/node_dir_ops.go +++ /dev/null @@ -1,114 +0,0 @@ -package fusefrontend_reverse - -import ( - "context" - "fmt" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/pathiv" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Readdir - FUSE call. -// -// This function is symlink-safe through use of openBackingDir() and -// ReadDirIVAt(). -func (n *Node) Readdir(ctx context.Context) (stream fs.DirStream, errno syscall.Errno) { - d, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - - // Read plaintext directory - var entries []fuse.DirEntry - fd, err := syscallcompat.Openat(d.dirfd, d.pName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - return nil, fs.ToErrno(err) - } - defer syscall.Close(fd) - entries, err = syscallcompat.Getdents(fd) - if err != nil { - return nil, fs.ToErrno(err) - } - - rn := n.rootNode() - - // Filter out excluded entries - entries = rn.excludeDirEntries(d, entries) - - if rn.args.PlaintextNames { - return n.readdirPlaintextnames(entries) - } - - // Virtual files: at least one gocryptfs.diriv file - virtualFiles := []fuse.DirEntry{ - {Mode: virtualFileMode, Name: nametransform.DirIVFilename}, - } - - dirIV := pathiv.Derive(d.cPath, pathiv.PurposeDirIV) - // Encrypt names - for i := range entries { - var cName string - // ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf" - if n.isRoot() && entries[i].Name == configfile.ConfReverseName && - !rn.args.ConfigCustom { - cName = configfile.ConfDefaultName - } else { - cName, err = rn.nameTransform.EncryptName(entries[i].Name, dirIV) - if err != nil { - entries[i].Name = "___GOCRYPTFS_INVALID_NAME___" - continue - } - if len(cName) > unix.NAME_MAX { - cName = rn.nameTransform.HashLongName(cName) - dotNameFile := fuse.DirEntry{ - Mode: virtualFileMode, - Name: cName + nametransform.LongNameSuffix, - } - virtualFiles = append(virtualFiles, dotNameFile) - } - } - entries[i].Name = cName - } - - // Add virtual files - entries = append(entries, virtualFiles...) - return fs.NewListDirStream(entries), 0 -} - -func (n *Node) readdirPlaintextnames(entries []fuse.DirEntry) (stream fs.DirStream, errno syscall.Errno) { - rn := n.rootNode() - // If we are not the root dir or a custom config path was used, we don't - // need to map anything - if !n.isRoot() || rn.args.ConfigCustom { - return fs.NewListDirStream(entries), 0 - } - // We are in the root dir and the default config file name - // ".gocryptfs.reverse.conf" is used. We map it to "gocryptfs.conf". - dupe := -1 - for i := range entries { - if entries[i].Name == configfile.ConfReverseName { - entries[i].Name = configfile.ConfDefaultName - } else if entries[i].Name == configfile.ConfDefaultName { - dupe = i - } - } - if dupe >= 0 { - // Warn the user loudly: The gocryptfs.conf_NAME_COLLISION file will - // throw ENOENT errors that are hard to miss. - tlog.Warn.Printf("The file %q is mapped to %q and shadows another file. Please rename %q in directory %q.", - configfile.ConfReverseName, configfile.ConfDefaultName, configfile.ConfDefaultName, rn.args.Cipherdir) - entries[dupe].Name = "gocryptfs.conf_NAME_COLLISION_" + fmt.Sprintf("%d", cryptocore.RandUint64()) - } - return fs.NewListDirStream(entries), 0 -} diff --git a/internal/fusefrontend_reverse/node_helpers.go b/internal/fusefrontend_reverse/node_helpers.go deleted file mode 100644 index 92f6a87..0000000 --- a/internal/fusefrontend_reverse/node_helpers.go +++ /dev/null @@ -1,231 +0,0 @@ -package fusefrontend_reverse - -import ( - "context" - "path/filepath" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/pathiv" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" -) - -const ( - // File names are padded to 16-byte multiples, encrypted and - // base64-encoded. We can encode at most 176 bytes to stay below the 255 - // bytes limit: - // * base64(176 bytes) = 235 bytes - // * base64(192 bytes) = 256 bytes (over 255!) - // But the PKCS#7 padding is at least one byte. This means we can only use - // 175 bytes for the file name. - shortNameMax = 175 -) - -// translateSize translates the ciphertext size in `out` into plaintext size. -func (n *Node) translateSize(dirfd int, cName string, pName string, out *fuse.Attr) { - if out.IsRegular() { - rn := n.rootNode() - out.Size = rn.contentEnc.PlainSizeToCipherSize(out.Size) - } else if out.IsSymlink() { - cLink, _ := n.readlink(dirfd, cName, pName) - out.Size = uint64(len(cLink)) - } -} - -// Path returns the relative plaintext path of this node -func (n *Node) Path() string { - return n.Inode.Path(n.Root()) -} - -// rootNode returns the Root Node of the filesystem. -func (n *Node) rootNode() *RootNode { - return n.Root().Operations().(*RootNode) -} - -// dirfdPlus gets filled out as we gather information about a node -type dirfdPlus struct { - // fd to the directory, opened with O_DIRECTORY|O_PATH - dirfd int - // Relative plaintext path - pPath string - // Plaintext basename: filepath.Base(pPath) - pName string - // Relative ciphertext path - cPath string - // Ciphertext basename: filepath.Base(cPath) - cName string -} - -// prepareAtSyscall returns a (dirfd, cName) pair that can be used -// with the "___at" family of system calls (openat, fstatat, unlinkat...) to -// access the backing encrypted directory. -// -// If you pass a `child` file name, the (dirfd, cName) pair will refer to -// a child of this node. -// If `child` is empty, the (dirfd, cName) pair refers to this node itself. -func (n *Node) prepareAtSyscall(child string) (d *dirfdPlus, errno syscall.Errno) { - cPath := n.Path() - if child != "" { - cPath = filepath.Join(cPath, child) - } - rn := n.rootNode() - dirfd, pPath, err := rn.openBackingDir(cPath) - if err != nil { - errno = fs.ToErrno(err) - } - d = &dirfdPlus{ - dirfd: dirfd, - pPath: pPath, - pName: filepath.Base(pPath), - cPath: cPath, - cName: filepath.Base(cPath), - } - return -} - -// newChild attaches a new child inode to n. -// The passed-in `st` will be modified to get a unique inode number. -func (n *Node) newChild(ctx context.Context, st *syscall.Stat_t, out *fuse.EntryOut) *fs.Inode { - // Get unique inode number - rn := n.rootNode() - rn.inoMap.TranslateStat(st) - out.Attr.FromStat(st) - // Create child node - id := fs.StableAttr{ - Mode: uint32(st.Mode), - Gen: 1, - Ino: st.Ino, - } - node := &Node{} - return n.NewInode(ctx, node, id) -} - -// isRoot returns true if this node is the root node -func (n *Node) isRoot() bool { - rn := n.rootNode() - return &rn.Node == n -} - -func (n *Node) lookupLongnameName(ctx context.Context, nameFile string, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { - d, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - - // Find the file the gocryptfs.longname.XYZ.name file belongs to in the - // directory listing - fd, err := syscallcompat.Openat(d.dirfd, d.pName, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - errno = fs.ToErrno(err) - return - } - defer syscall.Close(fd) - diriv := pathiv.Derive(d.cPath, pathiv.PurposeDirIV) - rn := n.rootNode() - pName, cFullname, errno := rn.findLongnameParent(fd, diriv, nameFile) - if errno != 0 { - return - } - if rn.isExcludedPlain(filepath.Join(d.cPath, pName)) { - errno = syscall.EPERM - return - } - // Get attrs from parent file - st, err := syscallcompat.Fstatat2(fd, pName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - errno = fs.ToErrno(err) - return - } - var vf *VirtualMemNode - vf, errno = n.newVirtualMemNode([]byte(cFullname), st, inoTagNameFile) - if errno != 0 { - return nil, errno - } - out.Attr = vf.attr - // Create child node - id := fs.StableAttr{Mode: uint32(vf.attr.Mode), Gen: 1, Ino: 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) { - d, errno := n.prepareAtSyscall("") - if errno != 0 { - return - } - defer syscall.Close(d.dirfd) - st, err := syscallcompat.Fstatat2(d.dirfd, d.pName, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - errno = fs.ToErrno(err) - return - } - content := pathiv.Derive(d.cPath, pathiv.PurposeDirIV) - var vf *VirtualMemNode - vf, errno = n.newVirtualMemNode(content, st, inoTagDirIV) - if errno != 0 { - return nil, errno - } - out.Attr = vf.attr - // Create child node - id := fs.StableAttr{Mode: uint32(vf.attr.Mode), Gen: 1, Ino: vf.attr.Ino} - ch = n.NewInode(ctx, vf, id) - return -} - -// lookupConf returns a new Inode for the gocryptfs.conf file -func (n *Node) lookupConf(ctx context.Context, out *fuse.EntryOut) (ch *fs.Inode, errno syscall.Errno) { - rn := n.rootNode() - p := filepath.Join(rn.args.Cipherdir, configfile.ConfReverseName) - var st syscall.Stat_t - err := syscall.Stat(p, &st) - if err != nil { - errno = fs.ToErrno(err) - return - } - // 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, - } - node := &VirtualConfNode{path: p} - ch = n.NewInode(ctx, node, id) - return -} - -// readlink reads and encrypts a symlink. Used by Readlink, Getattr, Lookup. -func (n *Node) readlink(dirfd int, cName string, pName string) (out []byte, errno syscall.Errno) { - plainTarget, err := syscallcompat.Readlinkat(dirfd, pName) - if err != nil { - errno = fs.ToErrno(err) - return - } - rn := n.rootNode() - if rn.args.PlaintextNames { - return []byte(plainTarget), 0 - } - // Nonce is derived from the relative *ciphertext* path - p := filepath.Join(n.Path(), cName) - nonce := pathiv.Derive(p, pathiv.PurposeSymlinkIV) - // Symlinks are encrypted like file contents and base64-encoded - cBinTarget := rn.contentEnc.EncryptBlockNonce([]byte(plainTarget), 0, nil, nonce) - cTarget := rn.nameTransform.B64EncodeToString(cBinTarget) - // The kernel will reject a symlink target above 4096 chars and return - // and I/O error to the user. Better emit the proper error ourselves. - if len(cTarget) > syscallcompat.PATH_MAX { - errno = syscall.ENAMETOOLONG - return - } - return []byte(cTarget), 0 -} diff --git a/internal/fusefrontend_reverse/root_node.go b/internal/fusefrontend_reverse/root_node.go deleted file mode 100644 index 10b0d69..0000000 --- a/internal/fusefrontend_reverse/root_node.go +++ /dev/null @@ -1,120 +0,0 @@ -package fusefrontend_reverse - -import ( - "log" - "path/filepath" - "strings" - "syscall" - - "github.com/rfjakob/gocryptfs/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/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/fusefrontend" - "github.com/rfjakob/gocryptfs/internal/inomap" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - - "github.com/sabhiram/go-gitignore" -) - -// RootNode is the root directory in a `gocryptfs -reverse` mount -type RootNode struct { - Node - // Stores configuration arguments - args fusefrontend.Args - // Filename encryption helper - nameTransform nametransform.NameTransformer - // Content encryption helper - contentEnc *contentenc.ContentEnc - // Tests whether a path is excluded (hidden) from the user. Used by -exclude. - excluder ignore.IgnoreParser - // inoMap translates inode numbers from different devices to unique inode - // numbers. - inoMap *inomap.InoMap -} - -// NewRootNode returns an encrypted FUSE overlay filesystem. -// In this case (reverse mode) the backing directory is plain-text and -// ReverseFS provides an encrypted view. -func NewRootNode(args fusefrontend.Args, c *contentenc.ContentEnc, n nametransform.NameTransformer) *RootNode { - rn := &RootNode{ - args: args, - nameTransform: n, - contentEnc: c, - inoMap: inomap.New(), - } - if len(args.Exclude) > 0 || len(args.ExcludeWildcard) > 0 || len(args.ExcludeFrom) > 0 { - rn.excluder = prepareExcluder(args) - } - return rn -} - -// You can pass either gocryptfs.longname.XYZ.name or gocryptfs.longname.XYZ. -func (rn *RootNode) findLongnameParent(fd int, diriv []byte, longname string) (pName string, cFullName string, errno syscall.Errno) { - defer func() { - tlog.Debug.Printf("findLongnameParent: %d %x %q -> %q %q %d\n", fd, diriv, longname, pName, cFullName, errno) - }() - if strings.HasSuffix(longname, nametransform.LongNameSuffix) { - longname = nametransform.RemoveLongNameSuffix(longname) - } - entries, err := syscallcompat.Getdents(fd) - if err != nil { - errno = fs.ToErrno(err) - return - } - for _, entry := range entries { - if len(entry.Name) <= shortNameMax { - continue - } - cFullName, err = rn.nameTransform.EncryptName(entry.Name, diriv) - if err != nil { - continue - } - if len(cFullName) <= unix.NAME_MAX { - // Entry should have been skipped by the shortNameMax check above - log.Panic("logic error or wrong shortNameMax constant?") - } - hName := rn.nameTransform.HashLongName(cFullName) - if longname == hName { - pName = entry.Name - break - } - } - if pName == "" { - errno = syscall.ENOENT - return - } - return -} - -// isExcludedPlain finds out if the plaintext path "pPath" is -// excluded (used when -exclude is passed by the user). -func (rn *RootNode) isExcludedPlain(pPath string) bool { - return rn.excluder != nil && rn.excluder.MatchesPath(pPath) -} - -// excludeDirEntries filters out directory entries that are "-exclude"d. -// pDir is the relative plaintext path to the directory these entries are -// from. The entries should be plaintext files. -func (rn *RootNode) excludeDirEntries(d *dirfdPlus, entries []fuse.DirEntry) (filtered []fuse.DirEntry) { - if rn.excluder == nil { - return entries - } - filtered = make([]fuse.DirEntry, 0, len(entries)) - for _, entry := range entries { - // filepath.Join handles the case of pDir="" correctly: - // Join("", "foo") -> "foo". This does not: pDir + "/" + name" - p := filepath.Join(d.pPath, entry.Name) - if rn.isExcludedPlain(p) { - // Skip file - continue - } - filtered = append(filtered, entry) - } - return filtered -} diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go deleted file mode 100644 index f29bbf5..0000000 --- a/internal/fusefrontend_reverse/rpath.go +++ /dev/null @@ -1,123 +0,0 @@ -package fusefrontend_reverse - -import ( - "encoding/base64" - "path/filepath" - "strings" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/pathiv" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// abs basically returns storage dir + "/" + relPath. -// It takes an error parameter so it can directly wrap decryptPath like this: -// a, err := rfs.abs(rfs.decryptPath(relPath)) -// abs never generates an error on its own. In other words, abs(p, nil) never -// fails. -func (rfs *RootNode) abs(relPath string, err error) (string, error) { - if err != nil { - return "", err - } - return filepath.Join(rfs.args.Cipherdir, relPath), nil -} - -// rDecryptName decrypts the ciphertext name "cName", given the dirIV of the -// directory "cName" lies in. The relative plaintext path to the directory -// "pDir" is used if a "gocryptfs.longname.XYZ.name" must be resolved. -func (rfs *RootNode) rDecryptName(cName string, dirIV []byte, pDir string) (pName string, err error) { - nameType := nametransform.NameType(cName) - if nameType == nametransform.LongNameNone { - pName, err = rfs.nameTransform.DecryptName(cName, dirIV) - if err != nil { - // We get lots of decrypt requests for names like ".Trash" that - // are invalid base64. Convert them to ENOENT so the correct - // error gets returned to the user. - if _, ok := err.(base64.CorruptInputError); ok { - return "", syscall.ENOENT - } - // Stat attempts on the link target of encrypted symlinks. - // These are always valid base64 but the length is not a - // multiple of 16. - if err == syscall.EBADMSG { - return "", syscall.ENOENT - } - return "", err - } - } else if nameType == nametransform.LongNameContent { - dirfd, err := syscallcompat.OpenDirNofollow(rfs.args.Cipherdir, filepath.Dir(pDir)) - if err != nil { - return "", err - } - defer syscall.Close(dirfd) - fd, err := syscallcompat.Openat(dirfd, filepath.Base(pDir), syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NOFOLLOW, 0) - if err != nil { - return "", err - } - defer syscall.Close(fd) - var errno syscall.Errno - pName, _, errno = rfs.findLongnameParent(fd, dirIV, cName) - if errno != 0 { - return "", errno - } - } else { - // It makes no sense to decrypt a ".name" file. This is a virtual file - // that has no representation in the plaintext filesystem. ".name" - // files should have already been handled in virtualfile.go. - tlog.Warn.Printf("rDecryptName: cannot decrypt virtual file %q", cName) - return "", syscall.EINVAL - } - return pName, nil -} - -// decryptPath decrypts a relative ciphertext path to a relative plaintext -// path. -func (rn *RootNode) decryptPath(cPath string) (string, error) { - if rn.args.PlaintextNames || cPath == "" { - return cPath, nil - } - parts := strings.Split(cPath, "/") - var transformedParts []string - for i := range parts { - // Start at the top and recurse - currentCipherDir := filepath.Join(parts[:i]...) - currentPlainDir := filepath.Join(transformedParts[:i]...) - dirIV := pathiv.Derive(currentCipherDir, pathiv.PurposeDirIV) - transformedPart, err := rn.rDecryptName(parts[i], dirIV, currentPlainDir) - if err != nil { - return "", err - } - transformedParts = append(transformedParts, transformedPart) - } - pRelPath := filepath.Join(transformedParts...) - return pRelPath, nil -} - -// openBackingDir receives an already decrypted relative path -// "pRelPath", opens the directory that contains the target file/dir -// and returns the fd to the directory and the decrypted name of the -// target file. The fd/name pair is intended for use with fchownat and -// friends. -func (rn *RootNode) openBackingDir(cPath string) (dirfd int, pPath string, err error) { - defer func() { - tlog.Debug.Printf("openBackingDir %q -> %d %q %v\n", cPath, dirfd, pPath, err) - }() - dirfd = -1 - pPath, err = rn.decryptPath(cPath) - if err != nil { - return - } - if rn.isExcludedPlain(pPath) { - err = syscall.EPERM - return - } - // Open directory, safe against symlink races - pDir := filepath.Dir(pPath) - dirfd, err = syscallcompat.OpenDirNofollow(rn.args.Cipherdir, pDir) - if err != nil { - return - } - return dirfd, pPath, nil -} diff --git a/internal/fusefrontend_reverse/virtualconf.go b/internal/fusefrontend_reverse/virtualconf.go deleted file mode 100644 index 8620f6d..0000000 --- a/internal/fusefrontend_reverse/virtualconf.go +++ /dev/null @@ -1,55 +0,0 @@ -package fusefrontend_reverse - -import ( - "context" - "sync" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" -) - -var _ = (fs.NodeOpener)((*VirtualConfNode)(nil)) - -type VirtualConfNode struct { - fs.Inode - - path string -} - -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 { - errno = fs.ToErrno(err) - return - } - fh = &VirtualConfFile{fd: fd} - return -} - -// Check that we have implemented the fs.File* interfaces -var _ = (fs.FileReader)((*VirtualConfFile)(nil)) -var _ = (fs.FileReleaser)((*VirtualConfFile)(nil)) - -type VirtualConfFile struct { - mu sync.Mutex - fd int -} - -func (f *VirtualConfFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) { - f.mu.Lock() - defer f.mu.Unlock() - res = fuse.ReadResultFd(uintptr(f.fd), off, len(buf)) - return -} - -func (f *VirtualConfFile) Release(ctx context.Context) syscall.Errno { - f.mu.Lock() - defer f.mu.Unlock() - if f.fd != -1 { - err := syscall.Close(f.fd) - f.fd = -1 - return fs.ToErrno(err) - } - return syscall.EBADF -} diff --git a/internal/fusefrontend_reverse/virtualnode.go b/internal/fusefrontend_reverse/virtualnode.go deleted file mode 100644 index 2ee9548..0000000 --- a/internal/fusefrontend_reverse/virtualnode.go +++ /dev/null @@ -1,117 +0,0 @@ -package fusefrontend_reverse - -import ( - "context" - "log" - "syscall" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/inomap" - "github.com/rfjakob/gocryptfs/internal/nametransform" -) - -const ( - // virtualFileMode is the mode to use for virtual files (gocryptfs.diriv and - // *.name). They are always readable, as stated in func Access - virtualFileMode = syscall.S_IFREG | 0444 - // We use inomap's `Tag` feature to generate unique inode numbers for - // virtual files. These are the tags we use. - inoTagDirIV = 1 - inoTagNameFile = 2 -) - -type fileType int - -// Values returned by lookupFileType -const ( - // A real file/directory/symlink in the backing plaintext directory - typeReal fileType = iota - // A DirIV (gocryptfs.diriv) file - typeDiriv - // A gocryptfs.longname.*.name file for a file with a long name - typeName - // The config file gocryptfs.conf - typeConfig -) - -// lookupFileType returns the type of child file name -// (one of the fileType constants above). Called from Lookup(). -func (n *Node) lookupFileType(cName string) fileType { - rn := n.rootNode() - // In -plaintextname mode, neither diriv nor longname files exist. - if !rn.args.PlaintextNames { - // Is it a gocryptfs.diriv file? - if cName == nametransform.DirIVFilename { - return typeDiriv - } - // Is it a gocryptfs.longname.*.name file? - if t := nametransform.NameType(cName); t == nametransform.LongNameFilename { - return typeName - } - } - // gocryptfs.conf in the root directory. This is passed through to - // .gocryptfs.reverse.conf in the backing plaintext directory. - if n.isRoot() && !rn.args.ConfigCustom && cName == configfile.ConfDefaultName { - return typeConfig - } - return typeReal -} - -// VirtualMemNode is an in-memory node that does not have a representation -// on disk. -type VirtualMemNode struct { - fs.Inode - - // file content - content []byte - // attributes for Getattr() - attr fuse.Attr -} - -// newVirtualMemNode creates a new in-memory file that does not have a representation -// on disk. "content" is the file content. Timestamps and file owner are copied -// from "parentFile" (file descriptor). -// For a "gocryptfs.diriv" file, you would use the parent directory as -// "parentFile". -func (n *Node) newVirtualMemNode(content []byte, parentStat *syscall.Stat_t, inoTag uint8) (vf *VirtualMemNode, errno syscall.Errno) { - if inoTag == 0 { - log.Panicf("BUG: inoTag for virtual file is zero - this will cause ino collisions!") - } - - // 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) - st.Size = int64(len(content)) - st.Mode = virtualFileMode - st.Nlink = 1 - var a fuse.Attr - a.FromStat(st) - - vf = &VirtualMemNode{content: content, attr: a} - return -} - -// Open - FUSE call -func (f *VirtualMemNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { - return nil, fuse.FOPEN_KEEP_CACHE, 0 -} - -// GetAttr - FUSE call -func (f *VirtualMemNode) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno { - out.Attr = f.attr - return 0 -} - -// Read - FUSE call -func (f *VirtualMemNode) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) { - end := int(off) + len(dest) - if end > len(f.content) { - end = len(f.content) - } - return fuse.ReadResultData(f.content[off:end]), 0 -} diff --git a/internal/inomap/inomap.go b/internal/inomap/inomap.go deleted file mode 100644 index 0977a46..0000000 --- a/internal/inomap/inomap.go +++ /dev/null @@ -1,117 +0,0 @@ -// inomap translates (Dev, Flags, Ino) tuples to unique uint64 -// inode numbers. -// -// 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 ] -// -// Each (Dev, Flags) tuple gets a namespace id assigned. The original inode -// number is then passed through in the lower 48 bits. -// -// If namespace ids are exhaused, or the original id is larger than 48 bits, -// the whole (Dev, Flags, Ino) tuple gets mapped in the spill map, and the -// spill bit is set to 1. -package inomap - -import ( - "log" - "sync" - "syscall" -) - -const ( - // max value of 15 bit namespace id - 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 -) - -// InoMap stores the maps using for inode number translation. -// See package comment for details. -type InoMap struct { - sync.Mutex - // namespaceMap keeps the mapping of (Dev,Flags) tuples to - // 15-bit identifiers (stored in an uint16 with the high bit always zero) - namespaceMap map[namespaceData]uint16 - // spillNext is the next free namespace number in the namespaces map - namespaceNext uint16 - // spill is used once the namespaces map is full - spillMap map[QIno]uint64 - // spillNext is the next free inode number in the spill map - spillNext uint64 -} - -// New returns a new InoMap. -func New() *InoMap { - return &InoMap{ - namespaceMap: make(map[namespaceData]uint16), - namespaceNext: 0, - spillMap: make(map[QIno]uint64), - spillNext: 0, - } -} - -func (m *InoMap) spill(in QIno) (out uint64) { - out, found := m.spillMap[in] - if found { - return out | spillBit - } - if m.spillNext >= maxSpillIno { - log.Panicf("spillMap overflow: spillNext = 0x%x", m.spillNext) - } - out = m.spillNext - m.spillNext++ - m.spillMap[in] = out - return out | spillBit -} - -// Translate maps the passed-in (device, inode) pair to a unique inode number. -func (m *InoMap) Translate(in QIno) (out uint64) { - m.Lock() - defer m.Unlock() - - if in.Ino > maxPassthruIno { - out = m.spill(in) - return out - } - ns, found := m.namespaceMap[in.namespaceData] - // Use existing namespace - if found { - out = uint64(ns)<<48 | in.Ino - return out - } - // No free namespace slots? - if m.namespaceNext >= maxNamespaceId { - out = m.spill(in) - return out - } - ns = m.namespaceNext - m.namespaceNext++ - m.namespaceMap[in.namespaceData] = ns - out = uint64(ns)<<48 | in.Ino - return out -} - -// TranslateStat translates (device, ino) pair contained in "st" into a unique -// inode number and overwrites the ino in "st" with it. -// Convience wrapper around Translate(). -func (m *InoMap) TranslateStat(st *syscall.Stat_t) { - in := QInoFromStat(st) - st.Ino = m.Translate(in) -} - -type TranslateStater interface { - TranslateStat(st *syscall.Stat_t) -} - -// TranslateStatZero always sets st.Ino to zero. Used for `-sharedstorage`. -type TranslateStatZero struct{} - -func (z TranslateStatZero) TranslateStat(st *syscall.Stat_t) { - st.Ino = 0 -} diff --git a/internal/inomap/inomap_test.go b/internal/inomap/inomap_test.go deleted file mode 100644 index 6fbb4ef..0000000 --- a/internal/inomap/inomap_test.go +++ /dev/null @@ -1,160 +0,0 @@ -package inomap - -import ( - "sync" - "testing" -) - -func TestTranslate(t *testing.T) { - m := New() - q := QIno{Ino: 1} - out := m.Translate(q) - if out != 1 { - t.Errorf("expected 1, got %d", out) - } - q.Ino = maxPassthruIno - out = m.Translate(q) - if out < maxPassthruIno { - t.Errorf("got %d", out) - } - out2 := m.Translate(q) - if out2 != out { - t.Errorf("unstable mapping: %d %d", out2, out) - } -} - -func TestTranslateStress(t *testing.T) { - const baseDev = 12345 - m := New() - - // Make sure baseDev gets namespace id zero - var q QIno - q.Dev = baseDev - m.Translate(q) - - var wg sync.WaitGroup - wg.Add(4) - go func() { - // Some normal inode numbers on baseDev - var q QIno - q.Dev = baseDev - for i := uint64(1); i <= 10000; i++ { - q.Ino = i - out := m.Translate(q) - if out != i { - t.Errorf("i=%d out=%d", i, out) - break - } - } - wg.Done() - }() - go func() { - // Very high (>maxPassthruIno) inode numbers on baseDev - var q QIno - q.Dev = baseDev - for i := uint64(1); i <= 10000; i++ { - q.Ino = maxPassthruIno + i - out := m.Translate(q) - if out < maxPassthruIno { - t.Errorf("out=%d", out) - break - } - } - wg.Done() - }() - go func() { - // Device 9999999 - var q QIno - q.Dev = 9999999 - for i := uint64(1); i <= 10000; i++ { - q.Ino = i - out := m.Translate(q) - if out < maxPassthruIno { - t.Errorf("out=%d", out) - break - } - } - wg.Done() - }() - go func() { - // Device 4444444 - var q QIno - q.Dev = 4444444 - for i := uint64(1); i <= 10000; i++ { - q.Ino = i - out := m.Translate(q) - if out < maxPassthruIno { - t.Errorf("out=%d", out) - break - } - } - wg.Done() - }() - wg.Wait() - if len(m.spillMap) != 10000 { - t.Errorf("len=%d", len(m.spillMap)) - } - if len(m.namespaceMap) != 3 { - t.Errorf("len=%d", len(m.namespaceMap)) - } -} - -func TestSpill(t *testing.T) { - m := New() - var q QIno - q.Ino = maxPassthruIno + 1 - out1 := m.Translate(q) - if out1&spillBit == 0 { - t.Error("spill bit not set") - } - 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) - } -} - -// TestUniqueness checks that unique (Dev, Flags, Ino) tuples get unique inode -// numbers -func TestUniqueness(t *testing.T) { - m := New() - var q QIno - outMap := make(map[uint64]struct{}) - for q.Dev = 0; q.Dev < 10; q.Dev++ { - for q.Tag = 0; q.Tag < 10; q.Tag++ { - // some go into spill - for q.Ino = maxPassthruIno - 100; q.Ino < maxPassthruIno+100; q.Ino++ { - out := m.Translate(q) - _, found := outMap[out] - if found { - t.Fatalf("inode number %d already used", out) - } - outMap[out] = struct{}{} - } - } - } - if len(outMap) != 10*10*200 { - t.Errorf("%d", len(outMap)) - } -} - -func BenchmarkTranslateSingleDev(b *testing.B) { - m := New() - var q QIno - for n := 0; n < b.N; n++ { - q.Ino = uint64(n % 1000) - m.Translate(q) - } -} - -func BenchmarkTranslateManyDevs(b *testing.B) { - m := New() - var q QIno - for n := 0; n < b.N; n++ { - q.Dev = uint64(n % 10) - q.Ino = uint64(n % 1000) - m.Translate(q) - } -} diff --git a/internal/inomap/qino.go b/internal/inomap/qino.go deleted file mode 100644 index ed514e8..0000000 --- a/internal/inomap/qino.go +++ /dev/null @@ -1,43 +0,0 @@ -package inomap - -import ( - "syscall" -) - -type namespaceData struct { - // Stat_t.Dev is uint64 on 32- and 64-bit Linux - Dev uint64 - // Tag acts like an extension of the Dev field. - // It is used by reverse mode for virtual files. - // Normal (forward) mode does not use it and it - // stays always zero there. - Tag uint8 -} - -// QIno = Qualified Inode number. -// Uniquely identifies a backing file through the device number, -// inode number pair. -type QIno struct { - namespaceData - // Stat_t.Ino is uint64 on 32- and 64-bit Linu - Ino uint64 -} - -// NewQIno returns a filled QIno struct -func NewQIno(dev uint64, tag uint8, ino uint64) QIno { - return QIno{ - namespaceData: namespaceData{ - Dev: dev, - Tag: tag, - }, - Ino: ino, - } -} - -// QInoFromStat fills a new QIno struct with the passed Stat_t info. -func QInoFromStat(st *syscall.Stat_t) QIno { - // There are some architectures that use 32-bit values here - // (darwin, freebsd-32, maybe others). Add an explicit cast to make - // this function work everywhere. - return NewQIno(uint64(st.Dev), 0, uint64(st.Ino)) -} diff --git a/internal/nametransform/diriv.go b/internal/nametransform/diriv.go index 1d27aa5..67635b3 100644 --- a/internal/nametransform/diriv.go +++ b/internal/nametransform/diriv.go @@ -8,9 +8,8 @@ import ( "path/filepath" "syscall" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" + "../cryptocore" + "../syscallcompat" ) const ( @@ -68,7 +67,6 @@ func WriteDirIVAt(dirfd int) error { // https://github.com/rfjakob/gocryptfs/commit/7d38f80a78644c8ec4900cc990bfb894387112ed fd, err := syscallcompat.Openat(dirfd, DirIVFilename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, dirivPerms) if err != nil { - tlog.Warn.Printf("WriteDirIV: Openat: %v", err) return err } // Wrap the fd in an os.File - we need the write retry logic. @@ -77,16 +75,12 @@ func WriteDirIVAt(dirfd int) error { if err != nil { f.Close() // It is normal to get ENOSPC here - if !syscallcompat.IsENOSPC(err) { - tlog.Warn.Printf("WriteDirIV: Write: %v", err) - } // Delete incomplete gocryptfs.diriv file syscallcompat.Unlinkat(dirfd, DirIVFilename, 0) return err } err = f.Close() if err != nil { - tlog.Warn.Printf("WriteDirIV: Close: %v", err) // Delete incomplete gocryptfs.diriv file syscallcompat.Unlinkat(dirfd, DirIVFilename, 0) return err diff --git a/internal/nametransform/longnames.go b/internal/nametransform/longnames.go index 74ddb07..21e7714 100644 --- a/internal/nametransform/longnames.go +++ b/internal/nametransform/longnames.go @@ -9,8 +9,7 @@ import ( "strings" "syscall" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/internal/tlog" + "../syscallcompat" ) const ( @@ -113,9 +112,6 @@ func ReadLongNameAt(dirfd int, cName string) (string, error) { // This function is symlink-safe through the use of Unlinkat(). func DeleteLongNameAt(dirfd int, hashName string) error { err := syscallcompat.Unlinkat(dirfd, hashName+LongNameSuffix, 0) - if err != nil { - tlog.Warn.Printf("DeleteLongName: %v", err) - } return err } @@ -141,25 +137,18 @@ func (n *NameTransform) WriteLongNameAt(dirfd int, hashName string, plainName st fdRaw, err := syscallcompat.Openat(dirfd, hashName+LongNameSuffix, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_EXCL, namePerms) if err != nil { - // Don't warn if the file already exists - this is allowed for renames - // and should be handled by the caller. - if err != syscall.EEXIST { - tlog.Warn.Printf("WriteLongName: Openat: %v", err) - } return err } fd := os.NewFile(uintptr(fdRaw), hashName+LongNameSuffix) _, err = fd.Write([]byte(cName)) if err != nil { fd.Close() - tlog.Warn.Printf("WriteLongName: Write: %v", err) // Delete incomplete longname file syscallcompat.Unlinkat(dirfd, hashName+LongNameSuffix, 0) return err } err = fd.Close() if err != nil { - tlog.Warn.Printf("WriteLongName: Close: %v", err) // Delete incomplete longname file syscallcompat.Unlinkat(dirfd, hashName+LongNameSuffix, 0) return err diff --git a/internal/nametransform/longnames_test.go b/internal/nametransform/longnames_test.go deleted file mode 100644 index 4210492..0000000 --- a/internal/nametransform/longnames_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package nametransform - -import ( - "testing" -) - -func TestIsLongName(t *testing.T) { - n := "gocryptfs.longname.LkwUdALvV_ANnzQN6ZZMYnxxfARD3IeZWCKnxGJjYmU=.name" - if NameType(n) != LongNameFilename { - t.Errorf("False negative") - } - - n = "gocryptfs.longname.LkwUdALvV_ANnzQN6ZZMYnxxfARD3IeZWCKnxGJjYmU=" - if NameType(n) != LongNameContent { - t.Errorf("False negative") - } - - n = "LkwUdALvV_ANnzQN6ZZMYnxxfARD3IeZWCKnxGJjYmU=" - if NameType(n) != LongNameNone { - t.Errorf("False positive") - } -} - -func TestRemoveLongNameSuffix(t *testing.T) { - filename := "gocryptfs.longname.LkwUdALvV_ANnzQN6ZZMYnxxfARD3IeZWCKnxGJjYmU=.name" - content := "gocryptfs.longname.LkwUdALvV_ANnzQN6ZZMYnxxfARD3IeZWCKnxGJjYmU=" - if RemoveLongNameSuffix(filename) != content { - t.Error(".name suffix not removed") - } -} diff --git a/internal/nametransform/names.go b/internal/nametransform/names.go index ca28230..fc7c7ff 100644 --- a/internal/nametransform/names.go +++ b/internal/nametransform/names.go @@ -8,8 +8,6 @@ import ( "syscall" "github.com/rfjakob/eme" - - "github.com/rfjakob/gocryptfs/internal/tlog" ) const ( @@ -88,22 +86,18 @@ func (n *NameTransform) decryptName(cipherName string, iv []byte) (string, error return "", err } if len(bin) == 0 { - tlog.Warn.Printf("DecryptName: empty input") return "", syscall.EBADMSG } if len(bin)%aes.BlockSize != 0 { - tlog.Debug.Printf("DecryptName %q: decoded length %d is not a multiple of 16", cipherName, len(bin)) return "", syscall.EBADMSG } bin = n.emeCipher.Decrypt(iv, bin) bin, err = unPad16(bin) if err != nil { - tlog.Warn.Printf("DecryptName %q: unPad16 error: %v", cipherName, err) return "", syscall.EBADMSG } plain := string(bin) if err := IsValidName(plain); err != nil { - tlog.Warn.Printf("DecryptName %q: invalid name after decryption: %v", cipherName, err) return "", syscall.EBADMSG } return plain, err @@ -116,7 +110,6 @@ func (n *NameTransform) decryptName(cipherName string, iv []byte) (string, error // to the full (not hashed) name if longname is used. func (n *NameTransform) EncryptName(plainName string, iv []byte) (cipherName64 string, err error) { if err := IsValidName(plainName); err != nil { - tlog.Warn.Printf("EncryptName %q: invalid plainName: %v", plainName, err) return "", syscall.EBADMSG } bin := []byte(plainName) diff --git a/internal/nametransform/names_test.go b/internal/nametransform/names_test.go deleted file mode 100644 index b4e98d4..0000000 --- a/internal/nametransform/names_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package nametransform - -import ( - "bytes" - "strings" - "testing" -) - -func TestPad16(t *testing.T) { - s := [][]byte{ - []byte("foo"), - []byte("12345678901234567"), - []byte("12345678901234567abcdefg"), - } - - for i := range s { - orig := s[i] - padded := pad16(orig) - if len(padded) <= len(orig) { - t.Errorf("Padded length not bigger than orig: %d", len(padded)) - } - if len(padded)%16 != 0 { - t.Errorf("Length is not aligend: %d", len(padded)) - } - unpadded, err := unPad16(padded) - if err != nil { - t.Error("unPad16 returned error:", err) - } - if len(unpadded) != len(orig) { - t.Errorf("Size mismatch: orig=%d unpadded=%d", len(s[i]), len(unpadded)) - } - if !bytes.Equal(orig, unpadded) { - t.Error("Content mismatch orig vs unpadded") - } - } -} - -// TestUnpad16Garbage - unPad16 should never crash on corrupt or malicious inputs -func TestUnpad16Garbage(t *testing.T) { - testCases := [][]byte{ - make([]byte, 0), - make([]byte, 16), - make([]byte, 1), - make([]byte, 17), - bytes.Repeat([]byte{16}, 16), - bytes.Repeat([]byte{17}, 16), - } - for _, v := range testCases { - _, err := unPad16([]byte(v)) - if err == nil { - t.Fail() - } - } -} - -func TestIsValidName(t *testing.T) { - cases := []struct { - in string - want bool - }{ - {"", false}, - {".", false}, - {"..", false}, - {"...", true}, - {"asdasd/asdasd", false}, - {"asdasd\000asdasd", false}, - {"hello", true}, - {strings.Repeat("x", 255), true}, - {strings.Repeat("x", 256), false}, - } - for _, c := range cases { - have := IsValidName(c.in) - if (have == nil) != c.want { - t.Errorf("IsValidName(%q): want %v have %v", c.in, c.want, have) - } - } -} diff --git a/internal/openfiletable/open_file_table.go b/internal/openfiletable/open_file_table.go deleted file mode 100644 index dfd9637..0000000 --- a/internal/openfiletable/open_file_table.go +++ /dev/null @@ -1,103 +0,0 @@ -// Package openfiletable maintains a table of currently opened files, identified -// by the device number + inode number pair. This table is used by fusefrontend -// to centrally store the current file ID and to lock files against concurrent -// writes. -package openfiletable - -import ( - "sync" - "sync/atomic" - - "github.com/rfjakob/gocryptfs/internal/inomap" -) - -// wlock - serializes write accesses to each file (identified by inode number) -// Writing partial blocks means we have to do read-modify-write cycles. We -// really don't want concurrent writes there. -// Concurrent full-block writes could actually be allowed, but are not to -// keep the locking simple. -var t table - -func init() { - t.entries = make(map[inomap.QIno]*Entry) -} - -type table struct { - // writeOpCount counts entry.ContentLock.Lock() calls. As every operation that - // modifies a file should - // call it, this effectively serves as a write-operation counter. - // The variable is accessed without holding any locks so atomic operations - // must be used. It must be the first element of the struct to guarantee - // 64-bit alignment. - writeOpCount uint64 - // Protects map access - sync.Mutex - // Table entries - entries map[inomap.QIno]*Entry -} - -// Entry is an entry in the open file table -type Entry struct { - // Reference count. Protected by the table lock. - refCount int - // ContentLock protects on-disk content from concurrent writes. Every writer - // must take this lock before modifying the file content. - ContentLock countingMutex - // ID is the file ID in the file header. - ID []byte - // IDLock must be taken before reading or writing the ID field in this struct, - // unless you have an exclusive lock on ContentLock. - IDLock sync.Mutex -} - -// Register creates an open file table entry for "qi" (or incrementes the -// reference count if the entry already exists) and returns the entry. -func Register(qi inomap.QIno) *Entry { - t.Lock() - defer t.Unlock() - - e := t.entries[qi] - if e == nil { - e = &Entry{} - t.entries[qi] = e - } - e.refCount++ - return e -} - -// Unregister decrements the reference count for "qi" and deletes the entry from -// the open file table if the reference count reaches 0. -func Unregister(qi inomap.QIno) { - t.Lock() - defer t.Unlock() - - e := t.entries[qi] - e.refCount-- - if e.refCount == 0 { - delete(t.entries, qi) - } -} - -// countingMutex incrementes t.writeLockCount on each Lock() call. -type countingMutex struct { - sync.RWMutex -} - -func (c *countingMutex) Lock() { - c.RWMutex.Lock() - atomic.AddUint64(&t.writeOpCount, 1) -} - -// WriteOpCount returns the write lock counter value. This value is incremented -// each time writeLock.Lock() on a file table entry is called. -func WriteOpCount() uint64 { - return atomic.LoadUint64(&t.writeOpCount) -} - -// CountOpenFiles returns how many entries are currently in the table -// in a threadsafe manner. -func CountOpenFiles() int { - t.Lock() - defer t.Unlock() - return len(t.entries) -} diff --git a/internal/pathiv/pathiv.go b/internal/pathiv/pathiv.go index 08042e9..db0f7af 100644 --- a/internal/pathiv/pathiv.go +++ b/internal/pathiv/pathiv.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "encoding/binary" - "github.com/rfjakob/gocryptfs/internal/nametransform" + "../nametransform" ) // Purpose identifies for which purpose the IV will be used. This is mixed into the diff --git a/internal/readpassword/extpass_test.go b/internal/readpassword/extpass_test.go deleted file mode 100644 index 9a643a5..0000000 --- a/internal/readpassword/extpass_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package readpassword - -import ( - "os" - "os/exec" - "testing" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -var testPw = []byte("test") - -func TestMain(m *testing.M) { - // Shut up info output - tlog.Info.Enabled = false - os.Exit(m.Run()) -} - -func TestExtpass(t *testing.T) { - p1 := "ads2q4tw41reg52" - p2 := string(readPasswordExtpass([]string{"echo " + p1})) - if p1 != p2 { - t.Errorf("p1=%q != p2=%q", p1, p2) - } -} - -func TestOnceExtpass(t *testing.T) { - p1 := "lkadsf0923rdfi48rqwhdsf" - p2 := string(Once([]string{"echo " + p1}, nil, "")) - if p1 != p2 { - t.Errorf("p1=%q != p2=%q", p1, p2) - } -} - -// extpass with two arguments -func TestOnceExtpass2(t *testing.T) { - p1 := "foo" - p2 := string(Once([]string{"echo", p1}, nil, "")) - if p1 != p2 { - t.Errorf("p1=%q != p2=%q", p1, p2) - } -} - -// extpass with three arguments -func TestOnceExtpass3(t *testing.T) { - p1 := "foo bar baz" - p2 := string(Once([]string{"echo", "foo", "bar", "baz"}, nil, "")) - if p1 != p2 { - t.Errorf("p1=%q != p2=%q", p1, p2) - } -} - -func TestOnceExtpassSpaces(t *testing.T) { - p1 := "mypassword" - p2 := string(Once([]string{"cat", "passfile_test_files/file with spaces.txt"}, nil, "")) - if p1 != p2 { - t.Errorf("p1=%q != p2=%q", p1, p2) - } -} - -func TestTwiceExtpass(t *testing.T) { - p1 := "w5w44t3wfe45srz434" - p2 := string(Once([]string{"echo " + p1}, nil, "")) - if p1 != p2 { - t.Errorf("p1=%q != p2=%q", p1, p2) - } -} - -// When extpass returns an empty string, we should crash. -// -// The TEST_SLAVE magic is explained at -// https://talks.golang.org/2014/testing.slide#23 . -func TestExtpassEmpty(t *testing.T) { - if os.Getenv("TEST_SLAVE") == "1" { - readPasswordExtpass([]string{"echo"}) - return - } - cmd := exec.Command(os.Args[0], "-test.run=TestExtpassEmpty$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - err := cmd.Run() - if err != nil { - return - } - t.Fatal("empty password should have failed") -} diff --git a/internal/readpassword/passfile.go b/internal/readpassword/passfile.go deleted file mode 100644 index df6cd4d..0000000 --- a/internal/readpassword/passfile.go +++ /dev/null @@ -1,53 +0,0 @@ -package readpassword - -import ( - "bytes" - "os" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// readPassFileConcatenate reads the first line from each file name and -// concatenates the results. The result does not contain any newlines. -func readPassFileConcatenate(passfileSlice []string) (result []byte) { - for _, e := range passfileSlice { - result = append(result, readPassFile(e)...) - } - return result -} - -// readPassFile reads the first line from the passed file name. -func readPassFile(passfile string) []byte { - tlog.Info.Printf("passfile: reading from file %q", passfile) - f, err := os.Open(passfile) - if err != nil { - tlog.Fatal.Printf("fatal: passfile: could not open %q: %v", passfile, err) - os.Exit(exitcodes.ReadPassword) - } - defer f.Close() - // +1 for an optional trailing newline, - // +2 so we can detect if maxPasswordLen is exceeded. - buf := make([]byte, maxPasswordLen+2) - n, err := f.Read(buf) - if err != nil { - tlog.Fatal.Printf("fatal: passfile: could not read from %q: %v", passfile, err) - os.Exit(exitcodes.ReadPassword) - } - buf = buf[:n] - // Split into first line and "trailing garbage" - lines := bytes.SplitN(buf, []byte("\n"), 2) - if len(lines[0]) == 0 { - tlog.Fatal.Printf("fatal: passfile: empty first line in %q", passfile) - os.Exit(exitcodes.ReadPassword) - } - if len(lines[0]) > maxPasswordLen { - tlog.Fatal.Printf("fatal: passfile: max password length (%d bytes) exceeded", maxPasswordLen) - os.Exit(exitcodes.ReadPassword) - } - if len(lines) > 1 && len(lines[1]) > 0 { - tlog.Warn.Printf("warning: passfile: ignoring trailing garbage (%d bytes) after first line", - len(lines[1])) - } - return lines[0] -} diff --git a/internal/readpassword/passfile_test.go b/internal/readpassword/passfile_test.go deleted file mode 100644 index dbfe159..0000000 --- a/internal/readpassword/passfile_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package readpassword - -import ( - "os" - "os/exec" - "testing" -) - -func TestPassfile(t *testing.T) { - testcases := []struct { - file string - want string - }{ - {"mypassword.txt", "mypassword"}, - {"mypassword_garbage.txt", "mypassword"}, - {"mypassword_missing_newline.txt", "mypassword"}, - {"file with spaces.txt", "mypassword"}, - } - for _, tc := range testcases { - pw := readPassFile("passfile_test_files/" + tc.file) - if string(pw) != tc.want { - t.Errorf("Wrong result: want=%q have=%q", tc.want, pw) - } - // Calling readPassFileConcatenate with only one element should give the - // same result - pw = readPassFileConcatenate([]string{"passfile_test_files/" + tc.file}) - if string(pw) != tc.want { - t.Errorf("Wrong result: want=%q have=%q", tc.want, pw) - } - } -} - -// readPassFile() should exit instead of returning an empty string. -// -// The TEST_SLAVE magic is explained at -// https://talks.golang.org/2014/testing.slide#23 , mirror: -// http://web.archive.org/web/20200426174352/https://talks.golang.org/2014/testing.slide#23 -func TestPassfileEmpty(t *testing.T) { - if os.Getenv("TEST_SLAVE") == "1" { - readPassFile("passfile_test_files/empty.txt") - return - } - cmd := exec.Command(os.Args[0], "-test.run=TestPassfileEmpty$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - err := cmd.Run() - if err != nil { - return - } - t.Fatal("should have exited") -} - -// File containing just a newline. -// readPassFile() should exit instead of returning an empty string. -// -// The TEST_SLAVE magic is explained at -// https://talks.golang.org/2014/testing.slide#23 , mirror: -// http://web.archive.org/web/20200426174352/https://talks.golang.org/2014/testing.slide#23 -func TestPassfileNewline(t *testing.T) { - if os.Getenv("TEST_SLAVE") == "1" { - readPassFile("passfile_test_files/newline.txt") - return - } - cmd := exec.Command(os.Args[0], "-test.run=TestPassfileNewline$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - err := cmd.Run() - if err != nil { - return - } - t.Fatal("should have exited") -} - -// File containing "\ngarbage". -// readPassFile() should exit instead of returning an empty string. -// -// The TEST_SLAVE magic is explained at -// https://talks.golang.org/2014/testing.slide#23 , mirror: -// http://web.archive.org/web/20200426174352/https://talks.golang.org/2014/testing.slide#23 -func TestPassfileEmptyFirstLine(t *testing.T) { - if os.Getenv("TEST_SLAVE") == "1" { - readPassFile("passfile_test_files/empty_first_line.txt") - return - } - cmd := exec.Command(os.Args[0], "-test.run=TestPassfileEmptyFirstLine$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - err := cmd.Run() - if err != nil { - return - } - t.Fatal("should have exited") -} - -// TestPassFileConcatenate tests readPassFileConcatenate -func TestPassFileConcatenate(t *testing.T) { - files := []string{ - "passfile_test_files/file with spaces.txt", - "passfile_test_files/mypassword_garbage.txt", - } - res := string(readPassFileConcatenate(files)) - if res != "mypasswordmypassword" { - t.Errorf("wrong result: %q", res) - } -} diff --git a/internal/readpassword/passfile_test_files/empty.txt b/internal/readpassword/passfile_test_files/empty.txt deleted file mode 100644 index e69de29..0000000 diff --git a/internal/readpassword/passfile_test_files/empty_first_line.txt b/internal/readpassword/passfile_test_files/empty_first_line.txt deleted file mode 100644 index a607e80..0000000 --- a/internal/readpassword/passfile_test_files/empty_first_line.txt +++ /dev/null @@ -1,2 +0,0 @@ - -garbage diff --git a/internal/readpassword/passfile_test_files/file with spaces.txt b/internal/readpassword/passfile_test_files/file with spaces.txt deleted file mode 100644 index 48d23cf..0000000 --- a/internal/readpassword/passfile_test_files/file with spaces.txt +++ /dev/null @@ -1 +0,0 @@ -mypassword diff --git a/internal/readpassword/passfile_test_files/mypassword.txt b/internal/readpassword/passfile_test_files/mypassword.txt deleted file mode 100644 index 48d23cf..0000000 --- a/internal/readpassword/passfile_test_files/mypassword.txt +++ /dev/null @@ -1 +0,0 @@ -mypassword diff --git a/internal/readpassword/passfile_test_files/mypassword_garbage.txt b/internal/readpassword/passfile_test_files/mypassword_garbage.txt deleted file mode 100644 index 74ba741..0000000 --- a/internal/readpassword/passfile_test_files/mypassword_garbage.txt +++ /dev/null @@ -1,2 +0,0 @@ -mypassword -garbage diff --git a/internal/readpassword/passfile_test_files/mypassword_missing_newline.txt b/internal/readpassword/passfile_test_files/mypassword_missing_newline.txt deleted file mode 100644 index b3c42b5..0000000 --- a/internal/readpassword/passfile_test_files/mypassword_missing_newline.txt +++ /dev/null @@ -1 +0,0 @@ -mypassword \ No newline at end of file diff --git a/internal/readpassword/passfile_test_files/newline.txt b/internal/readpassword/passfile_test_files/newline.txt deleted file mode 100644 index 8b13789..0000000 --- a/internal/readpassword/passfile_test_files/newline.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/internal/readpassword/read.go b/internal/readpassword/read.go deleted file mode 100644 index e116f0b..0000000 --- a/internal/readpassword/read.go +++ /dev/null @@ -1,159 +0,0 @@ -// Package readpassword reads a password from the terminal of from stdin. -package readpassword - -import ( - "bytes" - "fmt" - "io" - "os" - "os/exec" - "strings" - - "golang.org/x/crypto/ssh/terminal" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -const ( - // 2kB limit like EncFS - maxPasswordLen = 2048 -) - -// Once tries to get a password from the user, either from the terminal, extpass, passfile -// or stdin. Leave "prompt" empty to use the default "Password: " prompt. -func Once(extpass []string, passfile []string, prompt string) []byte { - if len(passfile) != 0 { - return readPassFileConcatenate(passfile) - } - if len(extpass) != 0 { - return readPasswordExtpass(extpass) - } - if prompt == "" { - prompt = "Password" - } - if !terminal.IsTerminal(int(os.Stdin.Fd())) { - return readPasswordStdin(prompt) - } - return readPasswordTerminal(prompt + ": ") -} - -// Twice is the same as Once but will prompt twice if we get the password from -// the terminal. -func Twice(extpass []string, passfile []string) []byte { - if len(passfile) != 0 { - return readPassFileConcatenate(passfile) - } - if len(extpass) != 0 { - return readPasswordExtpass(extpass) - } - if !terminal.IsTerminal(int(os.Stdin.Fd())) { - return readPasswordStdin("Password") - } - p1 := readPasswordTerminal("Password: ") - p2 := readPasswordTerminal("Repeat: ") - if !bytes.Equal(p1, p2) { - tlog.Fatal.Println("Passwords do not match") - os.Exit(exitcodes.ReadPassword) - } - // Wipe the password duplicate from memory - for i := range p2 { - p2[i] = 0 - } - return p1 -} - -// readPasswordTerminal reads a line from the terminal. -// Exits on read error or empty result. -func readPasswordTerminal(prompt string) []byte { - fd := int(os.Stdin.Fd()) - fmt.Fprintf(os.Stderr, prompt) - // terminal.ReadPassword removes the trailing newline - p, err := terminal.ReadPassword(fd) - if err != nil { - tlog.Fatal.Printf("Could not read password from terminal: %v\n", err) - os.Exit(exitcodes.ReadPassword) - } - fmt.Fprintf(os.Stderr, "\n") - if len(p) == 0 { - tlog.Fatal.Println("Password is empty") - os.Exit(exitcodes.PasswordEmpty) - } - return p -} - -// readPasswordStdin reads a line from stdin. -// It exits with a fatal error on read error or empty result. -func readPasswordStdin(prompt string) []byte { - tlog.Info.Printf("Reading %s from stdin", prompt) - p := readLineUnbuffered(os.Stdin) - if len(p) == 0 { - tlog.Fatal.Printf("Got empty %s from stdin", prompt) - os.Exit(exitcodes.ReadPassword) - } - return p -} - -// readPasswordExtpass executes the "extpass" program and returns the first line -// of the output. -// Exits on read error or empty result. -func readPasswordExtpass(extpass []string) []byte { - var parts []string - if len(extpass) == 1 { - parts = strings.Split(extpass[0], " ") - } else { - parts = extpass - } - tlog.Info.Printf("Reading password from extpass program %q, arguments: %q\n", parts[0], parts[1:]) - cmd := exec.Command(parts[0], parts[1:]...) - cmd.Stderr = os.Stderr - pipe, err := cmd.StdoutPipe() - if err != nil { - tlog.Fatal.Printf("extpass pipe setup failed: %v", err) - os.Exit(exitcodes.ReadPassword) - } - err = cmd.Start() - if err != nil { - tlog.Fatal.Printf("extpass cmd start failed: %v", err) - os.Exit(exitcodes.ReadPassword) - } - p := readLineUnbuffered(pipe) - pipe.Close() - err = cmd.Wait() - if err != nil { - tlog.Fatal.Printf("extpass program returned an error: %v", err) - os.Exit(exitcodes.ReadPassword) - } - if len(p) == 0 { - tlog.Fatal.Println("extpass: password is empty") - os.Exit(exitcodes.ReadPassword) - } - return p -} - -// readLineUnbuffered reads single bytes from "r" util it gets "\n" or EOF. -// The returned string does NOT contain the trailing "\n". -func readLineUnbuffered(r io.Reader) (l []byte) { - b := make([]byte, 1) - for { - if len(l) > maxPasswordLen { - tlog.Fatal.Printf("fatal: maximum password length of %d bytes exceeded", maxPasswordLen) - os.Exit(exitcodes.ReadPassword) - } - n, err := r.Read(b) - if err == io.EOF { - return l - } - if err != nil { - tlog.Fatal.Printf("readLineUnbuffered: %v", err) - os.Exit(exitcodes.ReadPassword) - } - if n == 0 { - continue - } - if b[0] == '\n' { - return l - } - l = append(l, b...) - } -} diff --git a/internal/readpassword/stdin_test.go b/internal/readpassword/stdin_test.go deleted file mode 100644 index 01dd701..0000000 --- a/internal/readpassword/stdin_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package readpassword - -import ( - "fmt" - "os" - "os/exec" - "testing" -) - -// Provide password via stdin, terminated by "\n". -func TestStdin(t *testing.T) { - p1 := "g55434t55wef" - if os.Getenv("TEST_SLAVE") == "1" { - p2 := string(readPasswordStdin("foo")) - if p1 != p2 { - fmt.Fprintf(os.Stderr, "%q != %q", p1, p2) - os.Exit(1) - } - return - } - cmd := exec.Command(os.Args[0], "-test.run=TestStdin$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - cmd.Stderr = os.Stderr - pipe, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - n, err := pipe.Write([]byte(p1 + "\n")) - if n == 0 || err != nil { - t.Fatal(err) - } - err = cmd.Wait() - if err != nil { - t.Fatalf("slave failed with %v", err) - } -} - -// Provide password via stdin, terminated by EOF (pipe close). This should not -// hang. -func TestStdinEof(t *testing.T) { - p1 := "asd45as5f4a36" - if os.Getenv("TEST_SLAVE") == "1" { - p2 := string(readPasswordStdin("foo")) - if p1 != p2 { - fmt.Fprintf(os.Stderr, "%q != %q", p1, p2) - os.Exit(1) - } - return - } - cmd := exec.Command(os.Args[0], "-test.run=TestStdinEof$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - cmd.Stderr = os.Stderr - pipe, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - _, err = pipe.Write([]byte(p1)) - if err != nil { - t.Fatal(err) - } - pipe.Close() - err = cmd.Wait() - if err != nil { - t.Fatalf("slave failed with %v", err) - } -} - -// Provide empty password via stdin -func TestStdinEmpty(t *testing.T) { - if os.Getenv("TEST_SLAVE") == "1" { - readPasswordStdin("foo") - } - cmd := exec.Command(os.Args[0], "-test.run=TestStdinEmpty$") - cmd.Env = append(os.Environ(), "TEST_SLAVE=1") - pipe, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - _, err = pipe.Write([]byte("\n")) - if err != nil { - t.Fatal(err) - } - pipe.Close() - err = cmd.Wait() - if err == nil { - t.Fatalf("empty password should have failed") - } -} diff --git a/internal/serialize_reads/sr.go b/internal/serialize_reads/sr.go deleted file mode 100644 index 96cec4f..0000000 --- a/internal/serialize_reads/sr.go +++ /dev/null @@ -1,150 +0,0 @@ -package serialize_reads - -import ( - "log" - "sync" - "time" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// serializerState is used by the Wait and Done functions -type serializerState struct { - // we get submissions through the "input" channel - input chan *submission - // q = Queue - q []*submission - // wg is used to wait for the read to complete before unblocking the next - wg sync.WaitGroup -} - -// Wait places the caller into a queue and blocks -func Wait(offset int64, size int) { - serializer.wait(offset, size) -} - -// Done signals that the read operation has finished -func Done() { - serializer.wg.Done() -} - -type submission struct { - // "ch" is closed by "eventLoop" once it wants to unblock the caller - ch chan struct{} - // submissions are prioritized by offset (lowest offset gets unblocked first) - offset int64 - // size will be used in the future to detect consecutive read requests. These - // can be unblocked immediately. - size int -} - -func (sr *serializerState) wait(offset int64, size int) { - ch := make(chan struct{}) - sb := &submission{ - ch: ch, - offset: offset, - size: size, - } - // Send our submission - sr.input <- sb - // Wait till we get unblocked - <-ch -} - -// push returns true if the queue is full after the element has been stored. -// It panics if it did not have space to store the element. -func (sr *serializerState) push(sb *submission) (full bool) { - free := 0 - stored := false - for i, v := range sr.q { - if v != nil { - continue - } - if !stored { - sr.q[i] = sb - stored = true - continue - } - free++ - } - if !stored { - // This should never happen because eventLoop checks if the queue got full - log.Panic("BUG: unhandled queue overflow") - } - if free == 0 { - return true - } - return false -} - -// pop the submission with the lowest offset off the queue -func (sr *serializerState) pop() *submission { - var winner *submission - var winnerIndex int - for i, v := range sr.q { - if v == nil { - continue - } - if winner == nil { - winner = v - winnerIndex = i - continue - } - if v.offset < winner.offset { - winner = v - winnerIndex = i - } - } - if winner == nil { - return nil - } - sr.q[winnerIndex] = nil - return winner -} - -func (sr *serializerState) eventLoop() { - sr.input = make(chan *submission) - empty := true - for { - if empty { - // If the queue is empty we block on the channel to conserve CPU - sb := <-sr.input - sr.push(sb) - empty = false - } - select { - case sb := <-sr.input: - full := sr.push(sb) - if full { - // Queue is full, unblock the new request immediately - tlog.Warn.Printf("serialize_reads: queue full, forcing unblock") - sr.unblockOne() - } - case <-time.After(time.Microsecond * 500): - // Looks like we have waited out all concurrent requests. - empty = sr.unblockOne() - } - } -} - -// Unblock a submission and wait for completion -func (sr *serializerState) unblockOne() (empty bool) { - winner := sr.pop() - if winner == nil { - return true - } - sr.wg.Add(1) - close(winner.ch) - sr.wg.Wait() - return false -} - -var serializer serializerState - -// InitSerializer sets up the internal serializer state and starts the event loop. -// Called by fusefrontend.NewFS. -func InitSerializer() { - serializer.input = make(chan *submission) - serializer.q = make([]*submission, 10) - go serializer.eventLoop() -} diff --git a/internal/siv_aead/benchmark.bash b/internal/siv_aead/benchmark.bash deleted file mode 100755 index 40b57b3..0000000 --- a/internal/siv_aead/benchmark.bash +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -eu - -cd "$(dirname "$0")" - -../stupidgcm/benchmark.bash diff --git a/internal/siv_aead/correctness_test.go b/internal/siv_aead/correctness_test.go deleted file mode 100644 index b52774b..0000000 --- a/internal/siv_aead/correctness_test.go +++ /dev/null @@ -1,148 +0,0 @@ -package siv_aead - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/jacobsa/crypto/siv" -) - -// Test all supported key lengths -func TestKeyLens(t *testing.T) { - keyLens := []int{32, 48, 64} - nonce := make([]byte, 16) - plaintext := []byte("foobar") - for _, keyLen := range keyLens { - key := make([]byte, keyLen) - a := new2(key) - ciphertext2 := a.Seal(nil, nonce, plaintext, nil) - - ciphertext, err := siv.Encrypt(nil, key, plaintext, [][]byte{nil, nonce}) - if err != nil { - t.Error(err) - } else if o := len(ciphertext) - len(plaintext); o != a.Overhead() { - t.Errorf("keyLen=%d, actual overhead: %d\n", keyLen, o) - } - if !bytes.Equal(ciphertext, ciphertext2) { - t.Errorf("siv and siv_aead produce different results") - } - } - -} - -// Test using a 32-byte key -func TestK32(t *testing.T) { - key := bytes.Repeat([]byte{1}, 32) - nonce := bytes.Repeat([]byte{2}, 16) - plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} - aData := make([]byte, 24) - // Compare siv and siv_aead results - sResult, err := siv.Encrypt(nonce, key, plaintext, [][]byte{aData, nonce}) - if err != nil { - t.Fatal(err) - } - a := new2(key) - aResult := a.Seal(nonce, nonce, plaintext, aData) - if !bytes.Equal(sResult, aResult) { - t.Errorf("siv and siv_aead produce different results") - } - expectedResult, _ := hex.DecodeString( - "02020202020202020202020202020202ad7a4010649a84d8c1dd5f752e935eed57d45b8b10008f3834") - if !bytes.Equal(aResult, expectedResult) { - t.Errorf(hex.EncodeToString(aResult)) - } - // Verify overhead - overhead := len(aResult) - len(plaintext) - len(nonce) - if overhead != a.Overhead() { - t.Errorf("Overhead() returns a wrong value") - } - // Decrypt - p1, err := a.Open(nil, aResult[:16], aResult[16:], aData) - if err != nil { - t.Error(err) - } - if !bytes.Equal(plaintext, p1) { - t.Errorf("wrong plaintext") - } - // Decrypt and append - dst := []byte{0xaa, 0xbb, 0xcc} - p2, err := a.Open(dst, aResult[:16], aResult[16:], aData) - if err != nil { - t.Error(err) - } - p2e := append(dst, plaintext...) - if !bytes.Equal(p2e, p2) { - t.Errorf("wrong plaintext: %s", hex.EncodeToString(p2)) - } - // Decrypt corrupt - aResult[17] = 0 - _, err = a.Open(nil, aResult[:16], aResult[16:], aData) - if err == nil { - t.Error("should have failed") - } - // Decrypt and append corrupt - aResult[17] = 0 - _, err = a.Open(dst, aResult[:16], aResult[16:], aData) - if err == nil { - t.Error("should have failed") - } -} - -// Test using a 64-byte key -func TestK64(t *testing.T) { - key := bytes.Repeat([]byte{1}, 64) - nonce := bytes.Repeat([]byte{2}, 16) - plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} - aData := make([]byte, 24) - // Compare siv and siv_aead results - sResult, err := siv.Encrypt(nonce, key, plaintext, [][]byte{aData, nonce}) - if err != nil { - t.Fatal(err) - } - a := New(key) - aResult := a.Seal(nonce, nonce, plaintext, aData) - if !bytes.Equal(sResult, aResult) { - t.Errorf("siv and siv_aead produce different results") - } - expectedResult, _ := hex.DecodeString( - "02020202020202020202020202020202317b316f67c3ad336c01c9a01b4c5e552ba89e966bc4c1ade1") - if !bytes.Equal(aResult, expectedResult) { - t.Errorf(hex.EncodeToString(aResult)) - } - // Verify overhead - overhead := len(aResult) - len(plaintext) - len(nonce) - if overhead != a.Overhead() { - t.Errorf("Overhead() returns a wrong value") - } - // Decrypt - p1, err := a.Open(nil, aResult[:16], aResult[16:], aData) - if err != nil { - t.Error(err) - } - if !bytes.Equal(plaintext, p1) { - t.Errorf("wrong plaintext") - } - // Decrypt and append - dst := []byte{0xaa, 0xbb, 0xcc} - p2, err := a.Open(dst, aResult[:16], aResult[16:], aData) - if err != nil { - t.Error(err) - } - p2e := append(dst, plaintext...) - if !bytes.Equal(p2e, p2) { - t.Errorf("wrong plaintext: %s", hex.EncodeToString(p2)) - } - // Decrypt corrupt - aResult[17] = 0 - _, err = a.Open(nil, aResult[:16], aResult[16:], aData) - if err == nil { - t.Error("should have failed") - } - // Decrypt and append corrupt - aResult[17] = 0 - _, err = a.Open(dst, aResult[:16], aResult[16:], aData) - if err == nil { - t.Error("should have failed") - } -} diff --git a/internal/siv_aead/performance_test.go b/internal/siv_aead/performance_test.go deleted file mode 100644 index 626024e..0000000 --- a/internal/siv_aead/performance_test.go +++ /dev/null @@ -1 +0,0 @@ -package siv_aead diff --git a/internal/speed/benchmark.bash b/internal/speed/benchmark.bash deleted file mode 100755 index d2678a7..0000000 --- a/internal/speed/benchmark.bash +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -eu - -cd "$(dirname "$0")" - -go test -bench . diff --git a/internal/speed/speed.go b/internal/speed/speed.go deleted file mode 100644 index e097c55..0000000 --- a/internal/speed/speed.go +++ /dev/null @@ -1,148 +0,0 @@ -// Package speed implements the "-speed" command-line option, -// similar to "openssl speed". -// It benchmarks the crypto algorithms and libraries used by -// gocryptfs. -package speed - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "fmt" - "log" - "testing" - - "golang.org/x/crypto/chacha20poly1305" - - "github.com/rfjakob/gocryptfs/internal/siv_aead" - "github.com/rfjakob/gocryptfs/internal/stupidgcm" -) - -// 128-bit file ID + 64 bit block number = 192 bits = 24 bytes -const adLen = 24 - -// gocryptfs uses fixed-size 4 kiB blocks -const blockSize = 4096 - -// Run - run the speed the test and print the results. -func Run() { - bTable := []struct { - name string - f func(*testing.B) - preferred bool - }{ - {name: "AES-GCM-256-OpenSSL", f: bStupidGCM, preferred: stupidgcm.PreferOpenSSL()}, - {name: "AES-GCM-256-Go", f: bGoGCM, preferred: !stupidgcm.PreferOpenSSL()}, - {name: "AES-SIV-512-Go", f: bAESSIV, preferred: false}, - {name: "XChaCha20-Poly1305-Go", f: bChacha20poly1305, preferred: false}, - } - for _, b := range bTable { - fmt.Printf("%-20s\t", b.name) - mbs := mbPerSec(testing.Benchmark(b.f)) - if mbs > 0 { - fmt.Printf("%7.2f MB/s", mbs) - } else { - fmt.Printf(" N/A") - } - if b.preferred { - fmt.Printf("\t(selected in auto mode)\n") - } else if b.name == "XChaCha20-Poly1305-Go" { - fmt.Printf("\t(benchmark only, not selectable yet)\n") - } else { - fmt.Printf("\t\n") - } - } -} - -func mbPerSec(r testing.BenchmarkResult) float64 { - if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 { - return 0 - } - return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds() -} - -// Get "n" random bytes from /dev/urandom or panic -func randBytes(n int) []byte { - b := make([]byte, n) - _, err := rand.Read(b) - if err != nil { - log.Panic("Failed to read random bytes: " + err.Error()) - } - return b -} - -// bStupidGCM benchmarks stupidgcm's openssl GCM -func bStupidGCM(b *testing.B) { - if stupidgcm.BuiltWithoutOpenssl { - b.Skip("openssl has been disabled at compile-time") - } - key := randBytes(32) - authData := randBytes(adLen) - iv := randBytes(16) - in := make([]byte, blockSize) - b.SetBytes(int64(len(in))) - - sGCM := stupidgcm.New(key, false) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Encrypt and append to nonce - sGCM.Seal(iv, iv, in, authData) - } -} - -// bGoGCM benchmarks Go stdlib GCM -func bGoGCM(b *testing.B) { - key := randBytes(32) - authData := randBytes(adLen) - iv := randBytes(16) - in := make([]byte, blockSize) - b.SetBytes(int64(len(in))) - - gAES, err := aes.NewCipher(key) - if err != nil { - b.Fatal(err) - } - gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16) - if err != nil { - b.Fatal(err) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Encrypt and append to nonce - gGCM.Seal(iv, iv, in, authData) - } -} - -// bAESSIV benchmarks AES-SIV from github.com/jacobsa/crypto/siv -func bAESSIV(b *testing.B) { - key := randBytes(64) - authData := randBytes(adLen) - iv := randBytes(16) - in := make([]byte, blockSize) - b.SetBytes(int64(len(in))) - gGCM := siv_aead.New(key) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Encrypt and append to nonce - gGCM.Seal(iv, iv, in, authData) - } -} - -// bChacha20poly1305 benchmarks XChaCha20 from golang.org/x/crypto/chacha20poly1305 -func bChacha20poly1305(b *testing.B) { - key := randBytes(32) - authData := randBytes(adLen) - iv := randBytes(chacha20poly1305.NonceSizeX) - in := make([]byte, blockSize) - b.SetBytes(int64(len(in))) - c, _ := chacha20poly1305.NewX(key) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - // Encrypt and append to nonce - c.Seal(iv, iv, in, authData) - } -} diff --git a/internal/speed/speed_test.go b/internal/speed/speed_test.go deleted file mode 100644 index 1e9d859..0000000 --- a/internal/speed/speed_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package speed - -/* -Make the "-speed" benchmarks also accessible to the standard test system. -Example run: - -$ go test -bench . -BenchmarkStupidGCM-2 100000 22552 ns/op 181.62 MB/s -BenchmarkGoGCM-2 20000 81871 ns/op 50.03 MB/s -BenchmarkAESSIV-2 10000 104623 ns/op 39.15 MB/s -PASS -ok github.com/rfjakob/gocryptfs/internal/speed 6.022s -*/ - -import ( - "testing" -) - -func BenchmarkStupidGCM(b *testing.B) { - bStupidGCM(b) -} - -func BenchmarkGoGCM(b *testing.B) { - bGoGCM(b) -} - -func BenchmarkAESSIV(b *testing.B) { - bAESSIV(b) -} diff --git a/internal/stupidgcm/stupidgcm_test.go b/internal/stupidgcm/stupidgcm_test.go deleted file mode 100644 index 18732df..0000000 --- a/internal/stupidgcm/stupidgcm_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// +build !without_openssl - -// We compare against Go's built-in GCM implementation. Since stupidgcm only -// supports 128-bit IVs and Go only supports that from 1.5 onward, we cannot -// run these tests on older Go versions. -package stupidgcm - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/hex" - "log" - "testing" -) - -// Get "n" random bytes from /dev/urandom or panic -func randBytes(n int) []byte { - b := make([]byte, n) - _, err := rand.Read(b) - if err != nil { - log.Panic("Failed to read random bytes: " + err.Error()) - } - return b -} - -// TestEncryptDecrypt encrypts and decrypts using both stupidgcm and Go's built-in -// GCM implementation and verifies that the results are identical. -func TestEncryptDecrypt(t *testing.T) { - key := randBytes(32) - sGCM := New(key, false) - authData := randBytes(24) - iv := randBytes(16) - dst := make([]byte, 71) // 71 = random length - - gAES, err := aes.NewCipher(key) - if err != nil { - t.Fatal(err) - } - gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16) - if err != nil { - t.Fatal(err) - } - - // Check all block sizes from 1 to 5000 - for i := 1; i < 5000; i++ { - in := make([]byte, i) - - sOut := sGCM.Seal(dst, iv, in, authData) - gOut := gGCM.Seal(dst, iv, in, authData) - - // Ciphertext must be identical to Go GCM - if !bytes.Equal(sOut, gOut) { - t.Fatalf("Compare failed for encryption, size %d", i) - t.Log("sOut:") - t.Log("\n" + hex.Dump(sOut)) - t.Log("gOut:") - t.Log("\n" + hex.Dump(gOut)) - } - - sOut2, sErr := sGCM.Open(dst, iv, sOut[len(dst):], authData) - if sErr != nil { - t.Fatal(sErr) - } - gOut2, gErr := gGCM.Open(dst, iv, gOut[len(dst):], authData) - if gErr != nil { - t.Fatal(gErr) - } - - // Plaintext must be identical to Go GCM - if !bytes.Equal(sOut2, gOut2) { - t.Fatalf("Compare failed for decryption, size %d", i) - } - } -} - -// Seal re-uses the "dst" buffer it is large enough. -// Check that this works correctly by testing different "dst" capacities from -// 5000 to 16 and "in" lengths from 1 to 5000. -func TestInplaceSeal(t *testing.T) { - key := randBytes(32) - sGCM := New(key, false) - authData := randBytes(24) - iv := randBytes(16) - - gAES, err := aes.NewCipher(key) - if err != nil { - t.Fatal(err) - } - gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16) - if err != nil { - t.Fatal(err) - } - max := 5016 - // Check all block sizes from 1 to 5000 - for i := 1; i < max-16; i++ { - in := make([]byte, i) - dst := make([]byte, max-i) - dst = dst[:16] - - sOut := sGCM.Seal(dst, iv, in, authData) - dst2 := make([]byte, 16) - gOut := gGCM.Seal(dst2, iv, in, authData) - - // Ciphertext must be identical to Go GCM - if !bytes.Equal(sOut, gOut) { - t.Fatalf("Compare failed for encryption, size %d", i) - t.Log("sOut:") - t.Log("\n" + hex.Dump(sOut)) - t.Log("gOut:") - t.Log("\n" + hex.Dump(gOut)) - } - } -} - -// Open re-uses the "dst" buffer it is large enough. -// Check that this works correctly by testing different "dst" capacities from -// 5000 to 16 and "in" lengths from 1 to 5000. -func TestInplaceOpen(t *testing.T) { - key := randBytes(32) - sGCM := New(key, false) - authData := randBytes(24) - iv := randBytes(16) - - gAES, err := aes.NewCipher(key) - if err != nil { - t.Fatal(err) - } - gGCM, err := cipher.NewGCMWithNonceSize(gAES, 16) - if err != nil { - t.Fatal(err) - } - max := 5016 - // Check all block sizes from 1 to 5000 - for i := 1; i < max-16; i++ { - in := make([]byte, i) - - gCiphertext := gGCM.Seal(iv, iv, in, authData) - - dst := make([]byte, max-i) - // sPlaintext ... stupidgcm plaintext - sPlaintext, err := sGCM.Open(dst[:0], iv, gCiphertext[16:], authData) - if err != nil { - t.Fatal(err) - } - - // Plaintext must be identical to Go GCM - if !bytes.Equal(in, sPlaintext) { - t.Fatalf("Compare failed, i=%d", i) - } - } -} - -// TestCorruption verifies that changes in the ciphertext result in a decryption -// error -func TestCorruption(t *testing.T) { - key := randBytes(32) - sGCM := New(key, false) - authData := randBytes(24) - iv := randBytes(16) - - in := make([]byte, 354) - sOut := sGCM.Seal(nil, iv, in, authData) - sOut2, sErr := sGCM.Open(nil, iv, sOut, authData) - if sErr != nil { - t.Fatal(sErr) - } - if !bytes.Equal(in, sOut2) { - t.Fatalf("Compare failed") - } - - // Corrupt first byte - sOut[0]++ - sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) - if sErr == nil || sOut2 != nil { - t.Fatalf("Should have gotten error") - } - sOut[0]-- - - // Corrupt last byte - sOut[len(sOut)-1]++ - sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) - if sErr == nil || sOut2 != nil { - t.Fatalf("Should have gotten error") - } - sOut[len(sOut)-1]-- - - // Append one byte - sOut = append(sOut, 0) - sOut2, sErr = sGCM.Open(nil, iv, sOut, authData) - if sErr == nil || sOut2 != nil { - t.Fatalf("Should have gotten error") - } -} diff --git a/internal/stupidgcm/without_openssl.go b/internal/stupidgcm/without_openssl.go index deac342..d420da3 100644 --- a/internal/stupidgcm/without_openssl.go +++ b/internal/stupidgcm/without_openssl.go @@ -6,7 +6,7 @@ import ( "fmt" "os" - "github.com/rfjakob/gocryptfs/internal/exitcodes" + "../exitcodes" ) type StupidGCM struct{} diff --git a/internal/syscallcompat/emulate.go b/internal/syscallcompat/emulate.go deleted file mode 100644 index 91b592b..0000000 --- a/internal/syscallcompat/emulate.go +++ /dev/null @@ -1,29 +0,0 @@ -package syscallcompat - -import ( - "path/filepath" - "sync" - "syscall" -) - -var chdirMutex sync.Mutex - -// emulateMknodat emulates the syscall for platforms that don't have it -// in the kernel (darwin). -func emulateMknodat(dirfd int, path string, mode uint32, dev int) error { - if !filepath.IsAbs(path) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err - } - defer syscall.Close(cwd) - err = syscall.Fchdir(dirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) - } - return syscall.Mknod(path, mode, dev) -} diff --git a/internal/syscallcompat/emulate_test.go b/internal/syscallcompat/emulate_test.go deleted file mode 100644 index 16383f2..0000000 --- a/internal/syscallcompat/emulate_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package syscallcompat - -import ( - "os" - "testing" - - "golang.org/x/sys/unix" -) - -func TestEmulateMknodat(t *testing.T) { - err := emulateMknodat(tmpDirFd, "fifo1", unix.S_IFIFO, 0) - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/fifo1") - if err != nil { - t.Fatal(err) - } - // Test with absolute path - err = emulateMknodat(-1, tmpDir+"/fifo2", unix.S_IFIFO, 0) - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/fifo2") - if err != nil { - t.Fatal(err) - } -} diff --git a/internal/syscallcompat/getdents_linux.go b/internal/syscallcompat/getdents_linux.go index 852b3cd..df886b4 100644 --- a/internal/syscallcompat/getdents_linux.go +++ b/internal/syscallcompat/getdents_linux.go @@ -13,10 +13,6 @@ import ( "unsafe" "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/tlog" ) const sizeofDirent = int(unsafe.Sizeof(unix.Dirent{})) @@ -26,8 +22,13 @@ const sizeofDirent = int(unsafe.Sizeof(unix.Dirent{})) // See https://github.com/rfjakob/gocryptfs/issues/197 for details. const maxReclen = 280 +type DirEntry struct { + Name string + Mode uint32 +} + // getdents wraps unix.Getdents and converts the result to []fuse.DirEntry. -func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, err error) { +func getdents(fd int) (entries []DirEntry, entriesSpecial []DirEntry, err error) { // Collect syscall result in smartBuf. // "bytes.Buffer" is smart about expanding the capacity and avoids the // exponential runtime of simple append(). @@ -43,7 +44,6 @@ func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, continue } else if err != nil { if smartBuf.Len() > 0 { - tlog.Warn.Printf("warning: unix.Getdents returned errno %d in the middle of data ( https://github.com/rfjakob/gocryptfs/issues/483 )", err.(syscall.Errno)) return nil, nil, syscall.EIO } return nil, nil, err @@ -63,14 +63,10 @@ func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, for offset < len(buf) { s := *(*unix.Dirent)(unsafe.Pointer(&buf[offset])) if s.Reclen == 0 { - tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=0 at offset=%d. Returning EBADR", - numEntries, offset) // EBADR = Invalid request descriptor return nil, nil, syscall.EBADR } if int(s.Reclen) > maxReclen { - tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=%d > %d. Returning EBADR", - numEntries, s.Reclen, maxReclen) return nil, nil, syscall.EBADR } offset += int(s.Reclen) @@ -80,7 +76,7 @@ func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, // Note: syscall.ParseDirent() only returns the names, // we want all the data, so we have to implement // it on our own. - entries = make([]fuse.DirEntry, 0, numEntries) + entries = make([]DirEntry, 0, numEntries) offset = 0 for offset < len(buf) { s := *(*unix.Dirent)(unsafe.Pointer(&buf[offset])) @@ -91,8 +87,7 @@ func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, offset += int(s.Reclen) if name == "." || name == ".." { // These are always directories, no need to call convertDType. - entriesSpecial = append(entriesSpecial, fuse.DirEntry{ - Ino: s.Ino, + entriesSpecial = append(entriesSpecial, DirEntry{ Mode: syscall.S_IFDIR, Name: name, }) @@ -104,8 +99,7 @@ func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, // and go on. continue } - entries = append(entries, fuse.DirEntry{ - Ino: s.Ino, + entries = append(entries, DirEntry{ Mode: mode, Name: name, }) @@ -124,7 +118,6 @@ func getdentsName(s unix.Dirent) (string, error) { } } if l < 1 { - tlog.Warn.Printf("Getdents: invalid name length l=%d. Returning EBADR", l) // EBADR = Invalid request descriptor return "", syscall.EBADR } @@ -139,18 +132,8 @@ func getdentsName(s unix.Dirent) (string, error) { var dtUnknownWarnOnce sync.Once func dtUnknownWarn(dirfd int) { - const XFS_SUPER_MAGIC = 0x58465342 // From man 2 statfs var buf syscall.Statfs_t - err := syscall.Fstatfs(dirfd, &buf) - if err == nil && buf.Type == XFS_SUPER_MAGIC { - // Old XFS filesystems always return DT_UNKNOWN. Downgrade the message - // to "info" level if we are on XFS. - // https://github.com/rfjakob/gocryptfs/issues/267 - tlog.Info.Printf("Getdents: convertDType: received DT_UNKNOWN, fstype=xfs, falling back to stat") - } else { - tlog.Warn.Printf("Getdents: convertDType: received DT_UNKNOWN, fstype=%#x, falling back to stat", - buf.Type) - } + syscall.Fstatfs(dirfd, &buf) } // convertDType converts a Dirent.Type to at Stat_t.Mode value. diff --git a/internal/syscallcompat/getdents_other.go b/internal/syscallcompat/getdents_other.go deleted file mode 100644 index 1ed5ecf..0000000 --- a/internal/syscallcompat/getdents_other.go +++ /dev/null @@ -1,61 +0,0 @@ -package syscallcompat - -import ( - "os" - "syscall" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" -) - -func fillDirEntries(fd int, names []string) ([]fuse.DirEntry, error) { - out := make([]fuse.DirEntry, 0, len(names)) - for _, name := range names { - var st unix.Stat_t - err := Fstatat(fd, name, &st, unix.AT_SYMLINK_NOFOLLOW) - if err == syscall.ENOENT { - // File disappeared between readdir and stat. Pretend we did not - // see it. - continue - } - if err != nil { - return nil, err - } - newEntry := fuse.DirEntry{ - Name: name, - Mode: uint32(st.Mode) & syscall.S_IFMT, - Ino: st.Ino, - } - out = append(out, newEntry) - } - return out, nil -} - -// emulateGetdents reads all directory entries from the open directory "fd" -// and returns normal entries and "." / ".." split into two slices. -func emulateGetdents(fd int) (out []fuse.DirEntry, outSpecial []fuse.DirEntry, err error) { - // os.File closes the fd in its finalizer. Duplicate the fd to not affect - // the original fd. - newFd, err := syscall.Dup(fd) - if err != nil { - return nil, nil, err - } - f := os.NewFile(uintptr(newFd), "") - defer f.Close() - // Get all file names in the directory - names, err := f.Readdirnames(0) - if err != nil { - return nil, nil, err - } - // Stat all the names and convert to fuse.DirEntry - out, err = fillDirEntries(fd, names) - if err != nil { - return nil, nil, err - } - outSpecial, err = fillDirEntries(fd, []string{".", ".."}) - if err != nil { - return nil, nil, err - } - return out, outSpecial, nil -} diff --git a/internal/syscallcompat/getdents_test.go b/internal/syscallcompat/getdents_test.go deleted file mode 100644 index a6f41ca..0000000 --- a/internal/syscallcompat/getdents_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// +build linux - -package syscallcompat - -import ( - "io/ioutil" - "os" - "runtime" - "strings" - "syscall" - "testing" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" -) - -var emulate = false - -func TestGetdents(t *testing.T) { - t.Logf("testing native getdents") - testGetdents(t) - t.Logf("testing emulateGetdents") - emulate = true - testGetdents(t) -} - -// skipOnGccGo skips the emulateGetdents test when we are -// running linux and were compiled with gccgo. The test is known to fail -// (https://github.com/rfjakob/gocryptfs/issues/201), but getdents emulation -// is not used on linux, so let's skip the test and ignore the failure. -func skipOnGccGo(t *testing.T) { - if !emulate || runtime.GOOS != "linux" { - return - } - // runtime.Version() output... - // on go: go1.9.2 - // on gccgo: go1.8.1 gccgo (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2) - v := runtime.Version() - if strings.Contains(v, "gccgo") { - t.Skipf("test is known-broken on gccgo") - } -} - -func testGetdents(t *testing.T) { - getdentsUnderTest := getdents - if emulate { - getdentsUnderTest = emulateGetdents - } - // Fill a directory with filenames of length 1 ... 255 - testDir, err := ioutil.TempDir(tmpDir, "TestGetdents") - if err != nil { - t.Fatal(err) - } - for i := 1; i <= unix.NAME_MAX; i++ { - n := strings.Repeat("x", i) - err = ioutil.WriteFile(testDir+"/"+n, nil, 0600) - if err != nil { - t.Fatal(err) - } - } - // "/", "/dev" and "/proc/self" are good test cases because they contain - // many different file types (block and char devices, symlinks, - // mountpoints). - dirs := []string{testDir, "/", "/dev", "/proc/self"} - for _, dir := range dirs { - // Read directory using stdlib Readdir() - fd, err := os.Open(dir) - if err != nil { - t.Fatal(err) - } - defer fd.Close() - readdirEntries, err := fd.Readdir(0) - if err != nil { - t.Fatal(err) - } - readdirMap := make(map[string]*syscall.Stat_t) - for _, v := range readdirEntries { - readdirMap[v.Name()] = fuse.ToStatT(v) - } - // Read using our Getdents() implementation - _, err = fd.Seek(0, 0) // Rewind directory - if err != nil { - t.Fatal(err) - } - getdentsEntries, special, err := getdentsUnderTest(int(fd.Fd())) - if err != nil { - t.Log(err) - skipOnGccGo(t) - t.FailNow() - } - getdentsMap := make(map[string]fuse.DirEntry) - for _, v := range getdentsEntries { - getdentsMap[v.Name] = v - } - // Compare results - if len(getdentsEntries) != len(readdirEntries) { - t.Fatalf("len(getdentsEntries)=%d, len(readdirEntries)=%d", - len(getdentsEntries), len(readdirEntries)) - } - for name := range readdirMap { - g := getdentsMap[name] - r := readdirMap[name] - rTyp := r.Mode & syscall.S_IFMT - if g.Mode != rTyp { - t.Errorf("%q: g.Mode=%#o, r.Mode=%#o", name, g.Mode, rTyp) - } - if g.Ino != r.Ino { - // The inode number of a directory that is reported by stat - // and getdents is different when it is a mountpoint. Only - // throw an error when we are NOT looking at a directory. - if g.Mode != syscall.S_IFDIR { - t.Errorf("%s: g.Ino=%d, r.Ino=%d", name, g.Ino, r.Ino) - } - } - } - if len(special) != 2 { - t.Error(special) - } - if !(special[0].Name == "." && special[1].Name == ".." || - special[1].Name == "." && special[0].Name == "..") { - t.Error(special) - } - for _, v := range special { - if v.Ino == 0 { - t.Error(v) - } - if v.Mode != syscall.S_IFDIR { - t.Error(v) - } - } - } -} diff --git a/internal/syscallcompat/helpers.go b/internal/syscallcompat/helpers.go deleted file mode 100644 index e2a2215..0000000 --- a/internal/syscallcompat/helpers.go +++ /dev/null @@ -1,21 +0,0 @@ -package syscallcompat - -import ( - "os" - "syscall" -) - -// IsENOSPC tries to find out if "err" is a (potentially wrapped) ENOSPC error. -func IsENOSPC(err error) bool { - // syscallcompat.EnospcPrealloc returns the naked syscall error - if err == syscall.ENOSPC { - return true - } - // os.File.WriteAt returns &PathError - if err2, ok := err.(*os.PathError); ok { - if err2.Err == syscall.ENOSPC { - return true - } - } - return false -} diff --git a/internal/syscallcompat/main_test.go b/internal/syscallcompat/main_test.go deleted file mode 100644 index ddf6bc4..0000000 --- a/internal/syscallcompat/main_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package syscallcompat - -import ( - "fmt" - "io/ioutil" - "os" - "testing" -) - -var tmpDir string -var tmpDirFd int - -func TestMain(m *testing.M) { - origWd, err := os.Getwd() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - // Cannot import test_helpers because of import cycle - parent := fmt.Sprintf("/tmp/gocryptfs-test-parent-%d", os.Getuid()) - err = os.MkdirAll(parent, 0700) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - tmpDir, err = ioutil.TempDir(parent, "syscallcompat") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - dirf, err := os.Open(tmpDir) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - defer dirf.Close() - tmpDirFd = int(dirf.Fd()) - // Run the tests - r := m.Run() - // Check that we are in the same directory again (the emulated syscalls - // use Fchdir a lot) - cwd, _ := os.Getwd() - if cwd != origWd { - fmt.Printf("working dir has changed from %q to %q", origWd, cwd) - os.Exit(1) - } - os.Exit(r) -} diff --git a/internal/syscallcompat/open_nofollow.go b/internal/syscallcompat/open_nofollow.go index f8e50e3..150eb0f 100644 --- a/internal/syscallcompat/open_nofollow.go +++ b/internal/syscallcompat/open_nofollow.go @@ -4,8 +4,6 @@ import ( "path/filepath" "strings" "syscall" - - "github.com/rfjakob/gocryptfs/internal/tlog" ) // OpenDirNofollow opens the dir at "relPath" in a way that is secure against @@ -16,11 +14,9 @@ import ( // Retries on EINTR. func OpenDirNofollow(baseDir string, relPath string) (fd int, err error) { if !filepath.IsAbs(baseDir) { - tlog.Warn.Printf("BUG: OpenDirNofollow called with relative baseDir=%q", baseDir) return -1, syscall.EINVAL } if filepath.IsAbs(relPath) { - tlog.Warn.Printf("BUG: OpenDirNofollow called with absolute relPath=%q", relPath) return -1, syscall.EINVAL } // Open the base dir (following symlinks) diff --git a/internal/syscallcompat/open_nofollow_test.go b/internal/syscallcompat/open_nofollow_test.go deleted file mode 100644 index 1eeac3a..0000000 --- a/internal/syscallcompat/open_nofollow_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package syscallcompat - -import ( - "os" - "syscall" - "testing" -) - -func TestOpenNofollow(t *testing.T) { - err := os.MkdirAll(tmpDir+"/d1/d2/d3", 0700) - if err != nil { - t.Fatal(err) - } - // Create a file - dirfd, err := OpenDirNofollow(tmpDir, "d1/d2/d3") - if err != nil { - t.Fatal(err) - } - fd, err := Openat(dirfd, "f1", syscall.O_RDWR|syscall.O_CREAT|syscall.O_EXCL, 0600) - if err != nil { - t.Fatal(err) - } - syscall.Close(fd) - _, err = os.Stat(tmpDir + "/d1/d2/d3/f1") - if err != nil { - t.Fatal(err) - } - // Replace "d1" with a symlink - open should fail with ELOOP - err = os.Rename(tmpDir+"/d1", tmpDir+"/d1.renamed") - if err != nil { - t.Fatal(err) - } - os.Symlink(tmpDir+"/d1.renamed", tmpDir+"/d1") - fd, err = OpenDirNofollow(tmpDir, "d1/d2/d3") - if err == nil { - t.Fatalf("should have failed") - } - if err != syscall.ELOOP && err != syscall.ENOTDIR { - t.Errorf("expected ELOOP or ENOTDIR, got %v", err) - } - // Check to see that the base dir can be opened as well - fd, err = OpenDirNofollow(tmpDir, "") - if err != nil { - t.Errorf("cannot open base dir: %v", err) - } else { - syscall.Close(fd) - } -} diff --git a/internal/syscallcompat/sys_common.go b/internal/syscallcompat/sys_common.go index fc020bd..0123b25 100644 --- a/internal/syscallcompat/sys_common.go +++ b/internal/syscallcompat/sys_common.go @@ -5,8 +5,6 @@ import ( "syscall" "golang.org/x/sys/unix" - - "github.com/rfjakob/gocryptfs/internal/tlog" ) // PATH_MAX is the maximum allowed path length on Linux. @@ -47,16 +45,9 @@ func Faccessat(dirfd int, path string, mode uint32) error { // Openat wraps the Openat syscall. // Retries on EINTR. func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) { - if flags&syscall.O_CREAT != 0 { - // O_CREAT should be used with O_EXCL. O_NOFOLLOW has no effect with O_EXCL. - if flags&syscall.O_EXCL == 0 { - tlog.Warn.Printf("Openat: O_CREAT without O_EXCL: flags = %#x", flags) - flags |= syscall.O_EXCL - } - } else { + if flags&syscall.O_CREAT == 0 { // If O_CREAT is not used, we should use O_NOFOLLOW if flags&syscall.O_NOFOLLOW == 0 { - tlog.Warn.Printf("Openat: O_NOFOLLOW missing: flags = %#x", flags) flags |= syscall.O_NOFOLLOW } } @@ -70,7 +61,6 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { // Why would we ever want to call this without AT_SYMLINK_NOFOLLOW? if flags&unix.AT_SYMLINK_NOFOLLOW == 0 { - tlog.Warn.Printf("Fchownat: adding missing AT_SYMLINK_NOFOLLOW flag") flags |= unix.AT_SYMLINK_NOFOLLOW } return unix.Fchownat(dirfd, path, uid, gid, flags) @@ -81,7 +71,6 @@ func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) { // Why would we ever want to call this without AT_SYMLINK_NOFOLLOW? if flags&unix.AT_SYMLINK_NOFOLLOW == 0 { - tlog.Warn.Printf("Fstatat: adding missing AT_SYMLINK_NOFOLLOW flag") flags |= unix.AT_SYMLINK_NOFOLLOW } err = retryEINTR(func() error { diff --git a/internal/syscallcompat/sys_common_test.go b/internal/syscallcompat/sys_common_test.go deleted file mode 100644 index 7141b92..0000000 --- a/internal/syscallcompat/sys_common_test.go +++ /dev/null @@ -1,327 +0,0 @@ -package syscallcompat - -import ( - "bytes" - "os" - "runtime" - "syscall" - "testing" - - "golang.org/x/sys/unix" -) - -func TestReadlinkat(t *testing.T) { - for _, targetLen := range []int{100, 500, 4000} { - target := string(bytes.Repeat([]byte("x"), targetLen)) - err := syscall.Symlink(target, tmpDir+"/readlinkat") - if err != nil { - if targetLen > 1000 { - // Symlinks longer than 1024 (?) bytes are not supported on - // MacOS and XFS - t.Logf("skipping targetLen=%d: %v", targetLen, err) - continue - } - t.Fatalf("targetLen=%d: %v", targetLen, err) - } - target2, err := Readlinkat(tmpDirFd, "readlinkat") - if err != nil { - t.Fatal(err) - } - if target != target2 { - t.Errorf("target=%q != target2=%q", target, target2) - } - err = syscall.Unlink(tmpDir + "/readlinkat") - if err != nil { - t.Fatal(err) - } - } -} - -func TestOpenat(t *testing.T) { - // Always pass O_NOFOLLOW to avoid this warning: - // Openat: O_NOFOLLOW missing: flags = 0x0" - _, err := Openat(tmpDirFd, "testOpenAt", syscall.O_NOFOLLOW, 0) - if err == nil { - t.Errorf("should have failed") - } - fd, err := os.Create(tmpDir + "/testOpenAt") - if err != nil { - t.Fatal(err) - } - fd.Close() - rawFd, err := Openat(tmpDirFd, "testOpenAt", syscall.O_NOFOLLOW, 0) - if err != nil { - t.Fatal(err) - } - defer syscall.Close(rawFd) - if rawFd < 0 { - t.Fatalf("rawFd=%d", rawFd) - } - // Test with absolute path - rawFd, err = Openat(-1, tmpDir+"/testOpenAt", syscall.O_NOFOLLOW, 0) - if err != nil { - t.Fatal(err) - } - defer syscall.Close(rawFd) - if rawFd < 0 { - t.Fatalf("rawFd=%d", rawFd) - } -} - -func TestRenameat(t *testing.T) { - os.Mkdir(tmpDir+"/dir1", 0700) - dir1, err := os.Open(tmpDir + "/dir1") - if err != nil { - t.Fatal(err) - } - defer dir1.Close() - os.Mkdir(tmpDir+"/dir2", 0700) - dir2, err := os.Open(tmpDir + "/dir2") - if err != nil { - t.Fatal(err) - } - defer dir2.Close() - fd, err := os.Create(tmpDir + "/dir1/f1") - if err != nil { - t.Fatal(err) - } - fd.Close() - err = Renameat(int(dir1.Fd()), "f1", int(dir2.Fd()), "f2") - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/dir2/f2") - if err != nil { - t.Fatal(err) - } - // Test with absolute path - err = Renameat(-1, tmpDir+"/dir2/f2", -1, tmpDir+"/dir2/f1") - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/dir2/f1") - if err != nil { - t.Fatal(err) - } -} - -func TestUnlinkat(t *testing.T) { - os.Mkdir(tmpDir+"/unlink1", 0700) - dirfd, err := os.Open(tmpDir + "/unlink1") - if err != nil { - t.Fatal(err) - } - defer dirfd.Close() - // Try to delete file - fd, err := os.Create(tmpDir + "/unlink1/f1") - if err != nil { - t.Fatal(err) - } - fd.Close() - err = Unlinkat(int(dirfd.Fd()), "f1", 0) - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/unlink1/f1") - if err == nil { - t.Fatalf("file not deleted!") - } - // Try to delete dir - err = os.Mkdir(tmpDir+"/unlink1/d1", 0700) - if err != nil { - t.Fatal(err) - } - err = Unlinkat(int(dirfd.Fd()), "d1", 0) - if err == nil { - t.Fatalf("this should fail due to missing AT_REMOVEDIR flag") - } - err = Unlinkat(int(dirfd.Fd()), "d1", unix.AT_REMOVEDIR) - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/unlink1/d1") - if err == nil { - t.Fatalf("dir not deleted!") - } - // Test with absolute path - err = os.Mkdir(tmpDir+"/unlink1/d1", 0700) - if err != nil { - t.Fatal(err) - } - err = Unlinkat(-1, tmpDir+"/unlink1/d1", unix.AT_REMOVEDIR) - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(tmpDir + "/unlink1/d1") - if err == nil { - t.Fatalf("dir not deleted!") - } -} - -func TestFchmodatNofollow(t *testing.T) { - regular := "TestFchmodat_Regular" - f, err := os.OpenFile(tmpDir+"/"+regular, os.O_CREATE|os.O_WRONLY, 0000) - if err != nil { - t.Fatal(err) - } - f.Close() - symlink := "TestFchmodat_Symlink" - err = syscall.Symlink(regular, tmpDir+"/"+symlink) - if err != nil { - t.Fatal(err) - } - dirfd, err := syscall.Open(tmpDir, syscall.O_RDONLY, 0) - if err != nil { - t.Fatal(err) - } - defer syscall.Close(dirfd) - - // Check that chmod on a regular file works ok - err = FchmodatNofollow(dirfd, regular, 0111) - if err != nil { - t.Fatal(err) - } - var st syscall.Stat_t - syscall.Lstat(tmpDir+"/"+regular, &st) - st.Mode &= 0777 - if st.Mode != 0111 { - t.Errorf("wrong mode: %#0o", st.Mode) - } - err = FchmodatNofollow(dirfd, regular, 0000) - if err != nil { - t.Error(err) - } - syscall.Lstat(tmpDir+"/"+regular, &st) - st.Mode &= 0777 - if st.Mode != 0000 { - t.Errorf("wrong mode: %#0o", st.Mode) - } - - // Check what happens on a symlink - err = FchmodatNofollow(dirfd, symlink, 0333) - // On Darwin, permissions on symlinks are significant and can be changed. On - // Linux they are ignored, and FchmodatNofollow rejects attempts to change - // them. - if err == nil && runtime.GOOS == "linux" { - syscall.Lstat(tmpDir+"/"+symlink, &st) - st.Mode &= 0777 - t.Errorf("chmod on symlink should have failed, but did not. New mode=%#0o", st.Mode) - } - syscall.Lstat(tmpDir+"/"+regular, &st) - st.Mode &= 0777 - if st.Mode != 0000 { - t.Errorf("chmod on symlink affected symlink target: New mode=%#0o", st.Mode) - } -} - -// symlinkCheckMode looks if the mode bits in "st" say that this is a symlink. -// Calls t.Fatal() if not. -func symlinkCheckMode(t *testing.T, st syscall.Stat_t) { - if runtime.GOOS == "darwin" { - // On MacOS, symlinks don't carry their own permissions, so - // only check the file type. - if st.Mode&syscall.S_IFMT != syscall.S_IFLNK { - t.Fatalf("This is not a symlink: mode = 0%o", st.Mode) - } - return - } - if st.Mode != 0120777 { - t.Fatalf("Wrong mode, have 0%o, want 0120777", st.Mode) - } -} - -// We used to have our own wrapper for Symlinkat. The wrapper is gone but the test -// is still useful. -func TestSymlinkat(t *testing.T) { - err := unix.Symlinkat("/foo/bar/baz", tmpDirFd, "symlink1") - if err != nil { - t.Fatal(err) - } - var st syscall.Stat_t - err = syscall.Lstat(tmpDir+"/symlink1", &st) - if err != nil { - t.Fatal(err) - } - symlinkCheckMode(t, st) - // Test with absolute path - err = unix.Symlinkat("/foo/bar/baz", -1, tmpDir+"/symlink2") - if err != nil { - t.Fatal(err) - } - err = syscall.Lstat(tmpDir+"/symlink2", &st) - if err != nil { - t.Fatal(err) - } - symlinkCheckMode(t, st) -} - -// We used to have our own wrapper for Mkdirat. The wrapper is gone but the test -// is still useful. -func TestMkdirat(t *testing.T) { - err := unix.Mkdirat(tmpDirFd, "mkdirat", 0700) - if err != nil { - t.Fatal(err) - } - fi, err := os.Stat(tmpDir + "/mkdirat") - if err != nil { - t.Fatal(err) - } - if !fi.IsDir() { - t.Fatalf("mkdirat did not create a directory") - } - // Test with absolute path - err = unix.Mkdirat(-1, tmpDir+"/mkdirat2", 0700) - if err != nil { - t.Fatal(err) - } - fi, err = os.Stat(tmpDir + "/mkdirat2") - if err != nil { - t.Fatal(err) - } - if !fi.IsDir() { - t.Fatalf("mkdirat did not create a directory") - } -} - -func TestFstatat(t *testing.T) { - var st unix.Stat_t - // stat a normal file (size 3) - f, err := os.Create(tmpDir + "/fstatat") - if err != nil { - t.Fatal(err) - } - _, err = f.Write([]byte("foo")) - if err != nil { - t.Fatal(err) - } - f.Close() - err = Fstatat(tmpDirFd, "fstatat", &st, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - t.Fatal(err) - } - if st.Size != 3 { - t.Errorf("wrong file size: %d", st.Size) - } - // stat a symlink and check that the size matches the length of the - // symlink target. This proves that we have stat'ed the symlink itself. - err = os.Symlink(tmpDir+"/fstatat", tmpDir+"/fstatatSymlink") - if err != nil { - t.Fatal(err) - } - err = Fstatat(tmpDirFd, "fstatatSymlink", &st, unix.AT_SYMLINK_NOFOLLOW) - if err != nil { - t.Fatal(err) - } - expectedSize := int64(len(tmpDir + "/fstatat")) - if st.Size != expectedSize { - t.Errorf("symlink size: expected=%d, got=%d", expectedSize, st.Size) - } -} - -// BenchmarkLgetxattr benchmarks Lgetxattr. Lgetxattr is very hot as the kernel -// queries security.capabilities for every file access. -func BenchmarkLgetxattr(b *testing.B) { - for i := 0; i < b.N; i++ { - Lgetxattr("/", "user.this.attr.does.not.exist") - } -} diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go deleted file mode 100644 index 075563f..0000000 --- a/internal/syscallcompat/sys_darwin.go +++ /dev/null @@ -1,230 +0,0 @@ -package syscallcompat - -import ( - "log" - "path/filepath" - "runtime" - "syscall" - "time" - "unsafe" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" -) - -const ( - // O_DIRECT means oncached I/O on Linux. No direct equivalent on MacOS and defined - // to zero there. - O_DIRECT = 0 - - // O_PATH is only defined on Linux - O_PATH = 0 - - // RENAME_NOREPLACE is only defined on Linux - RENAME_NOREPLACE = 0 - - // KAUTH_UID_NONE and KAUTH_GID_NONE are special values to - // revert permissions to the process credentials. - KAUTH_UID_NONE = ^uint32(0) - 100 - KAUTH_GID_NONE = ^uint32(0) - 100 -) - -// Unfortunately pthread_setugid_np does not have a syscall wrapper yet. -func pthread_setugid_np(uid uint32, gid uint32) (err error) { - _, _, e1 := syscall.RawSyscall(syscall.SYS_SETTID, uintptr(uid), uintptr(gid), 0) - if e1 != 0 { - err = e1 - } - return -} - -// Unfortunately fsetattrlist does not have a syscall wrapper yet. -func fsetattrlist(fd int, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) { - _, _, e1 := syscall.Syscall6(syscall.SYS_FSETATTRLIST, uintptr(fd), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0) - if e1 != 0 { - err = e1 - } - return -} - -// Setattrlist already has a syscall wrapper, but it is not exported. -func setattrlist(path *byte, list unsafe.Pointer, buf unsafe.Pointer, size uintptr, options int) (err error) { - _, _, e1 := syscall.Syscall6(syscall.SYS_SETATTRLIST, uintptr(unsafe.Pointer(path)), uintptr(list), uintptr(buf), uintptr(size), uintptr(options), 0) - if e1 != 0 { - err = e1 - } - return -} - -// Sorry, fallocate is not available on OSX at all and -// fcntl F_PREALLOCATE is not accessible from Go. -// See https://github.com/rfjakob/gocryptfs/issues/18 if you want to help. -func EnospcPrealloc(fd int, off int64, len int64) error { - return nil -} - -// See above. -func Fallocate(fd int, mode uint32, off int64, len int64) error { - return syscall.EOPNOTSUPP -} - -// Dup3 is not available on Darwin, so we use Dup2 instead. -func Dup3(oldfd int, newfd int, flags int) (err error) { - if flags != 0 { - log.Panic("darwin does not support dup3 flags") - } - return syscall.Dup2(oldfd, newfd) -} - -//////////////////////////////////////////////////////// -//// Emulated Syscalls (see emulate.go) //////////////// -//////////////////////////////////////////////////////// - -func OpenatUser(dirfd int, path string, flags int, mode uint32, context *fuse.Context) (fd int, err error) { - if context != nil { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - err = pthread_setugid_np(context.Owner.Uid, context.Owner.Gid) - if err != nil { - return -1, err - } - defer pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE) - } - - return Openat(dirfd, path, flags, mode) -} - -func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { - return emulateMknodat(dirfd, path, mode, dev) -} - -func MknodatUser(dirfd int, path string, mode uint32, dev int, context *fuse.Context) (err error) { - if context != nil { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - err = pthread_setugid_np(context.Owner.Uid, context.Owner.Gid) - if err != nil { - return err - } - defer pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE) - } - - return Mknodat(dirfd, path, mode, dev) -} - -func FchmodatNofollow(dirfd int, path string, mode uint32) (err error) { - return unix.Fchmodat(dirfd, path, mode, unix.AT_SYMLINK_NOFOLLOW) -} - -func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.Context) (err error) { - if context != nil { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - err = pthread_setugid_np(context.Owner.Uid, context.Owner.Gid) - if err != nil { - return err - } - defer pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE) - } - - return unix.Symlinkat(oldpath, newdirfd, newpath) -} - -func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) { - if context != nil { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - err = pthread_setugid_np(context.Owner.Uid, context.Owner.Gid) - if err != nil { - return err - } - defer pthread_setugid_np(KAUTH_UID_NONE, KAUTH_GID_NONE) - } - - return unix.Mkdirat(dirfd, path, mode) -} - -type attrList struct { - bitmapCount uint16 - _ uint16 - CommonAttr uint32 - VolAttr uint32 - DirAttr uint32 - FileAttr uint32 - Forkattr uint32 -} - -func timesToAttrList(a *time.Time, m *time.Time) (attrList attrList, attributes [2]unix.Timespec) { - attrList.bitmapCount = unix.ATTR_BIT_MAP_COUNT - attrList.CommonAttr = 0 - i := 0 - if m != nil { - attributes[i] = unix.Timespec(fuse.UtimeToTimespec(m)) - attrList.CommonAttr |= unix.ATTR_CMN_MODTIME - i += 1 - } - if a != nil { - attributes[i] = unix.Timespec(fuse.UtimeToTimespec(a)) - attrList.CommonAttr |= unix.ATTR_CMN_ACCTIME - i += 1 - } - return attrList, attributes -} - -// FutimesNano syscall. -func FutimesNano(fd int, a *time.Time, m *time.Time) (err error) { - attrList, attributes := timesToAttrList(a, m) - return fsetattrlist(fd, unsafe.Pointer(&attrList), unsafe.Pointer(&attributes), - unsafe.Sizeof(attributes), 0) -} - -// UtimesNanoAtNofollow is like UtimesNanoAt but never follows symlinks. -// -// Unfortunately we cannot use unix.UtimesNanoAt since it is broken and just -// ignores the provided 'dirfd'. In addition, it also lacks handling of 'nil' -// pointers (used to preserve one of both timestamps). -func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (err error) { - if !filepath.IsAbs(path) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - var cwd int - cwd, err = syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err - } - defer syscall.Close(cwd) - err = syscall.Fchdir(dirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) - } - - _p0, err := syscall.BytePtrFromString(path) - if err != nil { - return err - } - - attrList, attributes := timesToAttrList(a, m) - return setattrlist(_p0, unsafe.Pointer(&attrList), unsafe.Pointer(&attributes), - unsafe.Sizeof(attributes), unix.FSOPT_NOFOLLOW) -} - -func Getdents(fd int) ([]fuse.DirEntry, error) { - entries, _, err := emulateGetdents(fd) - return entries, err -} - -func GetdentsSpecial(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, err error) { - return emulateGetdents(fd) -} - -// Renameat2 does not exist on Darwin, so we call Renameat and ignore the flags. -func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error) { - return unix.Renameat(olddirfd, oldpath, newdirfd, newpath) -} diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 9672be7..d0fa027 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -2,20 +2,9 @@ package syscallcompat import ( - "fmt" - "io/ioutil" - "runtime" - "strconv" - "strings" - "sync" "syscall" - "time" "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/tlog" ) const ( @@ -32,8 +21,6 @@ const ( RENAME_NOREPLACE = unix.RENAME_NOREPLACE ) -var preallocWarn sync.Once - // EnospcPrealloc preallocates ciphertext space without changing the file // size. This guarantees that we don't run out of space while writing a // ciphertext block (that would corrupt the block). @@ -49,233 +36,18 @@ func EnospcPrealloc(fd int, off int64, len int64) (err error) { if err == syscall.EOPNOTSUPP { // ZFS and ext3 do not support fallocate. Warn but continue anyway. // https://github.com/rfjakob/gocryptfs/issues/22 - preallocWarn.Do(func() { - tlog.Warn.Printf("Warning: The underlying filesystem " + - "does not support fallocate(2). gocryptfs will continue working " + - "but is no longer resistant against out-of-space errors.\n") - }) return nil } return err } } -// Fallocate wraps the Fallocate syscall. -func Fallocate(fd int, mode uint32, off int64, len int64) (err error) { - return syscall.Fallocate(fd, mode, off, len) -} - -func getSupplementaryGroups(pid uint32) (gids []int) { - procPath := fmt.Sprintf("/proc/%d/task/%d/status", pid, pid) - blob, err := ioutil.ReadFile(procPath) - if err != nil { - return nil - } - - lines := strings.Split(string(blob), "\n") - for _, line := range lines { - if strings.HasPrefix(line, "Groups:") { - f := strings.Fields(line[7:]) - gids = make([]int, len(f)) - for i := range gids { - val, err := strconv.ParseInt(f[i], 10, 32) - if err != nil { - return nil - } - gids[i] = int(val) - } - return gids - } - } - - return nil -} - -// asUser runs the function `f` under the effective uid, gid, groups specified -// in `context`. -func asUser(f func() (int, error), context *fuse.Context) (int, error) { - if context != nil { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - // Since go1.16beta1 (commit d1b1145cace8b968307f9311ff611e4bb810710c , - // https://go-review.googlesource.com/c/go/+/210639 ) - // syscall.{Setgroups,Setregid,Setreuid} affects all threads, which - // is exactly what we not want. - // - // We now use unix.{Setgroups,Setregid,Setreuid} instead. - - err := unix.Setgroups(getSupplementaryGroups(context.Pid)) - if err != nil { - return -1, err - } - defer unix.Setgroups(nil) - - err = unix.Setregid(-1, int(context.Owner.Gid)) - if err != nil { - return -1, err - } - defer unix.Setregid(-1, 0) - - err = unix.Setreuid(-1, int(context.Owner.Uid)) - if err != nil { - return -1, err - } - defer unix.Setreuid(-1, 0) - } - return f() -} - -// OpenatUser runs the Openat syscall in the context of a different user. -// -// It switches the current thread to the new user, performs the syscall, -// and switches back. -// -// If `context` is nil, this function behaves like ordinary Openat (no -// user switching). -func OpenatUser(dirfd int, path string, flags int, mode uint32, context *fuse.Context) (fd int, err error) { - f := func() (int, error) { - return Openat(dirfd, path, flags, mode) - } - return asUser(f, context) -} - -// Mknodat wraps the Mknodat syscall. -func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { - return syscall.Mknodat(dirfd, path, mode, dev) -} - -// MknodatUser runs the Mknodat syscall in the context of a different user. -// If `context` is nil, this function behaves like ordinary Mknodat. -// -// See OpenatUser() for how this works. -func MknodatUser(dirfd int, path string, mode uint32, dev int, context *fuse.Context) (err error) { - f := func() (int, error) { - err := Mknodat(dirfd, path, mode, dev) - return -1, err - } - _, err = asUser(f, context) - return err -} - -// Dup3 wraps the Dup3 syscall. We want to use Dup3 rather than Dup2 because Dup2 -// is not implemented on arm64. -func Dup3(oldfd int, newfd int, flags int) (err error) { - return syscall.Dup3(oldfd, newfd, flags) -} - -// FchmodatNofollow is like Fchmodat but never follows symlinks. -// -// This should be handled by the AT_SYMLINK_NOFOLLOW flag, but Linux -// does not implement it, so we have to perform an elaborate dance -// with O_PATH and /proc/self/fd. -// -// See also: Qemu implemented the same logic as fchmodat_nofollow(): -// https://git.qemu.org/?p=qemu.git;a=blob;f=hw/9pfs/9p-local.c#l335 -func FchmodatNofollow(dirfd int, path string, mode uint32) (err error) { - // Open handle to the filename (but without opening the actual file). - // This succeeds even when we don't have read permissions to the file. - fd, err := syscall.Openat(dirfd, path, syscall.O_NOFOLLOW|O_PATH, 0) - if err != nil { - return err - } - defer syscall.Close(fd) - - // Now we can check the type without the risk of race-conditions. - // Return syscall.ELOOP if it is a symlink. - var st syscall.Stat_t - err = syscall.Fstat(fd, &st) - if err != nil { - return err - } - if st.Mode&syscall.S_IFMT == syscall.S_IFLNK { - return syscall.ELOOP - } - - // Change mode of the actual file. Fchmod does not work with O_PATH, - // but Chmod via /proc/self/fd works. - procPath := fmt.Sprintf("/proc/self/fd/%d", fd) - return syscall.Chmod(procPath, mode) -} - -// SymlinkatUser runs the Symlinkat syscall in the context of a different user. -// If `context` is nil, this function behaves like ordinary Symlinkat. -// -// See OpenatUser() for how this works. -func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.Context) (err error) { - f := func() (int, error) { - err := unix.Symlinkat(oldpath, newdirfd, newpath) - return -1, err - } - _, err = asUser(f, context) - return err -} - -// MkdiratUser runs the Mkdirat syscall in the context of a different user. -// If `context` is nil, this function behaves like ordinary Mkdirat. -// -// See OpenatUser() for how this works. -func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) { - f := func() (int, error) { - err := unix.Mkdirat(dirfd, path, mode) - return -1, err - } - _, err = asUser(f, context) - return err -} - -// LsetxattrUser runs the Lsetxattr syscall in the context of a different user. -// This is useful when setting ACLs, as the result depends on the user running -// the operation (see fuse-xfstests generic/375). -// -// If `context` is nil, this function behaves like ordinary Lsetxattr. -func LsetxattrUser(path string, attr string, data []byte, flags int, context *fuse.Context) (err error) { - f := func() (int, error) { - err := unix.Lsetxattr(path, attr, data, flags) - return -1, err - } - _, err = asUser(f, context) - return err -} - -func timesToTimespec(a *time.Time, m *time.Time) []unix.Timespec { - ts := make([]unix.Timespec, 2) - ts[0] = unix.Timespec(fuse.UtimeToTimespec(a)) - ts[1] = unix.Timespec(fuse.UtimeToTimespec(m)) - return ts -} - -// FutimesNano syscall. -func FutimesNano(fd int, a *time.Time, m *time.Time) (err error) { - ts := timesToTimespec(a, m) - // To avoid introducing a separate syscall wrapper for futimens() - // (as done in go-fuse, for example), we instead use the /proc/self/fd trick. - procPath := fmt.Sprintf("/proc/self/fd/%d", fd) - return unix.UtimesNanoAt(unix.AT_FDCWD, procPath, ts, 0) -} - -// UtimesNanoAtNofollow is like UtimesNanoAt but never follows symlinks. -// Retries on EINTR. -func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (err error) { - ts := timesToTimespec(a, m) - err = retryEINTR(func() error { - return unix.UtimesNanoAt(dirfd, path, ts, unix.AT_SYMLINK_NOFOLLOW) - }) - return err -} - // Getdents syscall with "." and ".." filtered out. -func Getdents(fd int) ([]fuse.DirEntry, error) { +func Getdents(fd int) ([]DirEntry, error) { entries, _, err := getdents(fd) return entries, err } -// GetdentsSpecial calls the Getdents syscall, -// with normal entries and "." / ".." split into two slices. -func GetdentsSpecial(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, err error) { - return getdents(fd) -} - // Renameat2 does not exist on Darwin, so we have to wrap it here. // Retries on EINTR. func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) (err error) { diff --git a/internal/syscallcompat/unix2syscall_darwin.go b/internal/syscallcompat/unix2syscall_darwin.go deleted file mode 100644 index 5767a27..0000000 --- a/internal/syscallcompat/unix2syscall_darwin.go +++ /dev/null @@ -1,26 +0,0 @@ -package syscallcompat - -import ( - "syscall" - - "golang.org/x/sys/unix" -) - -// Unix2syscall converts a unix.Stat_t struct to a syscall.Stat_t struct. -func Unix2syscall(u unix.Stat_t) syscall.Stat_t { - return syscall.Stat_t{ - Dev: u.Dev, - Ino: u.Ino, - Nlink: u.Nlink, - Mode: u.Mode, - Uid: u.Uid, - Gid: u.Gid, - Rdev: u.Rdev, - Size: u.Size, - Blksize: u.Blksize, - Blocks: u.Blocks, - Atimespec: syscall.Timespec(u.Atim), - Mtimespec: syscall.Timespec(u.Mtim), - Ctimespec: syscall.Timespec(u.Ctim), - } -} diff --git a/internal/tlog/log.go b/internal/tlog/log.go deleted file mode 100644 index 6d32a6b..0000000 --- a/internal/tlog/log.go +++ /dev/null @@ -1,201 +0,0 @@ -// Package tlog is a "toggled logger" that can be enabled and disabled and -// provides coloring. -package tlog - -import ( - "encoding/hex" - "encoding/json" - "fmt" - "log" - "log/syslog" - "os" - - "golang.org/x/crypto/ssh/terminal" -) - -const ( - // ProgramName is used in log reports. - ProgramName = "gocryptfs" - wpanicMsg = "-wpanic turns this warning into a panic: " -) - -// Escape sequences for terminal colors. These are set in init() if and only -// if stdout is a terminal. Otherwise they are empty strings. -var ( - // ColorReset is used to reset terminal colors. - ColorReset string - // ColorGrey is a terminal color setting string. - ColorGrey string - // ColorRed is a terminal color setting string. - ColorRed string - // ColorGreen is a terminal color setting string. - ColorGreen string - // ColorYellow is a terminal color setting string. - ColorYellow string -) - -// JSONDump writes the object in json form. -func JSONDump(obj interface{}) string { - b, err := json.MarshalIndent(obj, "", "\t") - if err != nil { - return err.Error() - } - - return string(b) -} - -// toggledLogger - a Logger than can be enabled and disabled -type toggledLogger struct { - // Enable or disable output - Enabled bool - // Panic after logging a message, useful in regression tests - Wpanic bool - // Private prefix and postfix are used for coloring - prefix string - postfix string - - Logger *log.Logger -} - -// trimNewline removes one trailing newline from "msg" -func trimNewline(msg string) string { - if len(msg) == 0 { - return msg - } - if msg[len(msg)-1] == '\n' { - return msg[:len(msg)-1] - } - return msg -} - -func (l *toggledLogger) Printf(format string, v ...interface{}) { - if !l.Enabled { - return - } - msg := trimNewline(fmt.Sprintf(format, v...)) - l.Logger.Printf(l.prefix + msg + l.postfix) - if l.Wpanic { - l.Logger.Panic(wpanicMsg + msg) - } -} -func (l *toggledLogger) Println(v ...interface{}) { - if !l.Enabled { - return - } - msg := trimNewline(fmt.Sprint(v...)) - l.Logger.Println(l.prefix + msg + l.postfix) - if l.Wpanic { - l.Logger.Panic(wpanicMsg + msg) - } -} - -// Debug logs debug messages -// Can be enabled by passing "-d" -var Debug *toggledLogger - -// Info logs informational message -// Can be disabled by passing "-q" -var Info *toggledLogger - -// Warn logs warnings, -// meaning nothing serious by itself but might indicate problems. -// Passing "-wpanic" will make this function panic after printing the message. -var Warn *toggledLogger - -// Fatal error, we are about to exit -var Fatal *toggledLogger - -func init() { - if terminal.IsTerminal(int(os.Stdout.Fd())) { - ColorReset = "\033[0m" - ColorGrey = "\033[2m" - ColorRed = "\033[31m" - ColorGreen = "\033[32m" - ColorYellow = "\033[33m" - } - - Debug = &toggledLogger{ - Logger: log.New(os.Stdout, "", 0), - } - Info = &toggledLogger{ - Enabled: true, - Logger: log.New(os.Stdout, "", 0), - } - Warn = &toggledLogger{ - Enabled: true, - Logger: log.New(os.Stderr, "", 0), - prefix: ColorYellow, - postfix: ColorReset, - } - Fatal = &toggledLogger{ - Enabled: true, - Logger: log.New(os.Stderr, "", 0), - prefix: ColorRed, - postfix: ColorReset, - } -} - -// SwitchToSyslog redirects the output of this logger to syslog. -// p = facility | severity -func (l *toggledLogger) SwitchToSyslog(p syslog.Priority) { - w, err := syslog.New(p, ProgramName) - if err != nil { - Warn.Printf("SwitchToSyslog: %v", err) - } else { - l.Logger.SetOutput(w) - // Disable colors - l.prefix = "" - l.postfix = "" - } -} - -// SwitchLoggerToSyslog redirects the default log.Logger that the go-fuse lib uses -// to syslog. -func SwitchLoggerToSyslog() { - p := syslog.LOG_USER | syslog.LOG_WARNING - w, err := syslog.New(p, ProgramName) - if err != nil { - Warn.Printf("SwitchLoggerToSyslog: %v", err) - } else { - log.SetPrefix("go-fuse: ") - // Disable printing the timestamp, syslog already provides that - log.SetFlags(0) - log.SetOutput(w) - } -} - -// PrintMasterkeyReminder reminds the user that he should store the master key in -// a safe place. -func PrintMasterkeyReminder(key []byte) { - if !Info.Enabled { - // Quiet mode - return - } - if !terminal.IsTerminal(int(os.Stdout.Fd())) { - // We don't want the master key to end up in a log file - Info.Printf("Not running on a terminal, suppressing master key display\n") - return - } - h := hex.EncodeToString(key) - var hChunked string - // Try to make it less scary by splitting it up in chunks - for i := 0; i < len(h); i += 8 { - hChunked += h[i : i+8] - if i < 52 { - hChunked += "-" - } - if i == 24 { - hChunked += "\n " - } - } - Info.Printf(` -Your master key is: - - %s - -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. - -`, ColorGrey+hChunked+ColorReset) -} diff --git a/internal/tlog/tlog_test.go b/internal/tlog/tlog_test.go deleted file mode 100644 index 2e1c034..0000000 --- a/internal/tlog/tlog_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package tlog - -import ( - "testing" -) - -// Test that trimNewline() works as expected -func TestTrimNewline(t *testing.T) { - testTable := []struct { - in string - want string - }{ - {"...\n", "..."}, - {"\n...\n", "\n..."}, - {"", ""}, - {"\n", ""}, - {"\n\n", "\n"}, - {" ", " "}, - } - for _, v := range testTable { - have := trimNewline(v.in) - if v.want != have { - t.Errorf("want=%q have=%q", v.want, have) - } - } -} diff --git a/main.go b/main.go index edd61ff..38dd16d 100644 --- a/main.go +++ b/main.go @@ -1,335 +1,3 @@ -// gocryptfs is an encrypted overlay filesystem written in Go. -// See README.md ( https://github.com/rfjakob/gocryptfs/blob/master/README.md ) -// and the official website ( https://nuetzlich.net/gocryptfs/ ) for details. package main -import ( - "fmt" - "log" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/fido2" - "github.com/rfjakob/gocryptfs/internal/readpassword" - "github.com/rfjakob/gocryptfs/internal/speed" - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// GitVersion is the gocryptfs version according to git, set by build.bash -var GitVersion = "[GitVersion not set - please compile using ./build.bash]" - -// GitVersionFuse is the go-fuse library version, set by build.bash -var GitVersionFuse = "[GitVersionFuse not set - please compile using ./build.bash]" - -// BuildDate is a date string like "2017-09-06", set by build.bash -var BuildDate = "0000-00-00" - -// raceDetector is set to true by race.go if we are compiled with "go build -race" -var raceDetector bool - -// loadConfig loads the config file `args.config` and decrypts the masterkey, -// or gets via the `-masterkey` or `-zerokey` command line options, if specified. -func loadConfig(args *argContainer) (masterkey []byte, cf *configfile.ConfFile, err error) { - // First check if the file can be read at all. - cf, err = configfile.Load(args.config) - if err != nil { - tlog.Fatal.Printf("Cannot open config file: %v", err) - return nil, nil, err - } - // The user may have passed the master key on the command line (probably because - // he forgot the password). - masterkey = handleArgsMasterkey(args) - if masterkey != nil { - return masterkey, cf, nil - } - var pw []byte - if cf.IsFeatureFlagSet(configfile.FlagFIDO2) { - if args.fido2 == "" { - tlog.Fatal.Printf("Masterkey encrypted using FIDO2 token; need to use the --fido2 option.") - os.Exit(exitcodes.Usage) - } - pw = fido2.Secret(args.fido2, cf.FIDO2.CredentialID, cf.FIDO2.HMACSalt) - } else { - pw = readpassword.Once([]string(args.extpass), []string(args.passfile), "") - } - tlog.Info.Println("Decrypting master key") - masterkey, err = cf.DecryptMasterKey(pw) - for i := range pw { - pw[i] = 0 - } - - if err != nil { - tlog.Fatal.Println(err) - return nil, nil, err - } - return masterkey, cf, nil -} - -// changePassword - change the password of config file "filename" -// Does not return (calls os.Exit both on success and on error). -func changePassword(args *argContainer) { - var confFile *configfile.ConfFile - { - var masterkey []byte - var err error - masterkey, confFile, err = loadConfig(args) - if err != nil { - exitcodes.Exit(err) - } - if len(masterkey) == 0 { - log.Panic("empty masterkey") - } - if confFile.IsFeatureFlagSet(configfile.FlagFIDO2) { - tlog.Fatal.Printf("Password change is not supported on FIDO2-enabled filesystems.") - os.Exit(exitcodes.Usage) - } - tlog.Info.Println("Please enter your new password.") - newPw := readpassword.Twice([]string(args.extpass), []string(args.passfile)) - logN := confFile.ScryptObject.LogN() - if args._explicitScryptn { - logN = args.scryptn - } - confFile.EncryptKey(masterkey, newPw, logN) - for i := range newPw { - newPw[i] = 0 - } - for i := range masterkey { - masterkey[i] = 0 - } - // masterkey and newPw run out of scope here - } - // Are we resetting the password without knowing the old one using - // "-masterkey"? - if args.masterkey != "" { - bak := args.config + ".bak" - err := os.Link(args.config, bak) - if err != nil { - tlog.Fatal.Printf("Could not create backup file: %v", err) - os.Exit(exitcodes.Init) - } - tlog.Info.Printf(tlog.ColorGrey+ - "A copy of the old config file has been created at %q.\n"+ - "Delete it after you have verified that you can access your files with the new password."+ - tlog.ColorReset, bak) - } - err := confFile.WriteFile() - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.WriteConf) - } - tlog.Info.Printf(tlog.ColorGreen + "Password changed." + tlog.ColorReset) -} - -// printVersion prints a version string like this: -// gocryptfs v1.7-32-gcf99cfd; go-fuse v1.0.0-174-g22a9cb9; 2019-05-12 go1.12 linux/amd64 -func printVersion() { - var tagsSlice []string - if stupidgcm.BuiltWithoutOpenssl { - tagsSlice = append(tagsSlice, "without_openssl") - } - tags := "" - if tagsSlice != nil { - tags = " " + strings.Join(tagsSlice, " ") - } - built := fmt.Sprintf("%s %s", BuildDate, runtime.Version()) - if raceDetector { - built += " -race" - } - fmt.Printf("%s %s%s; go-fuse %s; %s %s/%s\n", - tlog.ProgramName, GitVersion, tags, GitVersionFuse, built, - runtime.GOOS, runtime.GOARCH) -} - -func main() { - mxp := runtime.GOMAXPROCS(0) - if mxp < 4 && os.Getenv("GOMAXPROCS") == "" { - // On a 2-core machine, setting maxprocs to 4 gives 10% better performance. - // But don't override an explicitly set GOMAXPROCS env variable. - runtime.GOMAXPROCS(4) - } - // mount(1) unsets PATH. Since exec.Command does not handle this case, we set - // PATH to a default value if it's empty or unset. - if os.Getenv("PATH") == "" { - os.Setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin") - } - // Show microseconds in go-fuse debug output (-fusedebug) - log.SetFlags(log.Lmicroseconds) - var err error - // Parse all command-line options (i.e. arguments starting with "-") - // into "args". Path arguments are parsed below. - args := parseCliOpts() - // Fork a child into the background if "-fg" is not set AND we are mounting - // a filesystem. The child will do all the work. - if !args.fg && flagSet.NArg() == 2 { - ret := forkChild() - os.Exit(ret) - } - if args.debug { - tlog.Debug.Enabled = true - } - // "-v" - if args.version { - tlog.Debug.Printf("openssl=%v\n", args.openssl) - tlog.Debug.Printf("on-disk format %d\n", contentenc.CurrentVersion) - printVersion() - os.Exit(0) - } - // "-hh" - if args.hh { - helpLong() - os.Exit(0) - } - // "-speed" - if args.speed { - printVersion() - speed.Run() - os.Exit(0) - } - if args.wpanic { - tlog.Warn.Wpanic = true - tlog.Debug.Printf("Panicking on warnings") - } - // Every operation below requires CIPHERDIR. Exit if we don't have it. - if flagSet.NArg() == 0 { - if flagSet.NFlag() == 0 { - // Naked call to "gocryptfs". Just print the help text. - helpShort() - } else { - // The user has passed some flags, but CIPHERDIR is missing. State - // what is wrong. - tlog.Fatal.Printf("CIPHERDIR argument is missing") - } - os.Exit(exitcodes.Usage) - } - // Check that CIPHERDIR exists - args.cipherdir, _ = filepath.Abs(flagSet.Arg(0)) - err = isDir(args.cipherdir) - if err != nil { - tlog.Fatal.Printf("Invalid cipherdir: %v", err) - os.Exit(exitcodes.CipherDir) - } - // "-q" - if args.quiet { - tlog.Info.Enabled = false - } - // "-reverse" implies "-aessiv" - if args.reverse { - args.aessiv = true - } else { - if args.exclude != nil { - tlog.Fatal.Printf("-exclude only works in reverse mode") - os.Exit(exitcodes.ExcludeError) - } - } - // "-config" - if args.config != "" { - args.config, err = filepath.Abs(args.config) - if err != nil { - tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err) - os.Exit(exitcodes.Init) - } - tlog.Info.Printf("Using config file at custom location %s", args.config) - args._configCustom = true - } else if args.reverse { - args.config = filepath.Join(args.cipherdir, configfile.ConfReverseName) - } else { - args.config = filepath.Join(args.cipherdir, configfile.ConfDefaultName) - } - // "-force_owner" - if args.force_owner != "" { - var uidNum, gidNum int64 - ownerPieces := strings.SplitN(args.force_owner, ":", 2) - if len(ownerPieces) != 2 { - tlog.Fatal.Printf("force_owner must be in form UID:GID") - os.Exit(exitcodes.Usage) - } - uidNum, err = strconv.ParseInt(ownerPieces[0], 0, 32) - if err != nil || uidNum < 0 { - tlog.Fatal.Printf("force_owner: Unable to parse UID %v as positive integer", ownerPieces[0]) - os.Exit(exitcodes.Usage) - } - gidNum, err = strconv.ParseInt(ownerPieces[1], 0, 32) - if err != nil || gidNum < 0 { - tlog.Fatal.Printf("force_owner: Unable to parse GID %v as positive integer", ownerPieces[1]) - os.Exit(exitcodes.Usage) - } - args._forceOwner = &fuse.Owner{Uid: uint32(uidNum), Gid: uint32(gidNum)} - } - // "-cpuprofile" - if args.cpuprofile != "" { - onExitFunc := setupCpuprofile(args.cpuprofile) - defer onExitFunc() - } - // "-memprofile" - if args.memprofile != "" { - onExitFunc := setupMemprofile(args.memprofile) - defer onExitFunc() - } - // "-trace" - if args.trace != "" { - onExitFunc := setupTrace(args.trace) - defer onExitFunc() - } - if args.cpuprofile != "" || args.memprofile != "" || args.trace != "" { - tlog.Info.Printf("Note: You must unmount gracefully, otherwise the profile file(s) will stay empty!\n") - } - // "-openssl" - if !args.openssl { - tlog.Debug.Printf("OpenSSL disabled, using Go GCM") - } else { - tlog.Debug.Printf("OpenSSL enabled") - } - // Operation flags - nOps := countOpFlags(&args) - if nOps == 0 { - // Default operation: mount. - if flagSet.NArg() != 2 { - prettyArgs := prettyArgs() - tlog.Info.Printf("Wrong number of arguments (have %d, want 2). You passed: %s", - flagSet.NArg(), prettyArgs) - tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT [-o COMMA-SEPARATED-OPTIONS]", tlog.ProgramName) - os.Exit(exitcodes.Usage) - } - doMount(&args) - // Don't call os.Exit to give deferred functions a chance to run - return - } - if nOps > 1 { - tlog.Fatal.Printf("At most one of -info, -init, -passwd, -fsck is allowed") - os.Exit(exitcodes.Usage) - } - if flagSet.NArg() != 1 { - tlog.Fatal.Printf("The options -info, -init, -passwd, -fsck take exactly one argument, %d given", - flagSet.NArg()) - os.Exit(exitcodes.Usage) - } - // "-info" - if args.info { - info(args.config) - os.Exit(0) - } - // "-init" - if args.init { - initDir(&args) - os.Exit(0) - } - // "-passwd" - if args.passwd { - changePassword(&args) - os.Exit(0) - } - // "-fsck" - if args.fsck { - code := fsck(&args) - os.Exit(code) - } -} +func main() {} diff --git a/masterkey.go b/masterkey.go deleted file mode 100644 index 8d75c75..0000000 --- a/masterkey.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "encoding/hex" - "os" - "strings" - - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/readpassword" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// unhexMasterKey - Convert a hex-encoded master key to binary. -// Calls os.Exit on failure. -func unhexMasterKey(masterkey string, fromStdin bool) []byte { - masterkey = strings.Replace(masterkey, "-", "", -1) - key, err := hex.DecodeString(masterkey) - if err != nil { - tlog.Fatal.Printf("Could not parse master key: %v", err) - os.Exit(exitcodes.MasterKey) - } - if len(key) != cryptocore.KeyLen { - tlog.Fatal.Printf("Master key has length %d but we require length %d", len(key), cryptocore.KeyLen) - os.Exit(exitcodes.MasterKey) - } - tlog.Info.Printf("Using explicit master key.") - if !fromStdin { - tlog.Info.Printf(tlog.ColorYellow + - "THE MASTER KEY IS VISIBLE VIA \"ps ax\" AND MAY BE STORED IN YOUR SHELL HISTORY!\n" + - "ONLY USE THIS MODE FOR EMERGENCIES" + tlog.ColorReset) - } - return key -} - -// handleArgsMasterkey looks at `args.masterkey` and `args.zerokey`, gets the -// masterkey from the source the user wanted (string on the command line, stdin, all-zero), -// and returns it in binary. Returns nil if no masterkey source was specified. -func handleArgsMasterkey(args *argContainer) (masterkey []byte) { - // "-masterkey=stdin" - if args.masterkey == "stdin" { - in := string(readpassword.Once(nil, nil, "Masterkey")) - return unhexMasterKey(in, true) - } - // "-masterkey=941a6029-3adc6a1c-..." - if args.masterkey != "" { - return unhexMasterKey(args.masterkey, false) - } - // "-zerokey" - if args.zerokey { - tlog.Info.Printf("Using all-zero dummy master key.") - tlog.Info.Printf(tlog.ColorYellow + - "ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING." + - tlog.ColorReset) - return make([]byte, cryptocore.KeyLen) - } - // No master key source specified on the command line. Caller must parse - // the config file. - return nil -} diff --git a/mount.go b/mount.go deleted file mode 100644 index f992f94..0000000 --- a/mount.go +++ /dev/null @@ -1,531 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - "log/syslog" - "math" - "net" - "os" - "os/exec" - "os/signal" - "path" - "path/filepath" - "runtime" - "runtime/debug" - "strings" - "sync/atomic" - "syscall" - "time" - - "golang.org/x/sys/unix" - - "github.com/hanwen/go-fuse/v2/fs" - "github.com/hanwen/go-fuse/v2/fuse" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/contentenc" - "github.com/rfjakob/gocryptfs/internal/cryptocore" - "github.com/rfjakob/gocryptfs/internal/ctlsocksrv" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/fusefrontend" - "github.com/rfjakob/gocryptfs/internal/fusefrontend_reverse" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/internal/openfiletable" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// AfterUnmount is called after the filesystem has been unmounted. -// This can be used for cleanup and printing statistics. -type AfterUnmounter interface { - AfterUnmount() -} - -// doMount mounts an encrypted directory. -// Called from main. -func doMount(args *argContainer) { - // Check mountpoint - var err error - args.mountpoint, err = filepath.Abs(flagSet.Arg(1)) - if err != nil { - tlog.Fatal.Printf("Invalid mountpoint: %v", err) - os.Exit(exitcodes.MountPoint) - } - // We cannot mount "/home/user/.cipher" at "/home/user" because the mount - // will hide ".cipher" also for us. - if args.cipherdir == args.mountpoint || strings.HasPrefix(args.cipherdir, args.mountpoint+"/") { - tlog.Fatal.Printf("Mountpoint %q would shadow cipherdir %q, this is not supported", - args.mountpoint, args.cipherdir) - os.Exit(exitcodes.MountPoint) - } - // Reverse-mounting "/foo" at "/foo/mnt" means we would be recursively - // encrypting ourselves. - if strings.HasPrefix(args.mountpoint, args.cipherdir+"/") { - tlog.Fatal.Printf("Mountpoint %q is contained in cipherdir %q, this is not supported", - args.mountpoint, args.cipherdir) - os.Exit(exitcodes.MountPoint) - } - if args.nonempty { - err = isDir(args.mountpoint) - } else { - err = isEmptyDir(args.mountpoint) - // OSXFuse will create the mountpoint for us ( https://github.com/rfjakob/gocryptfs/issues/194 ) - if runtime.GOOS == "darwin" && os.IsNotExist(err) { - tlog.Info.Printf("Mountpoint %q does not exist, but should be created by OSXFuse", - args.mountpoint) - err = nil - } - } - if err != nil { - tlog.Fatal.Printf("Invalid mountpoint: %v", err) - os.Exit(exitcodes.MountPoint) - } - // Open control socket early so we can error out before asking the user - // for the password - if args.ctlsock != "" { - // 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) - 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() - if err != nil { - tlog.Warn.Printf("ctlsock close: %v", err) - } - }() - } - // Preallocation on Btrfs is broken ( https://github.com/rfjakob/gocryptfs/issues/395 ) - // and slow ( https://github.com/rfjakob/gocryptfs/issues/63 ). - if !args.noprealloc { - // darwin does not have unix.BTRFS_SUPER_MAGIC, so we define it here - const BTRFS_SUPER_MAGIC = 0x9123683e - var st unix.Statfs_t - err = unix.Statfs(args.cipherdir, &st) - // Cast to uint32 avoids compile error on arm: "constant 2435016766 overflows int32" - if err == nil && uint32(st.Type) == BTRFS_SUPER_MAGIC { - tlog.Info.Printf(tlog.ColorYellow + - "Btrfs detected, forcing -noprealloc. See https://github.com/rfjakob/gocryptfs/issues/395 for why." + - tlog.ColorReset) - args.noprealloc = true - } - } - // We cannot use JSON for pretty-printing as the fields are unexported - tlog.Debug.Printf("cli args: %#v", args) - // Initialize gocryptfs (read config file, ask for password, ...) - fs, wipeKeys := initFuseFrontend(args) - // Try to wipe secret keys from memory after unmount - defer wipeKeys() - // Initialize go-fuse FUSE server - srv := initGoFuse(fs, args) - if x, ok := fs.(AfterUnmounter); ok { - defer x.AfterUnmount() - } - - tlog.Info.Println(tlog.ColorGreen + "Filesystem mounted and ready." + tlog.ColorReset) - // We have been forked into the background, as evidenced by the set - // "notifypid". - if args.notifypid > 0 { - // Chdir to the root directory so we don't block unmounting the CWD - os.Chdir("/") - // Switch to syslog - if !args.nosyslog { - // Switch all of our logs and the generic logger to syslog - tlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO) - tlog.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG) - tlog.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING) - tlog.Fatal.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_CRIT) - tlog.SwitchLoggerToSyslog() - // Daemons should redirect stdin, stdout and stderr - redirectStdFds() - } - // Disconnect from the controlling terminal by creating a new session. - // This prevents us from getting SIGINT when the user presses Ctrl-C - // to exit a running script that has called gocryptfs. - _, err = syscall.Setsid() - if err != nil { - tlog.Warn.Printf("Setsid: %v", err) - } - // Send SIGUSR1 to our parent - sendUsr1(args.notifypid) - } - // Increase the open file limit to 4096. This is not essential, so do it after - // we have switched to syslog and don't bother the user with warnings. - setOpenFileLimit() - // Wait for SIGINT in the background and unmount ourselves if we get it. - // This prevents a dangling "Transport endpoint is not connected" - // mountpoint if the user hits CTRL-C. - handleSigint(srv, args.mountpoint) - // Return memory that was allocated for scrypt (64M by default!) and other - // stuff that is no longer needed to the OS - debug.FreeOSMemory() - // Set up autounmount, if requested. - if args.idle > 0 && !args.reverse { - // Not being in reverse mode means we always have a forward file system. - fwdFs := fs.(*fusefrontend.RootNode) - go idleMonitor(args.idle, fwdFs, srv, args.mountpoint) - } - // Wait for unmount. - srv.Wait() -} - -// Based on the EncFS idle monitor: -// https://github.com/vgough/encfs/blob/1974b417af189a41ffae4c6feb011d2a0498e437/encfs/main.cpp#L851 -// idleMonitor is a function to be run as a thread that checks for -// filesystem idleness and unmounts if we've been idle for long enough. -const checksDuringTimeoutPeriod = 4 - -func idleMonitor(idleTimeout time.Duration, fs *fusefrontend.RootNode, srv *fuse.Server, mountpoint string) { - // sleepNs is the sleep time between checks, in nanoseconds. - sleepNs := contentenc.MinUint64( - uint64(idleTimeout/checksDuringTimeoutPeriod), - uint64(2*time.Minute)) - timeoutCycles := int(math.Ceil(float64(idleTimeout) / float64(sleepNs))) - idleCount := 0 - idleTime := func() time.Duration { - return time.Duration(sleepNs * uint64(idleCount)) - } - for { - // Atomically check whether the flag is 0 and reset it to 1 if so. - isIdle := !atomic.CompareAndSwapUint32(&fs.IsIdle, 0, 1) - // Any form of current or recent access resets the idle counter. - openFileCount := openfiletable.CountOpenFiles() - if !isIdle || openFileCount > 0 { - idleCount = 0 - } else { - idleCount++ - } - tlog.Debug.Printf( - "idleMonitor: idle for %v (idleCount = %d, isIdle = %t, open = %d)", - idleTime(), idleCount, isIdle, openFileCount) - if idleCount > 0 && idleCount%timeoutCycles == 0 { - tlog.Info.Printf("idleMonitor: filesystem idle; unmounting: %s", mountpoint) - err := srv.Unmount() - if err != nil { - // We get "Device or resource busy" when a process has its - // working directory on the mount. Log the event at Info level - // so the user finds out why their filesystem does not get - // unmounted. - tlog.Info.Printf("idleMonitor: unmount failed: %v. Resetting idle time.", err) - idleCount = 0 - } - } - time.Sleep(time.Duration(sleepNs)) - } -} - -// setOpenFileLimit tries to increase the open file limit to 4096 (the default hard -// limit on Linux). -func setOpenFileLimit() { - var lim syscall.Rlimit - err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) - if err != nil { - tlog.Warn.Printf("Getting RLIMIT_NOFILE failed: %v", err) - return - } - if lim.Cur >= 4096 { - return - } - lim.Cur = 4096 - err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &lim) - if err != nil { - tlog.Warn.Printf("Setting RLIMIT_NOFILE to %+v failed: %v", lim, err) - // %+v output: "{Cur:4097 Max:4096}" ^ - } -} - -// initFuseFrontend - initialize gocryptfs/internal/fusefrontend -// Calls os.Exit on errors -func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys func()) { - var err error - var confFile *configfile.ConfFile - // Get the masterkey from the command line if it was specified - masterkey := handleArgsMasterkey(args) - // Otherwise, load masterkey from config file (normal operation). - // Prompts the user for the password. - if masterkey == nil { - masterkey, confFile, err = loadConfig(args) - if err != nil { - if args._ctlsockFd != nil { - // Close the socket file (which also deletes it) - args._ctlsockFd.Close() - } - exitcodes.Exit(err) - } - } - // Reconciliate CLI and config file arguments into a fusefrontend.Args struct - // that is passed to the filesystem implementation - cryptoBackend := cryptocore.BackendGoGCM - if args.openssl { - cryptoBackend = cryptocore.BackendOpenSSL - } - if args.aessiv { - cryptoBackend = cryptocore.BackendAESSIV - } - // forceOwner implies allow_other, as documented. - // Set this early, so args.allow_other can be relied on below this point. - if args._forceOwner != nil { - args.allow_other = true - } - frontendArgs := fusefrontend.Args{ - Cipherdir: args.cipherdir, - PlaintextNames: args.plaintextnames, - LongNames: args.longnames, - ConfigCustom: args._configCustom, - NoPrealloc: args.noprealloc, - SerializeReads: args.serialize_reads, - ForceDecode: args.forcedecode, - ForceOwner: args._forceOwner, - Exclude: args.exclude, - ExcludeWildcard: args.excludeWildcard, - ExcludeFrom: args.excludeFrom, - Suid: args.suid, - KernelCache: args.kernel_cache, - SharedStorage: args.sharedstorage, - } - // confFile is nil when "-zerokey" or "-masterkey" was used - if confFile != nil { - // Settings from the config file override command line args - frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames) - args.raw64 = confFile.IsFeatureFlagSet(configfile.FlagRaw64) - args.hkdf = confFile.IsFeatureFlagSet(configfile.FlagHKDF) - if confFile.IsFeatureFlagSet(configfile.FlagAESSIV) { - cryptoBackend = cryptocore.BackendAESSIV - } else if args.reverse { - tlog.Fatal.Printf("AES-SIV is required by reverse mode, but not enabled in the config file") - os.Exit(exitcodes.Usage) - } - } - // If allow_other is set and we run as root, try to give newly created files to - // the right user. - if args.allow_other && os.Getuid() == 0 { - frontendArgs.PreserveOwner = true - } - jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t") - tlog.Debug.Printf("frontendArgs: %s", string(jsonBytes)) - - // Init crypto backend - cCore := cryptocore.New(masterkey, cryptoBackend, contentenc.DefaultIVBits, args.hkdf, args.forcedecode) - cEnc := contentenc.New(cCore, contentenc.DefaultBS, args.forcedecode) - nameTransform := nametransform.New(cCore.EMECipher, frontendArgs.LongNames, args.raw64) - // Init badname patterns - nameTransform.BadnamePatterns = make([]string, 0) - for _, pattern := range args.badname { - _, err := filepath.Match(pattern, "") // Make sure pattern is valid - if err != nil { - tlog.Fatal.Printf("-badname: invalid pattern %q supplied", pattern) - os.Exit(exitcodes.Usage) - } else { - nameTransform.BadnamePatterns = append(nameTransform.BadnamePatterns, pattern) - } - } - // After the crypto backend is initialized, - // we can purge the master key from memory. - for i := range masterkey { - masterkey[i] = 0 - } - masterkey = nil - // Spawn fusefrontend - if args.reverse { - if cryptoBackend != cryptocore.BackendAESSIV { - log.Panic("reverse mode must use AES-SIV, everything else is insecure") - } - rootNode = fusefrontend_reverse.NewRootNode(frontendArgs, cEnc, nameTransform) - } else { - rootNode = fusefrontend.NewRootNode(frontendArgs, cEnc, nameTransform) - } - // We have opened the socket early so that we cannot fail here after - // asking the user for the password - if args._ctlsockFd != nil { - go ctlsocksrv.Serve(args._ctlsockFd, rootNode.(ctlsocksrv.Interface)) - } - return rootNode, func() { cCore.Wipe() } -} - -// 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. -func initGoFuse(rootNode fs.InodeEmbedder, args *argContainer) *fuse.Server { - var fuseOpts *fs.Options - sec := time.Second - if args.sharedstorage { - // sharedstorage mode sets all cache timeouts to zero so changes to the - // backing shared storage show up immediately. - // Hard links are disabled by using automatically incrementing - // inode numbers provided by go-fuse. - fuseOpts = &fs.Options{ - FirstAutomaticIno: 1000, - } - } else { - fuseOpts = &fs.Options{ - // These options are to be compatible with libfuse defaults, - // making benchmarking easier. - NegativeTimeout: &sec, - AttrTimeout: &sec, - EntryTimeout: &sec, - } - } - fuseOpts.NullPermissions = true - // Enable go-fuse warnings - fuseOpts.Logger = log.New(os.Stderr, "go-fuse: ", log.Lmicroseconds) - fuseOpts.MountOptions = fuse.MountOptions{ - // Writes and reads are usually capped at 128kiB on Linux through - // the FUSE_MAX_PAGES_PER_REQ kernel constant in fuse_i.h. Our - // sync.Pool buffer pools are sized acc. to the default. Users may set - // the kernel constant higher, and Synology NAS kernels are known to - // have it >128kiB. We cannot handle more than 128kiB, so we tell - // the kernel to limit the size explicitly. - MaxWrite: fuse.MAX_KERNEL_WRITE, - Options: []string{fmt.Sprintf("max_read=%d", fuse.MAX_KERNEL_WRITE)}, - Debug: args.fusedebug, - } - - mOpts := &fuseOpts.MountOptions - 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") - } - if args.acl { - mOpts.EnableAcl = true - } - if args.forcedecode { - tlog.Info.Printf(tlog.ColorYellow + "THE OPTION \"-forcedecode\" IS ACTIVE. GOCRYPTFS WILL RETURN CORRUPT DATA!" + - tlog.ColorReset) - } - // 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") - } - // Set values shown in "df -T" and friends - // First column, "Filesystem" - fsname := args.cipherdir - if args.fsname != "" { - fsname = args.fsname - } - fsname2 := strings.Replace(fsname, ",", "_", -1) - if fsname2 != fsname { - tlog.Warn.Printf("Warning: %q will be displayed as %q in \"df -T\"", fsname, fsname2) - fsname = fsname2 - } - mOpts.Options = append(mOpts.Options, "fsname="+fsname) - // Second column, "Type", will be shown as "fuse." + Name - mOpts.Name = "gocryptfs" - if args.reverse { - mOpts.Name += "-reverse" - } - // 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) - } - // 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") - } else if args.rw { - mOpts.Options = append(mOpts.Options, "rw") - } - // If both "nosuid" & "suid", "nodev" & "dev", etc were passed, the safer - // option wins. - if args.nosuid { - mOpts.Options = append(mOpts.Options, "nosuid") - } else if args.suid { - mOpts.Options = append(mOpts.Options, "suid") - } - if args.nodev { - mOpts.Options = append(mOpts.Options, "nodev") - } else if args.dev { - mOpts.Options = append(mOpts.Options, "dev") - } - if args.noexec { - mOpts.Options = append(mOpts.Options, "noexec") - } else if args.exec { - mOpts.Options = append(mOpts.Options, "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...) - } - srv, err := fs.Mount(args.mountpoint, rootNode, fuseOpts) - if err != nil { - tlog.Fatal.Printf("fs.Mount failed: %s", strings.TrimSpace(err.Error())) - if runtime.GOOS == "darwin" { - tlog.Info.Printf("Maybe you should run: /Library/Filesystems/osxfuse.fs/Contents/Resources/load_osxfuse") - } - os.Exit(exitcodes.FuseNewServer) - } - - // All FUSE file and directory create calls carry explicit permission - // information. We need an unrestricted umask to create the files and - // directories with the requested permissions. - syscall.Umask(0000) - - return srv -} - -// haveFusermount2 finds out if the "fusermount" binary is from libfuse 2.x. -func haveFusermount2() bool { - path, err := exec.LookPath("fusermount") - if err != nil { - path = "/bin/fusermount" - } - cmd := exec.Command(path, "-V") - var out bytes.Buffer - cmd.Stdout = &out - err = cmd.Run() - if err != nil { - tlog.Warn.Printf("warning: haveFusermount2: %v", err) - return false - } - // libfuse 2: fusermount version: 2.9.9 - // libfuse 3: fusermount3 version: 3.9.0 - v := out.String() - if strings.HasPrefix(v, "fusermount version") { - return true - } - return false -} - -func handleSigint(srv *fuse.Server, mountpoint string) { - ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt) - signal.Notify(ch, syscall.SIGTERM) - go func() { - <-ch - unmount(srv, mountpoint) - os.Exit(exitcodes.SigInt) - }() -} - -// unmount() calls srv.Unmount(), and if that fails, calls "fusermount -u -z" -// (lazy unmount). -func unmount(srv *fuse.Server, mountpoint string) { - err := srv.Unmount() - if err != nil { - tlog.Warn.Printf("unmount: srv.Unmount returned %v", err) - if runtime.GOOS == "linux" { - // MacOSX does not support lazy unmount - tlog.Info.Printf("Trying lazy unmount") - cmd := exec.Command("fusermount", "-u", "-z", mountpoint) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Run() - } - } -} diff --git a/package-release-tarballs.bash b/package-release-tarballs.bash deleted file mode 100755 index ac651bf..0000000 --- a/package-release-tarballs.bash +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -set -eu - -cd "$(dirname "$0")" - -SIGNME="" - -# git_archive_extra PREFIX EXTRA1 [EXTRA2 ...] -# -# Call git-archive and add additional files to the tarball. -# Output tarball is called "$PREFIX.tar.gz" and contains one folder -# called "$PREFIX". -git_archive_extra() { - local PREFIX=$1 - shift - # Add files tracked in git - git archive --prefix "$PREFIX/" -o "$PREFIX.tar" HEAD - # Add "extra" files - tar --owner=root --group=root --transform "s!^!$PREFIX/!" --append -f "$PREFIX.tar" "$@" - # Compress - gzip -f "$PREFIX.tar" -} - -package_source() { - local GITVERSION - GITVERSION=$(git describe --tags --dirty) - echo "$GITVERSION" > VERSION - - # Render the manpages and include them in the tarball. This - # avoids a build-dependency to pandoc. - ./Documentation/MANPAGE-render.bash - - # gocryptfs source tarball - local PREFIX_SRC_ONLY=gocryptfs_${GITVERSION}_src - git_archive_extra "$PREFIX_SRC_ONLY" VERSION Documentation/*.1 - - # gocryptfs source + dependencies tarball - go mod vendor - local PREFIX_SRC_DEPS=gocryptfs_${GITVERSION}_src-deps - git_archive_extra "$PREFIX_SRC_DEPS" VERSION Documentation/*.1 vendor - - rm VERSION - rm -R vendor - - echo "Tars created." - SIGNME+=" $PREFIX_SRC_ONLY.tar.gz $PREFIX_SRC_DEPS.tar.gz" -} - -package_static_binary() { - # Compiles the gocryptfs binary and sets $GITVERSION - source build-without-openssl.bash - - if ldd gocryptfs > /dev/null ; then - echo "error: compiled gocryptfs binary is not static" - exit 1 - fi - - # Build man pages gocryptfs.1 & gocryptfs-xray.1 - ./Documentation/MANPAGE-render.bash > /dev/null - - local ARCH - ARCH=$(go env GOARCH) - local OS - OS=$(go env GOOS) - - local TARBALL - TARBALL=gocryptfs_${GITVERSION}_${OS}-static_${ARCH}.tar - local TARGZ - TARGZ=$TARBALL.gz - - tar --owner=root --group=root --create -vf "$TARBALL" gocryptfs - tar --owner=root --group=root --append -vf "$TARBALL" -C gocryptfs-xray gocryptfs-xray - tar --owner=root --group=root --append -vf "$TARBALL" -C Documentation gocryptfs.1 gocryptfs-xray.1 - - gzip -f "$TARBALL" - - echo "Tar created." - SIGNME+=" $TARGZ" -} - -signing_hint() { - local GITVERSION - GITVERSION=$(git describe --tags --dirty) - - echo "Hint for signing:" - echo " for i in gocryptfs_${GITVERSION}_*.tar.gz ; do gpg -u 23A02740 --armor --detach-sig \$i ; done" -} - -if git describe --dirty | grep dirty ; then - echo "Tree is dirty - I will not package this!" - exit 1 -fi - -package_source -package_static_binary -signing_hint diff --git a/profiling.go b/profiling.go deleted file mode 100644 index 11d6326..0000000 --- a/profiling.go +++ /dev/null @@ -1,95 +0,0 @@ -package main - -import ( - "os" - "runtime/pprof" - "runtime/trace" - "time" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// setupCpuprofile is called to handle a non-empty "-cpuprofile" cli argument -func setupCpuprofile(cpuprofileArg string) func() { - tlog.Info.Printf("Writing CPU profile to %s", cpuprofileArg) - f, err := os.Create(cpuprofileArg) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Profiler) - } - err = pprof.StartCPUProfile(f) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Profiler) - } - return func() { - pprof.StopCPUProfile() - } -} - -// setupTrace is called to handle a non-empty "-memprofile" cli argument -func setupMemprofile(memprofileArg string) func() { - tlog.Info.Printf("Will write memory profile to %q", memprofileArg) - f, err := os.Create(memprofileArg) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Profiler) - } - exiting := false - // Write the memory profile to disk every 60 seconds to get the in-use - // memory stats. - go func() { - for { - time.Sleep(60 * time.Second) - if exiting { - return - } - _, err = f.Seek(0, 0) - if err != nil { - tlog.Warn.Printf("memprofile: Seek failed: %v", err) - return - } - err = f.Truncate(0) - if err != nil { - tlog.Warn.Printf("memprofile: Truncate failed: %v", err) - return - } - err = pprof.WriteHeapProfile(f) - if err == nil { - tlog.Info.Printf("memprofile: periodic write to %q succeeded", - memprofileArg) - } else { - tlog.Warn.Printf("memprofile: periodic WriteHeapProfile failed: %v", err) - return - } - } - }() - // Final write on exit. - return func() { - exiting = true - err = pprof.WriteHeapProfile(f) - if err != nil { - tlog.Warn.Printf("memprofile: on-exit WriteHeapProfile failed: %v", err) - } - f.Close() - } -} - -// setupTrace is called to handle a non-empty "-trace" cli argument -func setupTrace(traceArg string) func() { - tlog.Info.Printf("Writing execution trace to %s", traceArg) - f, err := os.Create(traceArg) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Profiler) - } - err = trace.Start(f) - if err != nil { - tlog.Fatal.Println(err) - os.Exit(exitcodes.Profiler) - } - return func() { - trace.Stop() - } -} diff --git a/profiling/ls.bash b/profiling/ls.bash deleted file mode 100755 index 5f736b6..0000000 --- a/profiling/ls.bash +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -eu - -cd "$(dirname "$0")" - -# Download /tmp/linux-3.0.tar.gz -../tests/dl-linux-tarball.bash - -T=$(mktemp -d) -mkdir $T/a $T/b - -../gocryptfs -init -quiet -scryptn 10 -extpass "echo test" $T/a -../gocryptfs -quiet -nosyslog -extpass "echo test" $T/a $T/b - -# Cleanup trap -trap "cd /; fusermount -u -z $T/b; rm -Rf $T/a" EXIT - -echo "Creating 40000 empty files (linux-3.0.tar.gz contains 36782 files)..." -SECONDS=0 -for dir in $(seq -w 1 200); do - mkdir $T/b/$dir - ( cd $T/b/$dir ; touch $(seq -w 1 200) ) -done -echo "done, $SECONDS seconds" - -echo "Remount..." -fusermount -u $T/b -../gocryptfs -quiet -nosyslog -extpass "echo test" -cpuprofile $T/cprof -memprofile $T/mprof \ - $T/a $T/b - -echo "Running ls under profiler (3x)..." -for i in 1 2 3; do -SECONDS=0 -ls -lR $T/b > /dev/null -echo "$i done, $SECONDS seconds" -done - -echo -echo "Hint: go tool pprof ../gocryptfs $T/cprof" -echo " go tool pprof -alloc_space ../gocryptfs $T/mprof" diff --git a/profiling/streaming-read.bash b/profiling/streaming-read.bash deleted file mode 100755 index df75c68..0000000 --- a/profiling/streaming-read.bash +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -eu - -cd "$(dirname "$0")" - -T=$(mktemp -d) -mkdir $T/a $T/b - -../gocryptfs -init -quiet -scryptn 10 -extpass "echo test" $T/a -../gocryptfs -quiet -extpass "echo test" $T/a $T/b - -# Cleanup trap -trap "cd /; fusermount -u -z $T/b; rm -Rf $T/a" EXIT - -# Write 100MB test file -dd if=/dev/zero of=$T/b/zero bs=1M count=100 status=none - -# Remount with profiling -fusermount -u $T/b -../gocryptfs -quiet -extpass "echo test" -cpuprofile $T/cprof -memprofile $T/mprof \ - $T/a $T/b - -# Read 10 x 100MB instead of 1 x 1GB to keep the used disk space low -for i in $(seq 1 10); do - dd if=$T/b/zero of=/dev/null bs=1M count=100 -done - -echo -echo "Hint: go tool pprof ../gocryptfs $T/cprof" -echo " go tool pprof -alloc_space ../gocryptfs $T/mprof" diff --git a/profiling/streaming-write.bash b/profiling/streaming-write.bash deleted file mode 100755 index 7732cfb..0000000 --- a/profiling/streaming-write.bash +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -eu - -cd "$(dirname "$0")" - -T=$(mktemp -d) -mkdir $T/a $T/b - -../gocryptfs -init -quiet -scryptn 10 -extpass "echo test" $T/a -../gocryptfs -quiet -extpass "echo test" -cpuprofile $T/cprof -memprofile $T/mprof \ - $T/a $T/b - -# Cleanup trap -trap "cd /; fusermount -u -z $T/b; rm -Rf $T/a" EXIT - -# Write 10 x 100MB instead of 1 x 1GB to keep the used disk space low -for i in $(seq 1 10); do - dd if=/dev/zero of=$T/b/zero bs=1M count=100 -done - -echo -echo "Hint: go tool pprof ../gocryptfs $T/cprof" -echo " go tool pprof -alloc_space ../gocryptfs $T/mprof" diff --git a/profiling/tar-extract.bash b/profiling/tar-extract.bash deleted file mode 100755 index 21b2e2b..0000000 --- a/profiling/tar-extract.bash +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -eu - -cd "$(dirname "$0")" - -# Download /tmp/linux-3.0.tar.gz -../tests/dl-linux-tarball.bash - -T=$(mktemp -d) -mkdir $T/a $T/b - -../gocryptfs -init -quiet -scryptn 10 -extpass "echo test" $T/a -../gocryptfs -quiet -extpass "echo test" -cpuprofile $T/cprof -memprofile $T/mprof \ - $T/a $T/b - -# Cleanup trap -trap "cd /; fusermount -u -z $T/b; rm -Rf $T/a" EXIT - -echo "Extracting..." -time tar xzf /tmp/linux-3.0.tar.gz -C $T/b - -echo -echo "Hint: go tool pprof ../gocryptfs $T/cprof" -echo " go tool pprof -alloc_space ../gocryptfs $T/mprof" diff --git a/profiling/write-trace.bash b/profiling/write-trace.bash deleted file mode 100755 index 3475140..0000000 --- a/profiling/write-trace.bash +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -eu -# -# Write an execution trace of writing 100MB of data -# to a new gocryptfs mount on /tmp - -cd "$(dirname "$0")" - -T=$(mktemp -d) -mkdir $T/a $T/b - -../gocryptfs -init -quiet -scryptn 10 -extpass "echo test" $T/a -../gocryptfs -quiet -extpass "echo test" -trace $T/trace \ - $T/a $T/b - -# Cleanup trap -trap "cd /; fusermount -u -z $T/b; rm -Rf $T/a" EXIT - -# Write only 1x100MB, otherwise the trace gets too big. -dd if=/dev/zero of=$T/b/zero bs=1M count=100 - -echo -echo "Hint: go tool trace $T/trace" - diff --git a/race.go b/race.go deleted file mode 100644 index a17501a..0000000 --- a/race.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build race - -package main - -func init() { - // adds " -race" to the output of "gocryptfs -version" - raceDetector = true -} diff --git a/sendusr1.go b/sendusr1.go deleted file mode 100644 index 0fdcd0e..0000000 --- a/sendusr1.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "os" - "syscall" - - "github.com/rfjakob/gocryptfs/internal/tlog" -) - -// Send signal USR1 to "pid" (usually our parent process). This notifies it -// that the mounting has completed successfully. -func sendUsr1(pid int) { - p, err := os.FindProcess(pid) - if err != nil { - tlog.Warn.Printf("sendUsr1: FindProcess: %v\n", err) - return - } - err = p.Signal(syscall.SIGUSR1) - if err != nil { - tlog.Warn.Printf("sendUsr1: Signal: %v\n", err) - } -} diff --git a/test-without-openssl.bash b/test-without-openssl.bash deleted file mode 100755 index 3f9de9e..0000000 --- a/test-without-openssl.bash +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -eu - -cd "$(dirname "$0")" - -./test.bash -tags without_openssl "$@" diff --git a/test.bash b/test.bash deleted file mode 100755 index 091f0ac..0000000 --- a/test.bash +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -if [[ -z $TMPDIR ]]; then - TMPDIR=/var/tmp - export TMPDIR -else - echo "Using TMPDIR=$TMPDIR" -fi - -set -eu - -cd "$(dirname "$0")" -export GO111MODULE=on -MYNAME=$(basename "$0") -TESTDIR=$TMPDIR/gocryptfs-test-parent-$UID -mkdir -p "$TESTDIR" -LOCKFILE=$TESTDIR/$MYNAME.lock - -function unmount_leftovers { - RET=0 - for i in $(mount | grep "$TESTDIR" | cut -f3 -d" "); do - echo "Warning: unmounting leftover filesystem: $i" - tests/fuse-unmount.bash "$i" - RET=1 - done - return $RET -} - -( -# Prevent multiple parallel test.bash instances as this causes -# all kinds of mayham -if ! command -v flock > /dev/null ; then - echo "flock is not available, skipping" -elif ! flock -n 200 ; then - echo "Could not acquire lock on $LOCKFILE - already running?" - exit 1 -fi - -# Clean up dangling filesystems and don't exit if we found some -unmount_leftovers || true - -./build-without-openssl.bash -# Don't build with openssl if we were passed "-tags without_openssl" -if [[ "$*" != *without_openssl* ]] ; then - ./build.bash -fi - -if ! go tool | grep vet > /dev/null ; then - echo "'go tool vet' not available - skipping" -elif [[ -d vendor ]] ; then - echo "vendor directory exists, skipping 'go tool vet'" -else - go vet "$@" . -fi - -if command -v shellcheck > /dev/null ; then - # SC2002 = useless cat. Does no harm, disable the check. - shellcheck -x -e SC2002 ./*.bash -else - echo "shellcheck not installed - skipping" -fi - -# We don't want all the subprocesses -# holding the lock file open -# vvvvv -go test -count 1 ./... "$@" 200>&- -# ^^^^^^^^ -# Disable result caching - -# Clean up dangling filesystems but do exit with an error if we found one -unmount_leftovers || { echo "Error: the tests left mounted filesystems behind" ; exit 1 ; } - -# The tests cannot to this themselves as they are run in parallel. -# Don't descend into possibly still mounted example filesystems. -if [[ $OSTYPE == *linux* ]] ; then - rm -Rf --one-file-system "$TESTDIR" -else - # MacOS "rm" does not understand "--one-file-system" - rm -Rf "$TESTDIR" -fi - -if grep -R "panic(" ./*.go internal ; then - echo "$MYNAME: Please use log.Panic instead of naked panic!" - exit 1 -fi - -# All functions from the commit msg in https://go-review.googlesource.com/c/go/+/210639 -if find . -type f -name \*.go -print0 | xargs -0 grep -E 'syscall.(Setegid|Seteuid|Setgroups|Setgid|Setregid|Setreuid|Setresgid|Setresuid|Setuid)\(' ; then - echo "$MYNAME: You probably want to use unix.Setgroups and friends. See the comments in OpenatUser() for why." - exit 1 -fi - -) 200> "$LOCKFILE" diff --git a/tests/canonical-benchmarks.bash b/tests/canonical-benchmarks.bash deleted file mode 100755 index 71563ab..0000000 --- a/tests/canonical-benchmarks.bash +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -eu -# -# Run the set of "canonical" benchmarks that are shown on -# https://nuetzlich.net/gocryptfs/comparison/ -# against the directory passed as "$1". -# -# This is called by the top-level script "benchmark.bash". - -cd "$(dirname "$0")" -MYNAME=$(basename "$0") -MD5="$PWD/stress_tests/linux-3.0.md5sums" - -if [ $# -ne 1 ]; then - echo "usage: $MYNAME TESTDIR" - exit 1 -fi - -# Download /tmp/linux-3.0.tar.gz -./dl-linux-tarball.bash - -# cd to TESTDIR -cd "$1" - -# Execute command, discard all stdout output, print elapsed time -# (to stderr, unfortunately). -function etime { - # Make the bash builtin "time" print out only the elapsed wall clock - # seconds - TIMEFORMAT=%R - time "$@" > /dev/null -} - -echo -n "WRITE: " -dd if=/dev/zero of=zero bs=131072 count=2000 2>&1 | tail -n 1 -sleep 0.1 -echo -n "READ: " -dd if=zero of=/dev/null bs=131072 count=2000 2>&1 | tail -n 1 -rm zero -sleep 0.1 -echo -n "UNTAR: " -etime tar xzf /tmp/linux-3.0.tar.gz -sleep 0.1 -echo -n "MD5: " -etime md5sum --quiet -c $MD5 -sleep 0.1 -echo -n "LS: " -etime ls -lR linux-3.0 -sleep 0.1 -echo -n "RM: " -etime rm -Rf linux-3.0 diff --git a/tests/cli/cli_test.go b/tests/cli/cli_test.go deleted file mode 100644 index 9248f5d..0000000 --- a/tests/cli/cli_test.go +++ /dev/null @@ -1,888 +0,0 @@ -package cli - -// Test CLI operations like "-init", "-password" etc - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "strconv" - "strings" - "sync" - "syscall" - "testing" - "time" - - "github.com/rfjakob/gocryptfs/internal/configfile" - "github.com/rfjakob/gocryptfs/internal/exitcodes" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -var testPw = []byte("test") - -func TestMain(m *testing.M) { - test_helpers.ResetTmpDir(false) - before := test_helpers.ListFds(0, "") - r := m.Run() - after := test_helpers.ListFds(0, "") - if len(before) != len(after) { - fmt.Printf("fd leak in test process? before, after:\n%v\n%v\n", before, after) - os.Exit(1) - } - os.Exit(r) -} - -// Test -init flag -func TestInit(t *testing.T) { - dir := test_helpers.InitFS(t) - _, c, err := configfile.LoadAndDecrypt(dir+"/"+configfile.ConfDefaultName, testPw) - if err != nil { - t.Fatal(err) - } - if c.IsFeatureFlagSet(configfile.FlagAESSIV) { - t.Error("AESSIV flag should not be set") - } -} - -// Test that gocryptfs.conf and gocryptfs.diriv are there with the expected -// permissions after -init -func TestInitFilePerms(t *testing.T) { - dir := test_helpers.InitFS(t) - var st syscall.Stat_t - syscall.Stat(dir+"/gocryptfs.conf", &st) - perms := st.Mode & 0777 - if perms != 0400 { - t.Errorf("Wrong permissions for gocryptfs.conf: %#o", perms) - } - st = syscall.Stat_t{} - syscall.Stat(dir+"/gocryptfs.diriv", &st) - perms = st.Mode & 0777 - // From v1.7.1, these are created with 0440 permissions, see - // https://github.com/rfjakob/gocryptfs/issues/387 . - // From v2.0, created with 0444 perms, see - // https://github.com/rfjakob/gocryptfs/issues/539 . - if perms != 0444 { - t.Errorf("Wrong permissions for gocryptfs.diriv: %#o", perms) - } -} - -// Test -init with -devrandom flag -func TestInitDevRandom(t *testing.T) { - test_helpers.InitFS(t, "-devrandom") -} - -// Test -init with -aessiv -func TestInitAessiv(t *testing.T) { - dir := test_helpers.InitFS(t, "-aessiv") - _, c, err := configfile.LoadAndDecrypt(dir+"/"+configfile.ConfDefaultName, testPw) - if err != nil { - t.Fatal(err) - } - if !c.IsFeatureFlagSet(configfile.FlagAESSIV) { - t.Error("AESSIV flag should be set but is not") - } -} - -// Test -init with -reverse -func TestInitReverse(t *testing.T) { - dir := test_helpers.InitFS(t, "-reverse") - _, c, err := configfile.LoadAndDecrypt(dir+"/"+configfile.ConfReverseName, testPw) - if err != nil { - t.Fatal(err) - } - if !c.IsFeatureFlagSet(configfile.FlagAESSIV) { - t.Error("AESSIV flag should be set but is not") - } -} - -// 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" - 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() - if err != nil { - t.Error(err) - } - // Change password 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() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Error(err) - } - // Old password - p.Write([]byte("test\n")) - // New password - p.Write([]byte("newpasswd\n")) - p.Close() - err = cmd.Wait() - if err != nil { - t.Error(err) - } -} - -// Test -passwd flag -func TestPasswd(t *testing.T) { - // Create FS - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - // Add content - test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo test") - file1 := mnt + "/file1" - err := ioutil.WriteFile(file1, []byte("somecontent"), 0600) - if err != nil { - t.Fatal(err) - } - err = test_helpers.UnmountErr(mnt) - if err != nil { - t.Fatal(err) - } - // Change password to "newpasswd" - testPasswd(t, dir) - // Mount and verify - test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo newpasswd") - content, err := ioutil.ReadFile(file1) - if err != nil { - t.Error(err) - } else if string(content) != "somecontent" { - t.Errorf("wrong content: %q", string(content)) - } - err = test_helpers.UnmountErr(mnt) - if err != nil { - t.Fatal(err) - } -} - -// cp copies file at `src` to `dst`, overwriting -// `dst` if it already exists. Calls t.Fatal on failure. -func cp(t *testing.T, src string, dst string) { - conf, err := ioutil.ReadFile(src) - if err != nil { - t.Fatal(err) - } - syscall.Unlink(dst) - err = ioutil.WriteFile(dst, conf, 0600) - if err != nil { - t.Fatal(err) - } -} - -// Test -passwd with -masterkey -func TestPasswdMasterkey(t *testing.T) { - // Create FS - dir := test_helpers.InitFS(t) - // Overwrite with config with known master key - cp(t, "gocryptfs.conf.b9e5ba23", dir+"/gocryptfs.conf") - // Add content - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo test") - file1 := mnt + "/file1" - err := ioutil.WriteFile(file1, []byte("somecontent"), 0600) - if err != nil { - t.Fatal(err) - } - test_helpers.UnmountPanic(mnt) - // Change password using stdin - args := []string{"-q", "-passwd", "-masterkey", - "b9e5ba23-981a22b8-c8d790d8-627add29-f680513f-b7b7035f-d203fb83-21d82205"} - args = append(args, dir) - cmd := exec.Command(test_helpers.GocryptfsBinary, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - p, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Error(err) - } - // New password - p.Write([]byte("newpasswd\n")) - p.Close() - err = cmd.Wait() - if err != nil { - t.Error(err) - } - // Mount and verify - test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo newpasswd") - content, err := ioutil.ReadFile(file1) - if err != nil { - t.Error(err) - } else if string(content) != "somecontent" { - t.Errorf("wrong content: %q", string(content)) - } - test_helpers.UnmountPanic(mnt) -} - -// Test -passwd with -masterkey=stdin -func TestPasswdMasterkeyStdin(t *testing.T) { - // Create FS - dir := test_helpers.InitFS(t) - // Overwrite with config with known master key - cp(t, "gocryptfs.conf.b9e5ba23", dir+"/gocryptfs.conf") - // Add content - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo test") - file1 := mnt + "/file1" - err := ioutil.WriteFile(file1, []byte("somecontent"), 0600) - if err != nil { - t.Fatal(err) - } - test_helpers.UnmountPanic(mnt) - // Change password using stdin - args := []string{"-q", "-passwd", "-masterkey=stdin"} - args = append(args, dir) - cmd := exec.Command(test_helpers.GocryptfsBinary, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - p, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Error(err) - } - // Masterkey - p.Write([]byte("b9e5ba23-981a22b8-c8d790d8-627add29-f680513f-b7b7035f-d203fb83-21d82205\n")) - // New password - p.Write([]byte("newpasswd\n")) - p.Close() - err = cmd.Wait() - if err != nil { - t.Fatal(err) - } - // Mount and verify - test_helpers.MountOrFatal(t, dir, mnt, "-extpass", "echo newpasswd") - content, err := ioutil.ReadFile(file1) - if err != nil { - t.Fatal(err) - } else if string(content) != "somecontent" { - t.Errorf("wrong content: %q", string(content)) - } - test_helpers.UnmountPanic(mnt) -} - -// Test -passwd with -reverse -func TestPasswdReverse(t *testing.T) { - // Create FS - dir := test_helpers.InitFS(t, "-reverse") - testPasswd(t, dir, "-reverse") -} - -// Test -passwd with -scryptn -func TestPasswdScryptn(t *testing.T) { - dir := test_helpers.InitFS(t) - cf, err := configfile.Load(dir + "/gocryptfs.conf") - if err != nil { - t.Fatal(err) - } - testPasswd(t, dir, "-scryptn", strconv.Itoa(cf.ScryptObject.LogN()+1)) - cf2, err := configfile.Load(dir + "/gocryptfs.conf") - if err != nil { - t.Fatal(err) - } - if cf2.ScryptObject.LogN() != cf.ScryptObject.LogN()+1 { - t.Errorf("wrong logN value %d", cf2.ScryptObject.LogN()) - } -} - -// Test -init & -config flag -func TestInitConfig(t *testing.T) { - config := test_helpers.TmpDir + "/TestInitConfig.conf" - dir := test_helpers.InitFS(t, "-config="+config) - - _, err := os.Stat(config) - if err != nil { - t.Fatal(err) - } - - // Test -passwd & -config - cmd2 := exec.Command(test_helpers.GocryptfsBinary, "-q", "-passwd", "-extpass", "echo test", - "-config", config, dir) - cmd2.Stdout = os.Stdout - cmd2.Stderr = os.Stderr - err = cmd2.Run() - if err != nil { - t.Error(err) - } -} - -// Test -ro -func TestRo(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dir, mnt, "-ro", "-extpass=echo test") - defer test_helpers.UnmountPanic(mnt) - - file := mnt + "/file" - err := os.Mkdir(file, 0777) - if err == nil { - t.Errorf("Mkdir should have failed") - } - _, err = os.Create(file) - if err == nil { - t.Errorf("Create should have failed") - } -} - -// Test "-nonempty" -func TestNonempty(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - err := os.Mkdir(mnt, 0700) - if err != nil { - t.Fatal(err) - } - err = ioutil.WriteFile(mnt+"/somefile", []byte("xyz"), 0600) - if err != nil { - t.Fatal(err) - } - err = test_helpers.Mount(dir, mnt, false, "-extpass=echo test") - if err == nil { - t.Errorf("Mounting over a file should fail per default") - } - // Should work with "-nonempty" - test_helpers.MountOrFatal(t, dir, mnt, "-nonempty", "-extpass=echo test") - test_helpers.UnmountPanic(mnt) -} - -// -nofail should be ignored and the mount should succeed -func TestNofail(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dir, mnt, "-nofail", "-extpass=echo test") - defer test_helpers.UnmountPanic(mnt) -} - -// Test "mountpoint shadows cipherdir" handling -func TestShadows(t *testing.T) { - mnt := test_helpers.InitFS(t) - cipher := mnt + ".cipher" - err := os.Rename(mnt, cipher) - if err != nil { - t.Fatal(err) - } - // This should work - // (note that MountOrFatal creates "mnt" again) - test_helpers.MountOrFatal(t, cipher, mnt, "-extpass=echo test") - test_helpers.UnmountPanic(mnt) - cipher2 := mnt + "/cipher" - err = os.Rename(cipher, cipher2) - if err != nil { - t.Fatal(err) - } - // This should fail - err = test_helpers.Mount(cipher2, mnt, false, "-extpass=echo test") - if err == nil { - t.Errorf("Should have failed") - } -} - -// TestMountPasswordIncorrect makes sure the correct exit code is used when the password -// was incorrect while mounting -func TestMountPasswordIncorrect(t *testing.T) { - cDir := test_helpers.InitFS(t) // Create filesystem with password "test" - pDir := cDir + ".mnt" - err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo WRONG", "-wpanic=false") - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != exitcodes.PasswordIncorrect { - t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode) - } -} - -// TestPasswdPasswordIncorrect makes sure the correct exit code is used when the password -// was incorrect while changing the password -func TestPasswdPasswordIncorrect(t *testing.T) { - cDir := test_helpers.InitFS(t) // Create filesystem with password "test" - // Change password - cmd := exec.Command(test_helpers.GocryptfsBinary, "-passwd", cDir) - childStdin, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - _, err = childStdin.Write([]byte("WRONGPASSWORD\nNewPassword")) - if err != nil { - t.Fatal(err) - } - err = childStdin.Close() - if err != nil { - t.Fatal(err) - } - err = cmd.Wait() - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != exitcodes.PasswordIncorrect { - t.Errorf("want=%d, got=%d", exitcodes.PasswordIncorrect, exitCode) - } -} - -// Check that we correctly background on mount and close stderr and stdout. -// Something like -// gocryptfs a b | cat -// must not hang ( https://github.com/rfjakob/gocryptfs/issues/130 ). -func TestMountBackground(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - err := os.Mkdir(mnt, 0700) - if err != nil { - t.Fatal(err) - } - // Manually create a pipe pair and connect the child's stdout and stderr - // to it. We cannot use StdoutPipe because that will close the pipe - // when the child forks away. - pr, pw, err := os.Pipe() - if err != nil { - t.Fatal(err) - } - args := []string{"-extpass", "echo test", dir, mnt} - cmd := exec.Command(test_helpers.GocryptfsBinary, args...) - cmd.Stdout = pw - cmd.Stderr = pw - err = cmd.Run() - if err != nil { - t.Error(err) - } - pw.Close() - defer test_helpers.UnmountPanic(mnt) - // Read until we get EOF. - c1 := make(chan struct{}, 1) - go func() { - buf := make([]byte, 1000) - for { - _, err = pr.Read(buf) - // We should get io.EOF when the child closes stdout - // and stderr. - if err != nil { - pr.Close() - c1 <- struct{}{} - return - } - } - }() - select { - case <-c1: - return - case <-time.After(time.Second * 5): - t.Fatal("timeout") - } -} - -// Test that "gocryptfs -init -info CIPHERDIR" returns an error to the -// user. Only one operation flag is allowed. -func TestMultipleOperationFlags(t *testing.T) { - // Test all combinations - opFlags := []string{"-init", "-info", "-passwd", "-fsck"} - for _, flag1 := range opFlags { - var flag2 string - for _, flag2 = range opFlags { - if flag1 == flag2 { - continue - } - args := []string{flag1, flag2, "/tmp"} - //t.Logf("testing %v", args) - cmd := exec.Command(test_helpers.GocryptfsBinary, args...) - err := cmd.Run() - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != exitcodes.Usage { - t.Fatalf("this should have failed with code %d, but returned %d", - exitcodes.Usage, exitCode) - } - } - } -} - -func TestNoexec(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test", "-noexec") - defer test_helpers.UnmountPanic(mnt) - sh := mnt + "/x.sh" - content := `#!/bin/bash -echo hello -` - err := ioutil.WriteFile(sh, []byte(content), 0755) - if err != nil { - t.Fatal(err) - } - err = exec.Command(sh).Run() - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != int(syscall.EACCES) { - t.Errorf("got exitcode %d instead of EPERM (%d)", exitCode, syscall.EPERM) - } -} - -// Test that a missing argument to "-o" triggers exit code 1. -// See also cli_args_test.go for comprehensive tests of "-o" parsing. -func TestMissingOArg(t *testing.T) { - cmd := exec.Command(test_helpers.GocryptfsBinary, "foo", "bar", "-o") - err := cmd.Run() - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != exitcodes.Usage { - t.Fatalf("this should have failed with code %d, but returned %d", - exitcodes.Usage, exitCode) - } -} - -// -exclude must return an error in forward mode -func TestExcludeForward(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - err := test_helpers.Mount(dir, mnt, false, "-extpass", "echo test", "-exclude", "foo") - if err == nil { - t.Errorf("-exclude in forward mode should fail") - } - t.Log(err) -} - -// Check that the config file can be read from a named pipe. -// Make sure bug https://github.com/rfjakob/gocryptfs/issues/258 does not come -// back. -func TestConfigPipe(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - err := os.Mkdir(mnt, 0700) - if err != nil { - t.Fatal(err) - } - bashLine := fmt.Sprintf("%s -q -extpass \"echo test\" -config <(cat %s/gocryptfs.conf) %s %s", test_helpers.GocryptfsBinary, dir, dir, mnt) - cmd := exec.Command("bash", "-c", bashLine) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stdout - err = cmd.Run() - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != 0 { - t.Errorf("bash command\n%q\nresulted in exit code %d", bashLine, exitCode) - return - } - test_helpers.UnmountPanic(mnt) -} - -// Ciphertext dir and mountpoint contains a comma -// https://github.com/rfjakob/gocryptfs/issues/262 -func TestComma(t *testing.T) { - dir0 := test_helpers.InitFS(t) - dir := dir0 + ",foo,bar" - err := os.Rename(dir0, dir) - if err != nil { - t.Fatal(err) - } - mnt := dir + ".mnt" - err = test_helpers.Mount(dir, mnt, false, "-extpass", "echo test", "-wpanic=0") - if err != nil { - t.Fatalf("Failed to mount %q on %q: %v", dir, mnt, err) - } - test_helpers.UnmountPanic(mnt) -} - -// Mount with idle timeout 10ms and check that the process exits by itself -// within 5 seconds. -func TestIdle(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - err := os.Mkdir(mnt, 0700) - if err != nil { - t.Fatal(err) - } - cmd := exec.Command(test_helpers.GocryptfsBinary, - "-q", "-nosyslog", "-fg", "-extpass", "echo test", "-i", "10ms", dir, mnt) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - t.Fatal(err) - } - timer := time.AfterFunc(5*time.Second, func() { - t.Error("timeout waiting for umount") - cmd.Process.Kill() - }) - err = cmd.Wait() - timer.Stop() - if err != nil { - t.Error(err) - } -} - -// Mount with idle timeout of 100ms read something every 10ms. The fs should -// NOT get unmounted. Regression test for https://github.com/rfjakob/gocryptfs/issues/421 -func TestNotIdle(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - err := test_helpers.Mount(dir, mnt, false, "-extpass", "echo test", "-i=100ms") - if err != nil { - t.Fatal(err) - } - err = ioutil.WriteFile(mnt+"/foo", []byte("foo"), 0600) - if err != nil { - t.Fatal(err) - } - // Read every 10 milliseconds for a total of 1 second - for i := 1; i < 100; i++ { - _, err = ioutil.ReadFile(mnt + "/foo") - if err != nil { - t.Fatalf("iteration %d failed: %v", i, err) - } - time.Sleep(10 * time.Millisecond) - } - // Keep a file handle open for 1 second - fd, err := os.Open(mnt + "/foo") - if err != nil { - t.Fatal(err) - } - time.Sleep(1 * time.Second) - buf := make([]byte, 100) - _, err = fd.Read(buf) - if err != nil { - t.Fatal(err) - } - fd.Close() - // All good. - test_helpers.UnmountPanic(mnt) -} - -// TestSymlinkedCipherdir checks that if CIPHERDIR itself is a symlink, it is -// followed. -// https://github.com/rfjakob/gocryptfs/issues/450 -func TestSymlinkedCipherdir(t *testing.T) { - dir := test_helpers.InitFS(t) - dirSymlink := dir + ".symlink" - err := os.Symlink(dir, dirSymlink) - if err != nil { - t.Fatal(err) - } - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dirSymlink, mnt, "-extpass=echo test") - defer test_helpers.UnmountPanic(mnt) - - file := mnt + "/file" - f, err := os.Create(file) - if err != nil { - t.Fatal(err) - } - f.Close() - - f, err = os.Open(mnt) - if err != nil { - t.Fatal(err) - } - defer f.Close() - names, err := f.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - if len(names) != 1 || names[0] != "file" { - t.Errorf("wrong Readdirnames result: %v", names) - } -} - -// TestBadname tests the `-badname` option -func TestBadname(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - validFileName := "file" - invalidSuffix := ".invalid_file" - - // use static suffix for testing - test_helpers.MountOrFatal(t, dir, mnt, "-badname=*", "-extpass=echo test") - defer test_helpers.UnmountPanic(mnt) - - // write one valid filename (empty content) - file := mnt + "/" + validFileName - err := ioutil.WriteFile(file, nil, 0600) - if err != nil { - t.Fatal(err) - } - - // read encrypted file name - fread, err := os.Open(dir) - if err != nil { - t.Fatal(err) - } - defer fread.Close() - - encryptedfilename := "" - ciphernames, err := fread.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - for _, ciphername := range ciphernames { - if ciphername != "gocryptfs.conf" && ciphername != "gocryptfs.diriv" { - encryptedfilename = ciphername - // found cipher name of "file" - break - } - } - - // write invalid file which should be decodable - err = ioutil.WriteFile(dir+"/"+encryptedfilename+invalidSuffix, nil, 0600) - if err != nil { - t.Fatal(err) - } - // write invalid file which is not decodable (cropping the encrpyted file name) - err = ioutil.WriteFile(dir+"/"+encryptedfilename[:len(encryptedfilename)-2]+invalidSuffix, nil, 0600) - if err != nil { - t.Fatal(err) - } - - // check for filenames - f, err := os.Open(mnt) - if err != nil { - t.Fatal(err) - } - defer f.Close() - names, err := f.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - foundDecodable := false - foundUndecodable := false - for _, name := range names { - if strings.Contains(name, validFileName+invalidSuffix+" GOCRYPTFS_BAD_NAME") { - foundDecodable = true - } else if strings.Contains(name, encryptedfilename[:len(encryptedfilename)-2]+invalidSuffix+" GOCRYPTFS_BAD_NAME") { - foundUndecodable = true - } - } - - if !foundDecodable { - t.Errorf("did not find invalid name %s in %v", validFileName+invalidSuffix+" GOCRYPTFS_BAD_NAME", names) - } - - if !foundUndecodable { - t.Errorf("did not find invalid name %s in %v", encryptedfilename[:len(encryptedfilename)-2]+invalidSuffix+" GOCRYPTFS_BAD_NAME", names) - } -} - -// TestPassfile tests the `-passfile` option -func TestPassfile(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - passfile1 := mnt + ".1.txt" - ioutil.WriteFile(passfile1, []byte("test"), 0600) - test_helpers.MountOrFatal(t, dir, mnt, "-passfile="+passfile1) - defer test_helpers.UnmountPanic(mnt) -} - -// TestPassfileX2 tests that the `-passfile` option can be passed twice -func TestPassfileX2(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - passfile1 := mnt + ".1.txt" - passfile2 := mnt + ".2.txt" - ioutil.WriteFile(passfile1, []byte("te"), 0600) - ioutil.WriteFile(passfile2, []byte("st"), 0600) - test_helpers.MountOrFatal(t, dir, mnt, "-passfile="+passfile1, "-passfile="+passfile2) - defer test_helpers.UnmountPanic(mnt) -} - -// TestInitNotEmpty checks that `gocryptfs -init` returns the right error code -// if CIPHERDIR is not empty. See https://github.com/rfjakob/gocryptfs/pull/503 -func TestInitNotEmpty(t *testing.T) { - dir := test_helpers.TmpDir + "/" + t.Name() - if err := os.Mkdir(dir, 0700); err != nil { - t.Fatal(err) - } - if err := ioutil.WriteFile(dir+"/foo", nil, 0700); err != nil { - t.Fatal(err) - } - cmd := exec.Command(test_helpers.GocryptfsBinary, "-init", "-extpass", "echo test", dir) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err := cmd.Run() - exitCode := test_helpers.ExtractCmdExitCode(err) - if exitCode != exitcodes.CipherDir { - t.Fatalf("wrong exit code: have=%d, want=%d", exitCode, exitcodes.CipherDir) - } -} - -// TestSharedstorage checks that `-sharedstorage` hands out arbitrary inode -// numbers (no hard link tracking) -func TestSharedstorage(t *testing.T) { - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test", "-sharedstorage") - defer test_helpers.UnmountPanic(mnt) - foo1 := mnt + "/foo1" - foo2 := mnt + "/foo2" - if err := ioutil.WriteFile(foo1, nil, 0755); err != nil { - t.Fatal(err) - } - if err := os.Link(foo1, foo2); err != nil { - t.Fatal(err) - } - var st1, st2, st3 syscall.Stat_t - if err := syscall.Stat(foo1, &st1); err != nil { - t.Fatal(err) - } - // The link show show a new inode number - if err := syscall.Stat(foo2, &st2); err != nil { - t.Fatal(err) - } - // Stat()'ing again should give us again a new inode number - if err := syscall.Stat(foo2, &st3); err != nil { - t.Fatal(err) - } - if st1.Ino == st2.Ino || st2.Ino == st3.Ino || st1.Ino == st3.Ino { - t.Error(st1.Ino, st2.Ino, st3.Ino) - } - // Check that we we don't have stat caching. New length should show up - // on the hard link immediately. - if err := ioutil.WriteFile(foo1, []byte("xxxxxx"), 0755); err != nil { - t.Fatal(err) - } - if err := syscall.Stat(foo2, &st2); err != nil { - t.Fatal(err) - } - if st2.Size != 6 { - t.Fatal(st2.Size) - } -} - -// Test that the filesystem is immediately ready for Creat() after mount returns -func TestMountCreat(t *testing.T) { - const concurrency = 2 - const repeat = 2 - - dir := test_helpers.InitFS(t) - mnt := dir + ".mnt" - - for j := 0; j < repeat; j++ { - test_helpers.MountOrFatal(t, dir, mnt, "-extpass=echo test") - var wg sync.WaitGroup - wg.Add(concurrency) - for i := 0; i < concurrency; i++ { - go func(i int) { - path := fmt.Sprintf("%s/%d", mnt, i) - fd, err := syscall.Creat(path, 0600) - syscall.Close(fd) - if err != nil { - t.Errorf("Creat %q: %v", path, err) - } - wg.Done() - }(i) - } - wg.Wait() - test_helpers.UnmountPanic(mnt) - } -} diff --git a/tests/cli/gocryptfs.conf.b9e5ba23 b/tests/cli/gocryptfs.conf.b9e5ba23 deleted file mode 100644 index 9b2e61a..0000000 --- a/tests/cli/gocryptfs.conf.b9e5ba23 +++ /dev/null @@ -1,18 +0,0 @@ -{ - "Creator": "gocryptfs v1.1-rc1-31-gc487e17-dirty", - "EncryptedKey": "vthRM/vXg3Cn16jld2JxPiRv8OSBSwTYDzOqdlrvD1agJefZhhvDQS7b5fC7B7JRL4UiVUKkNaJRVMRf", - "ScryptObject": { - "Salt": "uVwNDUMLWktmV0WgvPHztVrmoahfYN8A/22I/uq66sU=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames" - ] -} \ No newline at end of file diff --git a/tests/defaults/1980.tar.gz b/tests/defaults/1980.tar.gz deleted file mode 100644 index 1073746cf35e8b3536dfee64bf8732e5a835891d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115 zcmb2|=3uyVi9LdW`RzGFzC#8AEDrWZ^*eHo7d^0CsKP2F(fNF`$ix*3xUTku-AY=& zqsDIO!u?ei0a07Sw(DivRf`8{=FVBBtN3(IHm{-V&nxeKzW8i=tYGf++fPs5T`$W3 R1OHy|hD{4!!=S;y008j7Ff#xE diff --git a/tests/defaults/acl_test.go b/tests/defaults/acl_test.go deleted file mode 100644 index b3826e8..0000000 --- a/tests/defaults/acl_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package defaults - -import ( - "io/ioutil" - "math/rand" - "os" - "os/exec" - "path/filepath" - "syscall" - "testing" - "time" - - "golang.org/x/sys/unix" - - "github.com/pkg/xattr" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -// https://github.com/rfjakob/gocryptfs/issues/543 -func TestCpA(t *testing.T) { - fn1 := filepath.Join(test_helpers.TmpDir, t.Name()) - fn2 := filepath.Join(test_helpers.DefaultPlainDir, t.Name()) - - rand.Seed(int64(time.Now().Nanosecond())) - - { - // Need unrestricted umask - old := syscall.Umask(000) - defer syscall.Umask(old) - } - - for i := 0; i < 10; i++ { - // Random permissions (except owner read, which cp needs) - var modeWant os.FileMode = os.FileMode(rand.Int31n(0777+1) | 0400) - - // Create file outside mount - err := ioutil.WriteFile(fn1, nil, modeWant) - if err != nil { - t.Fatal(err) - } - // Verify perms (umask problems?) - fi, err := os.Stat(fn1) - if err != nil { - t.Fatal(err) - } - if fi.Mode() != modeWant { - t.Errorf("ioutil.WriteFile created wrong permissions: want %o have %o", modeWant, fi.Mode()) - } - - // "cp -a" from outside to inside mount - c := exec.Command("cp", "-a", fn1, fn2) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - t.Fatal(err) - } - - // Check perms - fi, err = os.Stat(fn2) - if err != nil { - t.Fatal(err) - } - if fi.Mode() != modeWant { - t.Errorf("cp -a did not preserve permissions: want %o have %o", modeWant, fi.Mode()) - } - - syscall.Unlink(fn1) - syscall.Unlink(fn2) - } -} - -func getfacl(fn string) (string, error) { - c := exec.Command("getfacl", "-c", "--", fn) - out, err := c.Output() - return string(out), err -} - -// https://github.com/rfjakob/gocryptfs/issues/543 -func TestAcl543(t *testing.T) { - fn1 := test_helpers.TmpDir + "/TestAcl543" - fn2 := test_helpers.DefaultPlainDir + "/TestAcl543" - - var c *exec.Cmd - - var modeWant os.FileMode = 0777 - - { - // Need unrestricted umask - old := syscall.Umask(000) - defer syscall.Umask(old) - } - - // Set acl on file outside gocryptfs mount - err := ioutil.WriteFile(fn1, nil, modeWant) - if err != nil { - t.Fatal(err) - } - c = exec.Command("setfacl", "-m", "u:daemon:rwx", fn1) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - t.Skip(err) - } - aclWant, err := getfacl(fn1) - if err != nil { - t.Fatal(err) - } - fi, err := os.Stat(fn1) - if fi.Mode() != modeWant { - t.Fatalf("mode changed from %o to %o", modeWant, fi.Mode()) - } - - // Set acl on file inside gocryptfs mount - err = ioutil.WriteFile(fn2, nil, modeWant) - if err != nil { - t.Fatal(err) - } - c = exec.Command("setfacl", "-m", "u:daemon:rwx", fn2) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - t.Fatal(err) - } - aclHave1, err := getfacl(fn1) - if err != nil { - t.Fatal(err) - } - if aclHave1 != aclWant { - t.Error(aclHave1) - } - os.Remove(fn2) - - // "cp -a" from outside to inside mount - c = exec.Command("cp", "-a", fn1, fn2) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err = c.Run() - if err != nil { - t.Fatal(err) - } - fi, err = os.Stat(fn2) - if err != nil { - t.Fatal(err) - } - if fi.Mode() != modeWant { - t.Errorf("cp -a did not preserve permissions: want %o have %o", modeWant, fi.Mode()) - } - aclHave2, err := getfacl(fn2) - if err != nil { - t.Fatal(err) - } - if aclHave2 != aclWant { - t.Errorf("cp -a did not preserve acl: %q", aclHave1) - } -} - -// Check that we handle zero-sized and undersized buffers correctly -func TestXattrOverflow(t *testing.T) { - fn := filepath.Join(test_helpers.DefaultPlainDir, t.Name()) - ioutil.WriteFile(fn, nil, 0600) - - attr := "user.foo123" - val := []byte("12341234") - err := xattr.LSet(fn, attr, val) - if err != nil { - t.Skip(err) - } - - // Getxattr - sz, err := unix.Lgetxattr(fn, attr, nil) - if err != nil { - t.Error(err) - } - if sz != len(val) { - t.Errorf("wrong sz: want %d have %d", len(val), sz) - } - sz, err = unix.Lgetxattr(fn, attr, make([]byte, 1)) - if err != syscall.ERANGE { - t.Error(err) - } - - // Listxattr - szWant, err := unix.Llistxattr(fn, make([]byte, 64*1024)) - if err != nil { - t.Fatal(err) - } - sz, err = unix.Llistxattr(fn, nil) - if err != nil { - t.Error(err) - } - if sz != szWant { - t.Errorf("wrong sz: want %d have %d", szWant, sz) - } - sz, err = unix.Llistxattr(fn, make([]byte, 1)) - if err != syscall.ERANGE { - t.Error(err) - } -} diff --git a/tests/defaults/ctlsock_test.go b/tests/defaults/ctlsock_test.go deleted file mode 100644 index 64b72a2..0000000 --- a/tests/defaults/ctlsock_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package defaults - -import ( - "os" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/ctlsock" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func TestCtlSock(t *testing.T) { - cDir := test_helpers.InitFS(t) - pDir := cDir + ".mnt" - sock := cDir + ".sock" - test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") - defer test_helpers.UnmountPanic(pDir) - req := ctlsock.RequestStruct{ - EncryptPath: "foobar", - } - response := test_helpers.QueryCtlSock(t, sock, req) - if response.Result == "" || response.ErrNo != 0 { - t.Errorf("got an error reply: %+v", response) - } - req.EncryptPath = "not-existing-dir/xyz" - response = test_helpers.QueryCtlSock(t, sock, req) - if response.ErrNo != int32(syscall.ENOENT) || response.Result != "" { - t.Errorf("incorrect error handling: wanted ErrNo=%d, have %+v", syscall.ENOENT, response) - } - // Strange paths should not cause a crash - crashers := []string{"/foo", "foo/", "/foo/", ".", "/////", "/../../."} - for _, c := range crashers { - req.EncryptPath = c - // QueryCtlSock calls t.Fatal if it gets EOF when gocryptfs panics - response = test_helpers.QueryCtlSock(t, sock, req) - if response.WarnText == "" { - t.Errorf("We should get a warning about non-canonical paths here") - } - } -} - -func TestCtlSockDecrypt(t *testing.T) { - cDir := test_helpers.InitFS(t) - pDir := cDir + ".mnt" - sock := cDir + ".sock" - test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") - defer test_helpers.UnmountPanic(pDir) - - paths := []string{ - "xxxxxxx123456789", - "foo/bar/baz", - test_helpers.X255, - "123/" + test_helpers.X255, - "123/" + test_helpers.X255 + "/456", - } - - for _, p := range paths { - // Create path - err := os.MkdirAll(pDir+"/"+p, 0700) - if err != nil { - t.Fatal(err) - } - // Encrypt the path through the ctlsock - req := ctlsock.RequestStruct{ - EncryptPath: p, - } - response := test_helpers.QueryCtlSock(t, sock, req) - if response.Result == "" || response.ErrNo != 0 { - t.Fatalf("got an error reply: %+v", response) - } - // Check if the encrypted path actually exists - cPath := response.Result - _, err = os.Stat(cDir + "/" + cPath) - if err != nil { - t.Fatal(err) - } - // Decrypt the path through the ctlsock and see if we get the original path - req = ctlsock.RequestStruct{ - DecryptPath: cPath, - } - response = test_helpers.QueryCtlSock(t, sock, req) - if response.Result == "" || response.ErrNo != 0 { - t.Errorf("query=%+v, response=%+v", req, response) - continue - } - if response.Result != p { - t.Errorf("want=%q got=%q", p, response.Result) - } - } -} - -func TestCtlSockDecryptCrash(t *testing.T) { - cDir := test_helpers.InitFS(t) - pDir := cDir + ".mnt" - sock := cDir + ".sock" - test_helpers.MountOrFatal(t, cDir, pDir, "-ctlsock="+sock, "-extpass", "echo test") - defer test_helpers.UnmountPanic(pDir) -} diff --git a/tests/defaults/diriv_test.go b/tests/defaults/diriv_test.go deleted file mode 100644 index bf8f233..0000000 --- a/tests/defaults/diriv_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package defaults - -import ( - "io/ioutil" - "os" - "sync" - "sync/atomic" - "syscall" - "testing" - "time" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func TestDirIVRace(t *testing.T) { - // Create "dir1" with one file in it - dir1 := test_helpers.DefaultPlainDir + "/TestDirIVRace_Dir1" - err := os.Mkdir(dir1, 0700) - if err != nil { - t.Fatal(err) - } - err = ioutil.WriteFile(dir1+"/file", nil, 0600) - if err != nil { - t.Fatal(err) - } - - // Create directory "dir2" - dir2 := test_helpers.DefaultPlainDir + "/TestDirIVRace_Dir2" - err = os.Mkdir(dir2, 0700) - if err != nil { - t.Fatal(err) - } - file2 := dir2 + "/file" - err = ioutil.WriteFile(file2, nil, 0600) - if err != nil { - t.Fatal(err) - } - - var stop int32 - defer func() { atomic.StoreInt32(&stop, 1) }() - - var wg sync.WaitGroup - wg.Add(1) - go func() { - wg.Done() - for { - // Keep dir2 in the diriv cache - fd, err2 := os.Open(file2) - if err2 == nil { - fd.Close() - } - if atomic.LoadInt32(&stop) != 0 { - return - } - } - }() - wg.Wait() - time.Sleep(time.Millisecond) - - // Overwrite dir2 with dir1 - err = syscall.Unlink(file2) - if err != nil { - t.Fatal(err) - } - err = syscall.Rename(dir1, dir2) - if err != nil { - t.Fatal(err) - } - // We should be able to stat file2 - _, err = os.Stat(file2) - if err != nil { - t.Error(err) - } -} diff --git a/tests/defaults/main_test.go b/tests/defaults/main_test.go deleted file mode 100644 index 8873f8f..0000000 --- a/tests/defaults/main_test.go +++ /dev/null @@ -1,372 +0,0 @@ -// Tests and benchmarks performed with default settings only. -package defaults - -import ( - "bytes" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "strings" - "sync" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func TestMain(m *testing.M) { - test_helpers.ResetTmpDir(true) - test_helpers.MountOrExit(test_helpers.DefaultCipherDir, test_helpers.DefaultPlainDir, "-zerokey") - r := m.Run() - test_helpers.UnmountPanic(test_helpers.DefaultPlainDir) - os.Exit(r) -} - -// Test that we get the right timestamp when extracting a tarball. -func Test1980Tar(t *testing.T) { - c := exec.Command("tar", "xzf", "1980.tar.gz", "-C", test_helpers.DefaultPlainDir) - c.Stderr = os.Stderr - c.Stdout = os.Stdout - err := c.Run() - if err != nil { - t.Fatal(err) - } - fi, err := os.Stat(test_helpers.DefaultPlainDir + "/1980.txt") - if err != nil { - t.Fatal(err) - } - m := fi.ModTime().Unix() - if m != 315619323 { - t.Errorf("Wrong mtime: %d", m) - } -} - -// In gocryptfs before v1.2, the file header was only read once for each -// open. But truncating a file to zero will generate a new random file ID. -// The sequence below caused an I/O error to be returned. -func TestOpenTruncateRead(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/TestTruncateWrite" - // First FD is used for write and truncate. - writeFd, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - defer writeFd.Close() - abc := []byte("abc") - _, err = writeFd.WriteAt(abc, 0) - if err != nil { - t.Fatal(err) - } - // Second FD is just for reading. - readFd, err := os.Open(fn) - if err != nil { - t.Fatal(err) - } - defer readFd.Close() - content := make([]byte, 3) - _, err = readFd.ReadAt(content, 0) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(content, abc) { - t.Fatalf("wrong content: %s", string(content)) - } - // Truncate to zero to generate a new file ID and write new content. - err = writeFd.Truncate(0) - if err != nil { - t.Fatal(err) - } - xyz := []byte("xyz") - _, err = writeFd.WriteAt(xyz, 0) - if err != nil { - t.Fatal(err) - } - // Try to read from the other FD. - _, err = readFd.ReadAt(content, 0) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(content, xyz) { - t.Fatalf("wrong content: %s", string(content)) - } -} - -// TestWORead tries to read from a write-only FD. -func TestWORead(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/TestWORead" - fd, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0600) - if err != nil { - t.Fatal(err) - } - defer fd.Close() - buf := make([]byte, 10) - _, err = fd.Read(buf) - if err == nil { - t.Error("Reading from write-only file should fail, but did not") - } -} - -// xfstests generic/124 triggers this warning: -// cipherSize 18 == header size: interrupted write? -// This test reproduces the problem. -func TestXfs124(t *testing.T) { - // GOMAXPROCS=8 and N=5000 seem to reliably trigger the problem. With N=1000, - // the test passes sometimes. - runtime.GOMAXPROCS(8) - N := 5000 - - fn := test_helpers.DefaultPlainDir + "/TestXfs124" - fd, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - defer fd.Close() - - var wg sync.WaitGroup - wg.Add(2) - - go func() { - buf := make([]byte, 10) - var err2 error - for i := 0; i < N; i++ { - err2 = fd.Truncate(0) - if err2 != nil { - panic(err2) - } - _, err2 = fd.WriteAt(buf, 0) - if err2 != nil { - panic(err2) - } - } - wg.Done() - }() - - fd2, err := os.Open(fn) - if err != nil { - t.Fatal(err) - } - defer fd2.Close() - - go func() { - buf := make([]byte, 10) - var err3 error - for i := 0; i < N; i++ { - _, err3 = fd2.ReadAt(buf, 0) - if err3 == io.EOF { - continue - } - if err3 != nil { - panic(err3) - } - } - wg.Done() - }() - - wg.Wait() -} - -func TestWrite0200File(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/TestWrite0200File" - err := ioutil.WriteFile(fn, nil, 0200) - if err != nil { - t.Fatalf("creating empty file failed: %v", err) - } - fd, err := os.OpenFile(fn, os.O_WRONLY, 0) - if err != nil { - t.Fatal(err) - } - fi, err := fd.Stat() - if err != nil { - t.Fatal(err) - } - perms := fi.Mode().Perm() - if perms != 0200 { - t.Fatal("wrong initial permissions") - } - defer fd.Close() - _, err = fd.Write(make([]byte, 10)) - if err != nil { - t.Fatal(err) - } - perms = fi.Mode().Perm() - if perms != 0200 { - t.Fatal("wrong restored permissions") - } -} - -// TestMvWarnings: -// When xattr support was introduced, mv threw warnings like these: -// mv: preserving permissions for ‘b/x’: Operation not permitted -// because we returned EPERM when it tried to set system.posix_acl_access. -// Now we return EOPNOTSUPP and mv is happy. -func TestMvWarnings(t *testing.T) { - fn := test_helpers.TmpDir + "/TestMvWarnings" - err := ioutil.WriteFile(fn, nil, 0600) - if err != nil { - t.Fatalf("creating file failed: %v", err) - } - cmd := exec.Command("mv", fn, test_helpers.DefaultPlainDir) - out, err := cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - t.Fatal(err) - } - if len(out) != 0 { - t.Fatalf("Got warnings from mv:\n%s", string(out)) - } -} - -// Check for this bug in symlink handling: -// $ ln -s /asd/asdasd/asdasd b/foo -// $ mv b/foo . -// mv: listing attributes of 'b/foo': No such file or directory -// strace shows: -// llistxattr("b/foo", NULL, 0) = -1 ENOENT (No such file or directory) -func TestMvWarningSymlink(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/TestMvWarningSymlink" - err := os.Symlink("/foo/bar/baz", fn) - if err != nil { - t.Fatal(err) - } - cmd := exec.Command("mv", fn, test_helpers.TmpDir) - out, err := cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - if runtime.GOOS == "darwin" { - t.Skip("mv on darwin chokes on broken symlinks, see https://github.com/rfjakob/gocryptfs/issues/349") - } - t.Fatal(err) - } - if len(out) != 0 { - t.Log(strings.TrimSpace(string(out))) - t.Fatal("Got warnings") - } -} - -// See TestMvWarnings. -func TestCpWarnings(t *testing.T) { - fn := test_helpers.TmpDir + "/TestCpWarnings" - err := ioutil.WriteFile(fn, []byte("foo"), 0600) - if err != nil { - t.Fatalf("creating file failed: %v", err) - } - cmd := exec.Command("cp", "-a", fn, test_helpers.DefaultPlainDir) - out, err := cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - t.Fatal(err) - } - if len(out) != 0 { - t.Fatalf("Got warnings from cp -a:\n%s", string(out)) - } -} - -// TestSeekData tests that fs.FileLseeker is implemented -func TestSeekData(t *testing.T) { - fn := filepath.Join(test_helpers.DefaultPlainDir, t.Name()) - f, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - var oneTiB int64 = 1024 * 1024 * 1024 * 1024 - if _, err = f.Seek(oneTiB, 0); err != nil { - t.Fatal(err) - } - if _, err = f.Write([]byte("foo")); err != nil { - t.Fatal(err) - } - f.Close() - - const SEEK_DATA = 3 - - f, err = os.Open(fn) - if err != nil { - t.Fatal(err) - } - off, err := f.Seek(1024*1024, SEEK_DATA) - if err != nil { - t.Fatal(err) - } - if off < oneTiB-1024*1024 { - t.Errorf("off=%d, expected=%d\n", off, oneTiB) - } - f.Close() -} - -/* -TestMd5sumMaintainers tries to repro this interesting -bug that was seen during gocryptfs v2.0 development: - -$ md5sum linux-3.0/MAINTAINERS linux-3.0/MAINTAINERS linux-3.0/MAINTAINERS linux-3.0/MAINTAINERS -279b6ab0491e7532132e8f32afe6c04d linux-3.0/MAINTAINERS <-- WRONG!!!! -99cc9f0dfd86e63231b94edd43a43e02 linux-3.0/MAINTAINERS <-- correct -99cc9f0dfd86e63231b94edd43a43e02 linux-3.0/MAINTAINERS -99cc9f0dfd86e63231b94edd43a43e02 linux-3.0/MAINTAINERS - -strace shows: - -Bad ---- -fstat(3, {st_mode=S_IFREG|0644, st_size=196745, ...}) = 0 -read(3, "\n\tList of maintainers and how to"..., 32768) = 32768 -read(3, "M:\tSylwester Nawrocki \nL:\tlinux"..., 32768) = 32768 -read(3, "ach-spear3xx/\n\nSPEAR6XX MACHINE "..., 32768) = 32768 -read(3, "", 32768) = 0 -lseek(3, 0, SEEK_CUR) = 196608 -close(3) = 0 -fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0 -write(1, "279b6ab0491e7532132e8f32afe6c04d"..., 56279b6ab0491e7532132e8f32afe6c04d linux-3.0/MAINTAINERS - -Good ----- -fstat(3, {st_mode=S_IFREG|0644, st_size=195191, ...}) = 0 -read(3, "\n\tList of maintainers and how to"..., 32768) = 32768 -read(3, "M:\tSylwester Nawrocki \nL:\tlinux"..., 32768) = 32768 -read(3, "ach-spear3xx/\n\nSPEAR6XX MACHINE "..., 32768) = 31351 -read(3, "", 4096) = 0 -lseek(3, 0, SEEK_CUR) = 195191 -close(3) = 0 -fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x2), ...}) = 0 -write(1, "99cc9f0dfd86e63231b94edd43a43e02"..., 5699cc9f0dfd86e63231b94edd43a43e02 linux-3.0/MAINTAINERS -*/ -func TestMd5sumMaintainers(t *testing.T) { - fn := filepath.Join(test_helpers.DefaultPlainDir, t.Name()) - f, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - // Size of the MAINTAINERS file = 195191 - const sizeWant = 195191 - content := make([]byte, sizeWant) - _, err = f.Write(content) - if err != nil { - t.Fatal(err) - } - f.Close() - - // Remount to clear the linux kernel attr cache - // (otherwise we would have to wait 2 seconds for the entry to expire) - test_helpers.UnmountPanic(test_helpers.DefaultPlainDir) - test_helpers.MountOrExit(test_helpers.DefaultCipherDir, test_helpers.DefaultPlainDir, "-zerokey") - - cmd := exec.Command("md5sum", fn, fn, fn, fn) - out2, err := cmd.CombinedOutput() - out := string(out2) - - // 195191 zero bytes have this md5sum - const md5Want = "b99bf6917f688068acd49126f3b1b005" - - n := strings.Count(out, md5Want) - if n != 4 { - t.Errorf("found %d instead of %d instances of %q", n, 4, md5Want) - t.Logf("full output:\n%s", out) - } -} diff --git a/tests/defaults/performance_test.go b/tests/defaults/performance_test.go deleted file mode 100644 index a2ecf8c..0000000 --- a/tests/defaults/performance_test.go +++ /dev/null @@ -1,131 +0,0 @@ -// Tests and benchmarks performed with default settings only. -package defaults - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -// Benchmarks -func BenchmarkStreamWrite(t *testing.B) { - buf := make([]byte, 1024*1024) - t.SetBytes(int64(len(buf))) - - file, err := os.Create(test_helpers.DefaultPlainDir + "/BenchmarkWrite") - if err != nil { - t.Fatal(err) - } - - t.ResetTimer() - var i int - for i = 0; i < t.N; i++ { - written, err := file.Write(buf) - if err != nil { - fmt.Printf("err=\"%s\", written=%d\n", err.Error(), written) - t.Fatal(err) - } - } - file.Close() -} - -func BenchmarkStreamRead(t *testing.B) { - buf := make([]byte, 1024*1024) - t.SetBytes(int64(len(buf))) - - fn := test_helpers.DefaultPlainDir + "/BenchmarkWrite" - fi, err := os.Stat(fn) - if err != nil { - t.Fatal(err) - } - mb := int(fi.Size() / 1024 / 1024) - - if t.N > mb { - // Grow file so we can satisfy the test - //fmt.Printf("Growing file to %d MB... ", t.N) - var f2 *os.File - f2, err = os.OpenFile(fn, os.O_WRONLY|os.O_APPEND, 0666) - if err != nil { - fmt.Println(err) - t.FailNow() - } - for h := 0; h < t.N-mb; h++ { - _, err = f2.Write(buf) - if err != nil { - fmt.Println(err) - t.FailNow() - } - } - f2.Close() - } - - file, err := os.Open(fn) - if err != nil { - t.FailNow() - } - t.ResetTimer() - var i int - for i = 0; i < t.N; i++ { - _, err := file.Read(buf) - if err == io.EOF { - fmt.Println("Test file too small") - t.SkipNow() - } else if err != nil { - fmt.Println(err) - t.FailNow() - } - } - file.Close() -} - -// createFiles - create "count" files of size "size" bytes each -func createFiles(t *testing.B, count int, size int) { - dir := fmt.Sprintf("%s/createFiles_%d_%d", test_helpers.DefaultPlainDir, count, size) - err := os.Mkdir(dir, 0777) - if err != nil { - t.Fatal(err) - } - buf := make([]byte, size) - t.SetBytes(int64(len(buf))) - t.ResetTimer() - var i int - for i = 0; i < count; i++ { - file := fmt.Sprintf("%s/%d", dir, i) - if size > 0 { - err = ioutil.WriteFile(file, buf, 0666) - } else { - var fh *os.File - fh, err = os.Create(file) - fh.Close() - } - if err != nil { - t.Fatal(err) - } - } - t.StopTimer() - os.RemoveAll(dir) -} - -func BenchmarkCreate0B(t *testing.B) { - createFiles(t, t.N, 0) -} - -func BenchmarkCreate1B(t *testing.B) { - createFiles(t, t.N, 1) -} - -func BenchmarkCreate100B(t *testing.B) { - createFiles(t, t.N, 100) -} - -func BenchmarkCreate4kB(t *testing.B) { - createFiles(t, t.N, 4*1024) -} - -func BenchmarkCreate10kB(t *testing.B) { - createFiles(t, t.N, 10*1024) -} diff --git a/tests/dl-linux-tarball.bash b/tests/dl-linux-tarball.bash deleted file mode 100755 index fa27e37..0000000 --- a/tests/dl-linux-tarball.bash +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -eu -# -# This script checks the size of /tmp/linux-3.0.tar.gz and downloads -# a fresh copy if the size is incorrect or the file is missing. - -URL=https://cdn.kernel.org/pub/linux/kernel/v3.0/linux-3.0.tar.gz -TGZ=/tmp/linux-3.0.tar.gz - -SIZE_WANT=96675825 -SIZE_ACTUAL=0 -if [[ -e $TGZ ]]; then - if [[ $OSTYPE == linux* ]] ; then - SIZE_ACTUAL=$(stat -c %s $TGZ) - else - # Mac OS X - SIZE_ACTUAL=$(stat -f %z $TGZ) - fi -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 - else - curl -o $TGZ $URL - fi -fi diff --git a/tests/example_filesystems/content/abs b/tests/example_filesystems/content/abs deleted file mode 120000 index e1740fa..0000000 --- a/tests/example_filesystems/content/abs +++ /dev/null @@ -1 +0,0 @@ -/a/b/c/d \ No newline at end of file diff --git a/tests/example_filesystems/content/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/example_filesystems/content/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/content/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/content/rel b/tests/example_filesystems/content/rel deleted file mode 120000 index 8279c75..0000000 --- a/tests/example_filesystems/content/rel +++ /dev/null @@ -1 +0,0 @@ -status.txt \ No newline at end of file diff --git a/tests/example_filesystems/content/status.txt b/tests/example_filesystems/content/status.txt deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/content/status.txt +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/example_filesystems_test.go b/tests/example_filesystems/example_filesystems_test.go deleted file mode 100644 index 36fb554..0000000 --- a/tests/example_filesystems/example_filesystems_test.go +++ /dev/null @@ -1,384 +0,0 @@ -package example_filesystems - -// Mount example filesystems, check that the example content (normal file, symlinks) -// is there and test mkdir and rmdir -// -// Runs all the tests twice, once with "-openssl=false" and once with -// "-openssl=true". - -import ( - "flag" - "fmt" - "os" - "os/exec" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -var opensslOpt string - -// tmpFsPath contains a private writeable copy of the example_filesystems -// folder. -var tmpFsPath string - -func TestMain(m *testing.M) { - // Make "testing.Verbose()" return the correct value - flag.Parse() - variants := []string{"-openssl=false"} - if !stupidgcm.BuiltWithoutOpenssl { - variants = append(variants, "-openssl=true") - } else { - fmt.Println("Skipping OpenSSL tests, I have been compiled without openssl support") - } - for _, opensslOpt = range variants { - if testing.Verbose() { - fmt.Printf("example_filesystems: testing with %q\n", opensslOpt) - } - test_helpers.ResetTmpDir(false) - - // Create a private copy of the example filesystems that we can - // mess with - cmd := exec.Command("cp", "-a", "../example_filesystems", test_helpers.TmpDir) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - err := cmd.Run() - if err != nil { - fmt.Printf("cp -a failed: %v\n", err) - os.Exit(1) - } - tmpFsPath = test_helpers.TmpDir + "/example_filesystems/" - - r := m.Run() - if r != 0 { - os.Exit(r) - } - } - os.Exit(0) -} - -// This filesystem is not supported anymore. -func TestExampleFSv04(t *testing.T) { - cDir := "v0.4" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo test", opensslOpt) - if err == nil { - t.Errorf("Mounting too old FS should fail") - } -} - -// This filesystem is not supported anymore. -func TestExampleFSv05(t *testing.T) { - cDir := "v0.5" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo test", opensslOpt) - if err == nil { - t.Errorf("Mounting too old FS should fail") - } -} - -// This filesystem is not supported anymore. -func TestExampleFSv06(t *testing.T) { - cDir := "v0.6" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo test", opensslOpt) - if err == nil { - t.Errorf("Mounting too old FS should fail") - } -} - -// This filesystem is not supported anymore. -func TestExampleFSv06PlaintextNames(t *testing.T) { - cDir := "v0.6-plaintextnames" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := test_helpers.Mount(cDir, pDir, false, "-extpass", "echo test", opensslOpt) - if err == nil { - t.Errorf("Mounting too old FS should fail") - } -} - -// Test example_filesystems/v0.7 -// with password mount and -masterkey mount -// v0.7 adds 128 bit GCM IVs -func TestExampleFSv07(t *testing.T) { - cDir := "v0.7" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := os.Mkdir(pDir, 0777) - if err != nil { - t.Fatal(err) - } - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", opensslOpt) - checkExampleFS(t, pDir, true) - test_helpers.UnmountPanic(pDir) - test_helpers.MountOrFatal(t, cDir, pDir, "-masterkey", - "ed7f6d83-40cce86c-0e7d79c2-a9438710-575221bf-30a0eb60-2821fa8f-7f3123bf", - "-raw64=false", "-hkdf=false", opensslOpt) - checkExampleFS(t, pDir, true) - test_helpers.UnmountPanic(pDir) -} - -// gocryptfs v0.7 filesystem created with "-plaintextnames" -func TestExampleFSv07PlaintextNames(t *testing.T) { - cDir := "v0.7-plaintextnames" - pDir := test_helpers.TmpDir + "/" + cDir + ".mnt" - cDir = tmpFsPath + cDir - - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", opensslOpt) - checkExampleFS(t, pDir, true) - test_helpers.UnmountPanic(pDir) - // The actual unmount takes some time, this causes weird problems. Just don't - // reuse the mountpoint. - pDir = pDir + ".2" - test_helpers.MountOrFatal(t, cDir, pDir, "-plaintextnames", "-masterkey", - "6d96397b-585631e1-c7cba69d-61e738b6-4d5ad2c2-e21f0fb3-52f60d3a-b08526f7", - "-raw64=false", "-hkdf=false", opensslOpt) - checkExampleFS(t, pDir, true) - test_helpers.UnmountPanic(pDir) -} - -// Test example_filesystems/v0.9 -// (gocryptfs v0.9 introduced long file name support) -func TestExampleFSv09(t *testing.T) { - cDir := "v0.9" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := os.Mkdir(pDir, 0777) - if err != nil { - t.Fatal(err) - } - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", opensslOpt) - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) - pDir = pDir + ".2" - test_helpers.MountOrFatal(t, cDir, pDir, "-masterkey", - "1cafe3f4-bc316466-2214c47c-ecd89bf3-4e078fe4-f5faeea7-8b7cab02-884f5e1c", - "-raw64=false", "-hkdf=false", opensslOpt) - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) -} - -// gocryptfs v1.1 introduced AES-SIV -func TestExampleFSv11(t *testing.T) { - cDir := "v1.1-aessiv" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := os.Mkdir(pDir, 0777) - if err != nil { - t.Fatal(err) - } - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", opensslOpt) - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) - pDir = pDir + ".2" - test_helpers.MountOrFatal(t, cDir, pDir, "-masterkey", - "eaf371c3-f9a55336-8819f22b-7bccd7c2-a738cf61-7261c658-14c28a03-9428992b", - "-aessiv", "-raw64=false", "-hkdf=false", opensslOpt) - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) -} - -// gocryptfs v1.1 introduced reverse mode -func TestExampleFSv11reverse(t *testing.T) { - dirA := "v1.1-reverse" - dirB := test_helpers.TmpDir + "/" + dirA + ".B" - err := os.Mkdir(dirB, 0700) - if err != nil { - t.Fatal(err) - } - dirC := test_helpers.TmpDir + "/" + dirA + ".C" - err = os.Mkdir(dirC, 0700) - if err != nil { - t.Fatal(err) - } - dirA = tmpFsPath + dirA - test_helpers.MountOrFatal(t, dirA, dirB, "-reverse", "-extpass", "echo test", opensslOpt) - c := dirB + "/gocryptfs.conf" - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - test_helpers.MountOrFatal(t, dirB, dirC, "-extpass", "echo test", opensslOpt) - checkExampleFSrw(t, dirC, false) - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) - - m := "68b51855-042abd80-635ae1ba-90152a78-2ec2d243-832ac72a-eab0561a-f2d37913" - test_helpers.MountOrFatal(t, dirA, dirB, "-reverse", "-masterkey", m, - "-raw64=false", "-hkdf=false", opensslOpt) - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - test_helpers.MountOrFatal(t, dirB, dirC, "-aessiv", "-masterkey", m, - "-raw64=false", "-hkdf=false", opensslOpt) - checkExampleFSrw(t, dirC, false) - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) -} - -// gocryptfs v1.1 introduced reverse mode -func TestExampleFSv11reversePlaintextnames(t *testing.T) { - dirA := "v1.1-reverse-plaintextnames" - dirB := test_helpers.TmpDir + "/" + dirA + ".B" - err := os.Mkdir(dirB, 0700) - if err != nil { - t.Fatal(err) - } - dirC := test_helpers.TmpDir + "/" + dirA + ".C" - err = os.Mkdir(dirC, 0700) - if err != nil { - t.Fatal(err) - } - dirA = tmpFsPath + dirA - test_helpers.MountOrFatal(t, dirA, dirB, "-reverse", "-extpass", "echo test", opensslOpt) - c := dirB + "/gocryptfs.conf" - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - test_helpers.MountOrFatal(t, dirB, dirC, "-extpass", "echo test", opensslOpt) - checkExampleFSrw(t, dirC, false) - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) - - m := "e7fb8f0d-2a81df9e-26611e4b-5540b218-e48aa458-c2a623af-d0c82637-1466b5f2" - test_helpers.MountOrFatal(t, dirA, dirB, "-reverse", "-masterkey", m, - "-raw64=false", "-hkdf=false", opensslOpt) - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - test_helpers.MountOrFatal(t, dirB, dirC, "-aessiv", "-masterkey", m, - "-raw64=false", "-hkdf=false", opensslOpt) - checkExampleFSrw(t, dirC, false) - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) -} - -// gocryptfs v1.3 introduced HKDF -func TestExampleFSv13(t *testing.T) { - cDir := "v1.3" - pDir := test_helpers.TmpDir + "/" + cDir - cDir = tmpFsPath + cDir - err := os.Mkdir(pDir, 0777) - if err != nil { - t.Fatal(err) - } - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", opensslOpt) - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) - - pDir = pDir + "_m" - test_helpers.MountOrFatal(t, cDir, pDir, "-masterkey", - "fd890dab-86bf61cf-ec5ad460-ad3ed01f-9c52d546-2a31783d-a56b088d-3d05232e", - opensslOpt) - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) -} - -// Check that the masterkey=stdin cli option works. -func TestExampleFSv13MasterkeyStdin(t *testing.T) { - cDir := "v1.3" - pDir := test_helpers.TmpDir + "/TestExampleFSv13MasterkeyStdin.mnt" - cDir = tmpFsPath + cDir - err := os.Mkdir(pDir, 0777) - if err != nil { - t.Fatal(err) - } - args := []string{"-q", "-masterkey=stdin", opensslOpt, cDir, pDir} - cmd := exec.Command(test_helpers.GocryptfsBinary, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - p, err := cmd.StdinPipe() - if err != nil { - t.Fatal(err) - } - err = cmd.Start() - if err != nil { - t.Error(err) - } - // Write masterkey to stdin - p.Write([]byte("fd890dab-86bf61cf-ec5ad460-ad3ed01f-9c52d546-2a31783d-a56b088d-3d05232e")) - p.Close() - err = cmd.Wait() - if err != nil { - t.Error(err) - } - // Check that the fs decrypts ok & unmount - checkExampleFSLongnames(t, pDir) - test_helpers.UnmountPanic(pDir) -} - -// gocryptfs v1.3 introduced HKDF. -// We check the md5 sum of the encrypted version of a file to make sure we don't -// accidentally change the ciphertext generation. -// Create a full crypto round-trip by mounting two times: -// dirA -> reverse mount -> dirB -> forward mount -> dirC -func TestExampleFSv13reverse(t *testing.T) { - var R_OK uint32 = 4 - // Prepare directories - dirA := "v1.3-reverse" - dirB := test_helpers.TmpDir + "/" + dirA + ".B" - err := os.Mkdir(dirB, 0700) - if err != nil { - t.Fatal(err) - } - dirC := test_helpers.TmpDir + "/" + dirA + ".C" - err = os.Mkdir(dirC, 0700) - if err != nil { - t.Fatal(err) - } - dirA = tmpFsPath + dirA - // Mount using password - // We pass "-wpanic=false" because the '..' and '.' tests deliverately trigger warnings - test_helpers.MountOrFatal(t, dirA, dirB, "-reverse", "-extpass", "echo test", "-wpanic=false", opensslOpt) - c := dirB + "/gocryptfs.conf" - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - test_helpers.MountOrFatal(t, dirB, dirC, "-extpass", "echo test", opensslOpt) - // Test - checkExampleFSrw(t, dirC, false) - // Access to encrypted version of '..' should fail - cPath := dirB + "/D8VwRPqWW8x7M5OEoMs0Eg" - err = syscall.Access(cPath, R_OK) - if err != syscall.ENOENT { - t.Errorf("want ENOENT, got: %v", err) - } - // Access to encrypted version of '.' should fail - cPath = dirB + "/kkmARPseVj4XQFW-EL42-w" - err = syscall.Access(cPath, R_OK) - if err != syscall.ENOENT { - t.Errorf("want ENOENT, got: %v", err) - } - // Encrypted version of dir1/dir2/file (10000 zero bytes) - cPath = dirB + "/zOsW1-BUX54hC2hmhu2EOw/4ZqrpGQdw5r07KR1qw2ZeQ/tfCm9Sp9J_Dvc-jD7J6p8g" - want := "9818501d214c5eb42ca2472caf6c82a1" - actual := test_helpers.Md5fn(cPath) - if actual != want { - t.Errorf("wrong md5") - } - // Unmount - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) - - // Mount using masterkey - m := "2290a7f4-3e1908fb-b006f7d9-261bdaf1-4b72bc38-3b24956c-db7d8a8d-d996076a" - test_helpers.MountOrFatal(t, dirA, dirB, "-reverse", "-masterkey", m, opensslOpt) - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - test_helpers.MountOrFatal(t, dirB, dirC, "-aessiv", "-masterkey", m, opensslOpt) - // Test - checkExampleFSrw(t, dirC, false) - actual = test_helpers.Md5fn(cPath) - if actual != want { - t.Errorf("wrong md5") - } - // Unmmount - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) -} diff --git a/tests/example_filesystems/example_test_helpers.go b/tests/example_filesystems/example_test_helpers.go deleted file mode 100644 index e39f8d6..0000000 --- a/tests/example_filesystems/example_test_helpers.go +++ /dev/null @@ -1,80 +0,0 @@ -package example_filesystems - -import ( - "io/ioutil" - "os" - "path/filepath" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -const statusTxtContent = "It works!\n" - -// checkExampleFS - verify that "dir" contains the expected test files -func checkExampleFS(t *testing.T, dir string, rw bool) { - // Read regular file - statusFile := filepath.Join(dir, "status.txt") - contentBytes, err := ioutil.ReadFile(statusFile) - if err != nil { - t.Error(err) - return - } - content := string(contentBytes) - if content != statusTxtContent { - t.Errorf("Unexpected content: %s\n", content) - } - // Read relative symlink - symlink := filepath.Join(dir, "rel") - target, err := os.Readlink(symlink) - if err != nil { - t.Error(err) - return - } - if target != "status.txt" { - t.Errorf("Unexpected link target: %s\n", target) - } - // Read absolute symlink - symlink = filepath.Join(dir, "abs") - target, err = os.Readlink(symlink) - if err != nil { - t.Error(err) - return - } - if target != "/a/b/c/d" { - t.Errorf("Unexpected link target: %s\n", target) - } - if rw { - // Test directory operations - test_helpers.TestRename(t, dir) - test_helpers.TestMkdirRmdir(t, dir) - } -} - -// checkExampleFSLongnames - verify that "dir" contains the expected test files -// plus the long file name test file. -// Also tests simple directory operations. -func checkExampleFSLongnames(t *testing.T, dir string) { - checkExampleFSrw(t, dir, true) -} - -// checkExampleFSrw is like checkExampleFSLongnames but gives the caller the -// choice if he wants to run tests that write to the FS. -func checkExampleFSrw(t *testing.T, dir string, rw bool) { - // regular tests - checkExampleFS(t, dir, rw) - // long name test file - longname := "longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + - "xxxxxxxxxxxxxxxxxxxxxxxx" - contentBytes, err := ioutil.ReadFile(filepath.Join(dir, longname)) - if err != nil { - t.Error(err) - return - } - content := string(contentBytes) - if content != statusTxtContent { - t.Errorf("longname_255: unexpected content: %s\n", content) - } -} diff --git a/tests/example_filesystems/v0.4/3-HZSwv99agoWgTErV0YFQ== b/tests/example_filesystems/v0.4/3-HZSwv99agoWgTErV0YFQ== deleted file mode 100644 index 5d0af82344d03983cd62f89a4add808bc76a8734..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56 zcmV-80LT9T0!C-5ja%@o!{I4DBZ9~ZiA%1dZ0;023(iz<@=jt!^s3;e$=(zrWI!A~ ObvBXn`=A9>^9_35+Zs~< diff --git a/tests/example_filesystems/v0.4/6hL2fPVB2aMSh4-UoDn5Kw== b/tests/example_filesystems/v0.4/6hL2fPVB2aMSh4-UoDn5Kw== deleted file mode 120000 index 31b9013..0000000 --- a/tests/example_filesystems/v0.4/6hL2fPVB2aMSh4-UoDn5Kw== +++ /dev/null @@ -1 +0,0 @@ -3-HZSwv99agoWgTErV0YFQ== \ No newline at end of file diff --git a/tests/example_filesystems/v0.4/TBIgdfhDKwkXVTnWLVzFSg== b/tests/example_filesystems/v0.4/TBIgdfhDKwkXVTnWLVzFSg== deleted file mode 120000 index 7a15694..0000000 --- a/tests/example_filesystems/v0.4/TBIgdfhDKwkXVTnWLVzFSg== +++ /dev/null @@ -1 +0,0 @@ -/tTXhw8tmmz4PK9YG21Whug==/Qe8z0HUArb5bZJjUqEo2Nw==/wv68UB9DLF9OfAcxgRKKtQ==/9No5n3deBUGa-BsvPRi3DQ== \ No newline at end of file diff --git a/tests/example_filesystems/v0.4/gocryptfs.conf b/tests/example_filesystems/v0.4/gocryptfs.conf deleted file mode 100644 index 354b4bb..0000000 --- a/tests/example_filesystems/v0.4/gocryptfs.conf +++ /dev/null @@ -1,12 +0,0 @@ -{ - "EncryptedKey": "He757VFOKOWbMJqJ7HBs67SMSi3Vu8/2vgWNI6j1tVo4JBlNvrQSw6KkCh0lGrHrh6ICbPv4MyoyFdGa", - "ScryptObject": { - "Salt": "MeHSsxsnJwngAwptNzuXQlj7JtF1b0uzZuWvVV3cH3w=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": null -} \ No newline at end of file diff --git a/tests/example_filesystems/v0.5/AOtl_i4xQWDyG0_zUqntOw== b/tests/example_filesystems/v0.5/AOtl_i4xQWDyG0_zUqntOw== deleted file mode 120000 index 7c52a28..0000000 --- a/tests/example_filesystems/v0.5/AOtl_i4xQWDyG0_zUqntOw== +++ /dev/null @@ -1 +0,0 @@ -LFInXW9Djd1p8VfnwbhBaQy7MowhfNUDhsPPXXEiAfrfaVar6Ec= \ No newline at end of file diff --git a/tests/example_filesystems/v0.5/Pf35wlWlf43N68EbhIgTcQ== b/tests/example_filesystems/v0.5/Pf35wlWlf43N68EbhIgTcQ== deleted file mode 120000 index 596cc3d..0000000 --- a/tests/example_filesystems/v0.5/Pf35wlWlf43N68EbhIgTcQ== +++ /dev/null @@ -1 +0,0 @@ -OrWiZIVoBo4qbbJdsPy1MCwlvGTE4t_ackP4lbcLdKiTA-Zf \ No newline at end of file diff --git a/tests/example_filesystems/v0.5/gocryptfs.conf b/tests/example_filesystems/v0.5/gocryptfs.conf deleted file mode 100644 index f839664..0000000 --- a/tests/example_filesystems/v0.5/gocryptfs.conf +++ /dev/null @@ -1,14 +0,0 @@ -{ - "EncryptedKey": "zIY8foMncKrAG8USA1/AQ+R8z+xRge3QZqDpGRIDAeNOkMeQQsnMsJzFJbZHcGlRbUye0CwRghR/UX4R", - "ScryptObject": { - "Salt": "BFQklFzt3j9ZDa8zcR9pwHfa8nDdLqyCzNp5kA+V6Y0=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "DirIV" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v0.5/gocryptfs.diriv b/tests/example_filesystems/v0.5/gocryptfs.diriv deleted file mode 100644 index 3670294..0000000 --- a/tests/example_filesystems/v0.5/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -VLlgk# \ No newline at end of file diff --git a/tests/example_filesystems/v0.5/j2BpGUT5kOtia20PWQ2rEA== b/tests/example_filesystems/v0.5/j2BpGUT5kOtia20PWQ2rEA== deleted file mode 100644 index 1ad235896542acb4d484ed0061f640fa7b6bb10e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56 zcmV-80LT9T0tp2DV2eHDTK_kR=kp{9qb1hG%zsxricD!XBCeKV7T4KDs>Yn*ftdYJ}D7utxxoE^0 OdOvFE8+P{5(P3iglpIF@ diff --git a/tests/example_filesystems/v0.6/9pOs0yjJI5A67pv5CnqomQ== b/tests/example_filesystems/v0.6/9pOs0yjJI5A67pv5CnqomQ== deleted file mode 120000 index 11b0234..0000000 --- a/tests/example_filesystems/v0.6/9pOs0yjJI5A67pv5CnqomQ== +++ /dev/null @@ -1 +0,0 @@ -OoEsnmmWQzBSl3E471yZkI2t2vB4SteL_l1J60HYXZ7g0W3CkTM= \ No newline at end of file diff --git a/tests/example_filesystems/v0.6/G79Zdu41H3bgwdaQlrz-dg== b/tests/example_filesystems/v0.6/G79Zdu41H3bgwdaQlrz-dg== deleted file mode 120000 index b72a393..0000000 --- a/tests/example_filesystems/v0.6/G79Zdu41H3bgwdaQlrz-dg== +++ /dev/null @@ -1 +0,0 @@ -4IGGj21t4IYWI76F46v3gG-JwTcw_QxGDFMSk_19bJav2WNw \ No newline at end of file diff --git a/tests/example_filesystems/v0.6/RuYvQG_raW_-H_LcyJC4LQ== b/tests/example_filesystems/v0.6/RuYvQG_raW_-H_LcyJC4LQ== deleted file mode 100644 index 5821d876056177c789114f611c1f0e6090f267a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56 zcmV-80LT9T0@8HX5VCVUrCQ_KN6DfmoyrGBuNp7imYiJtS`Shq>={T6!r?iehqYxz O`(v9MxyDH-q81D0t{IvD diff --git a/tests/example_filesystems/v0.6/gocryptfs.conf b/tests/example_filesystems/v0.6/gocryptfs.conf deleted file mode 100644 index 1c72781..0000000 --- a/tests/example_filesystems/v0.6/gocryptfs.conf +++ /dev/null @@ -1,15 +0,0 @@ -{ - "EncryptedKey": "/PhLwDblkFRGfoIA0egXikG0ZSZTWrOOoFZJPPX0R8JgU5+XnT2M2rxUzHIKKeuGoqZN55phgJjhTu0J", - "ScryptObject": { - "Salt": "YSHRXpcWYp95npMxAy9cf27LoaPR3gvrFpk3Xhg2tM8=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "DirIV", - "EMENames" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v0.6/gocryptfs.diriv b/tests/example_filesystems/v0.6/gocryptfs.diriv deleted file mode 100644 index ec9f503..0000000 --- a/tests/example_filesystems/v0.6/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -('<7Q \ No newline at end of file diff --git a/tests/example_filesystems/v0.7-plaintextnames/abs b/tests/example_filesystems/v0.7-plaintextnames/abs deleted file mode 120000 index e1740fa..0000000 --- a/tests/example_filesystems/v0.7-plaintextnames/abs +++ /dev/null @@ -1 +0,0 @@ -/a/b/c/d \ No newline at end of file diff --git a/tests/example_filesystems/v0.7-plaintextnames/gocryptfs.conf b/tests/example_filesystems/v0.7-plaintextnames/gocryptfs.conf deleted file mode 100644 index 9b462a3..0000000 --- a/tests/example_filesystems/v0.7-plaintextnames/gocryptfs.conf +++ /dev/null @@ -1,15 +0,0 @@ -{ - "EncryptedKey": "13f9NLdlS20w26T0bukhVrqhumJOHhRyntEJb2y2BJK+K1kulklQGT6gxSWPsjDqw5514h9/euMiKMwc", - "ScryptObject": { - "Salt": "b2ZD+7sN6b/lchJbYT+4K73tscC6WwbGrdxHuFjhOT4=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "PlaintextNames" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v0.7-plaintextnames/rel b/tests/example_filesystems/v0.7-plaintextnames/rel deleted file mode 120000 index 8279c75..0000000 --- a/tests/example_filesystems/v0.7-plaintextnames/rel +++ /dev/null @@ -1 +0,0 @@ -status.txt \ No newline at end of file diff --git a/tests/example_filesystems/v0.7-plaintextnames/status.txt b/tests/example_filesystems/v0.7-plaintextnames/status.txt deleted file mode 100644 index 86ae6dc183be4ac68ddb88a48f69a47424dfa846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0vXfI@(6SUoVQ4WV|4D;bqo~%A<7^egjc0tRR7}%dgd#Me SN}j?bFxvUkUlt^dELHtF+8jmz diff --git a/tests/example_filesystems/v0.7/dwPcZNei4HN4qPA6FxoG_A== b/tests/example_filesystems/v0.7/dwPcZNei4HN4qPA6FxoG_A== deleted file mode 120000 index c0c731f..0000000 --- a/tests/example_filesystems/v0.7/dwPcZNei4HN4qPA6FxoG_A== +++ /dev/null @@ -1 +0,0 @@ -Ygqk_pYyxE_ac_ufVgxKCPxWHMGqQ8xUohIaHcmgeLU4uQB3_UmicPKB \ No newline at end of file diff --git a/tests/example_filesystems/v0.7/gocryptfs.conf b/tests/example_filesystems/v0.7/gocryptfs.conf deleted file mode 100644 index 80c2673..0000000 --- a/tests/example_filesystems/v0.7/gocryptfs.conf +++ /dev/null @@ -1,16 +0,0 @@ -{ - "EncryptedKey": "0crm+qEf00XPxQrc8NIMp/0rgfaLb8wzTj+3G1slSytjsLHctj/fOKkGJIFyBk7xzvnWdkhyxxvHgfMS", - "ScryptObject": { - "Salt": "yZn+QMjR2ENZ6MoiURpqEqr8mgnCX8WN87KJafgiXhU=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v0.7/gocryptfs.diriv b/tests/example_filesystems/v0.7/gocryptfs.diriv deleted file mode 100644 index 3adef3c..0000000 --- a/tests/example_filesystems/v0.7/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -iȤ\?=Mc \ No newline at end of file diff --git a/tests/example_filesystems/v0.9/00msNUi5h5aKMX_f-4pBhA== b/tests/example_filesystems/v0.9/00msNUi5h5aKMX_f-4pBhA== deleted file mode 120000 index c717734..0000000 --- a/tests/example_filesystems/v0.9/00msNUi5h5aKMX_f-4pBhA== +++ /dev/null @@ -1 +0,0 @@ -5nI119EbCRtgT8AwTDPmxCuORvbGV4xdtmqnur7KK9ufir-ALyneV7Iy \ No newline at end of file diff --git a/tests/example_filesystems/v0.9/R83PhW-BBA_q4rPYD7dEMg== b/tests/example_filesystems/v0.9/R83PhW-BBA_q4rPYD7dEMg== deleted file mode 100644 index 1bc4a8175da0927760c050785a52988bed864caa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0^57j+YfMsxgZL6sK#EUyJ#NPWRA@2;aOXaB8RYDM@oH&Wa1I_DmGl} SZ=gyBZUhP(*&qn1s%l3HC0aYGUQU{9c7I`03A*x#}4a!H-(=&$B5q- S)eU(9n;OJ_uBkM=V%1>6R2d}z diff --git a/tests/example_filesystems/v0.9/gocryptfs.longname.y4J-w3LBX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ=.name b/tests/example_filesystems/v0.9/gocryptfs.longname.y4J-w3LBX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ=.name deleted file mode 100644 index 535cee7..0000000 --- a/tests/example_filesystems/v0.9/gocryptfs.longname.y4J-w3LBX165Mn_pGdDRY7Gb6EgHcTrdWjME3WGu-CQ=.name +++ /dev/null @@ -1 +0,0 @@ -Py7wkyXYf1YXvFvHuizOqva6WWWYEQ-JYv1rOAvWWaARLyGdQdxuW-iBf1Vwd8orZjqkcIGgnxOfuRsarXKmn4-3L2MRIZKPhhx2QRqpPRtFylEABH1KwZ_2ZZnx5cpcO8TIoeVdL6NXnRw-WrT06zTUzVRioNPRLPa0J6c8_eb5QFb4EG5wPpn-XlJSedjAw31MUNvFxKYQku_UwJF0CvvXLCozVnNM_dDNWsgBevzB8VBySbfW7XgYMRPJRlJe3Pjeues9tyGJvAxGJVjfo4nZyWusMF2G4f9w06m3Bxjc7ladgJR6F6pyI4Z65DCIL7G6G2y__agmNcKtFwCS_Q== \ No newline at end of file diff --git a/tests/example_filesystems/v0.9/hwE1RKIXtF8hmQMvEXSTtg== b/tests/example_filesystems/v0.9/hwE1RKIXtF8hmQMvEXSTtg== deleted file mode 120000 index 4190a7e..0000000 --- a/tests/example_filesystems/v0.9/hwE1RKIXtF8hmQMvEXSTtg== +++ /dev/null @@ -1 +0,0 @@ -XRx7Nqxt_zuPNo7h8_j1LLVzqzIZg9qAYGRN9Iuq3XBc11Y7_RoQsg== \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-aessiv/MA0FDdmnXpmPJtS_AcAbqQ== b/tests/example_filesystems/v1.1-aessiv/MA0FDdmnXpmPJtS_AcAbqQ== deleted file mode 100644 index 6767b2a137477987199b7d4a315d493426296144..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0zZk(x0}QRBYo0zY|)URFfsFF>?rA522xTG{=`6acXq7Ny5AQT==nQN S54+bO`=^jGJ&auLUj#&Bt{lDq diff --git a/tests/example_filesystems/v1.1-aessiv/Sjl6QXHm2IjuKwaKgJ5jig== b/tests/example_filesystems/v1.1-aessiv/Sjl6QXHm2IjuKwaKgJ5jig== deleted file mode 120000 index b3d31e3..0000000 --- a/tests/example_filesystems/v1.1-aessiv/Sjl6QXHm2IjuKwaKgJ5jig== +++ /dev/null @@ -1 +0,0 @@ -ozeZ1xXCP-Q904JCvkPk40enJd5zVL6FqBugkS6Y4tfcii_G1DOdDQ== \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-aessiv/gocryptfs.conf b/tests/example_filesystems/v1.1-aessiv/gocryptfs.conf deleted file mode 100644 index d16051a..0000000 --- a/tests/example_filesystems/v1.1-aessiv/gocryptfs.conf +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Creator": "gocryptfs v1.1-beta1-33-gf054353-dirty", - "EncryptedKey": "y2ldEXg3Ui0jwic99bqvvrvGRPRDB7gYzvOBwZxcmWqRgcp3BLMShhIXwx3Pewmst5TivqSrK2r9wUIL", - "ScryptObject": { - "Salt": "oEt1In6W5UD1Pe9CFSz21x5ptTRluU43mmshUtmSwAk=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames", - "AESSIV" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-aessiv/gocryptfs.diriv b/tests/example_filesystems/v1.1-aessiv/gocryptfs.diriv deleted file mode 100644 index 9ca19fd..0000000 --- a/tests/example_filesystems/v1.1-aessiv/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -qy@0kY] \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk= b/tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk= deleted file mode 100644 index a5c444d4bb0da5f3679b9819b308c1791fb52b81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0^`jh(Pk&eSgw7Zg3gv-VQAgHUCY2URK~qETE~1R4GzG8$BNR35H`3| S%wK4?4ZeMK7MCrxM!a={e;pP8 diff --git a/tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk=.name b/tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk=.name deleted file mode 100644 index 1865eda..0000000 --- a/tests/example_filesystems/v1.1-aessiv/gocryptfs.longname.dDhdE3C5egl47Q4C4SuPNGPGkzyxuuHCP1efYMCaGqk=.name +++ /dev/null @@ -1 +0,0 @@ -8S9oaCrWGfWIF0_DTQsRvNARW78Vl5HcEYPZOwh3susDIHIbs8JiAF19oqWQz5HN1gpH2213kWqE4m1H1jVKslRHxSPxkQ5sDyaIm4PBZZg5-5djCYoQDLObvrgQv9HsN_NDb2rV8bmmH9SFArJ2SSKX5JdbMcraGr9Rj1AE89-9jIS0VTfKvpA_UgZEdR6IJ7V8VnD3eNo4KsBzJiL4G5wlpxvDLTy7mm7lxK_erV5gzKVHZGIz9Z-ehF--duIfY3x_h2hDNdocIKQltOEcxryHtcUqzcuFB_XkjW6BOTOYINHvLLA8CkLNSOXxLAqoeeu-8GwTgIDKrKhGnTTzgQ== \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-aessiv/jCGXyJJqu4sdxRLsDQNEtA== b/tests/example_filesystems/v1.1-aessiv/jCGXyJJqu4sdxRLsDQNEtA== deleted file mode 120000 index 20d2b58..0000000 --- a/tests/example_filesystems/v1.1-aessiv/jCGXyJJqu4sdxRLsDQNEtA== +++ /dev/null @@ -1 +0,0 @@ -GMqHOo4BNUhfLc-Vqi_R6J76C-OQhHLVgsBl5j9t-XFoq97KNlcuC1Vd \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse-plaintextnames/.gocryptfs.reverse.conf b/tests/example_filesystems/v1.1-reverse-plaintextnames/.gocryptfs.reverse.conf deleted file mode 100644 index e7ea1a8..0000000 --- a/tests/example_filesystems/v1.1-reverse-plaintextnames/.gocryptfs.reverse.conf +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Creator": "gocryptfs v1.1-beta1-33-gf054353-dirty", - "EncryptedKey": "bMqEbjtvZek9yAGzhsTYmaDcqnE7wvR+1fWvy+YCMQTwtmvNbpKnfFH3wacPNKttQ7BcpFrOi4Ux+Bw+", - "ScryptObject": { - "Salt": "RgvSW4AxpA9z/Gb6RCCmKeA4A2vC+l0vu9DnEKIWQZU=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "PlaintextNames", - "AESSIV" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse-plaintextnames/abs b/tests/example_filesystems/v1.1-reverse-plaintextnames/abs deleted file mode 120000 index e1740fa..0000000 --- a/tests/example_filesystems/v1.1-reverse-plaintextnames/abs +++ /dev/null @@ -1 +0,0 @@ -/a/b/c/d \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse-plaintextnames/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/example_filesystems/v1.1-reverse-plaintextnames/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/v1.1-reverse-plaintextnames/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/v1.1-reverse-plaintextnames/rel b/tests/example_filesystems/v1.1-reverse-plaintextnames/rel deleted file mode 120000 index 8279c75..0000000 --- a/tests/example_filesystems/v1.1-reverse-plaintextnames/rel +++ /dev/null @@ -1 +0,0 @@ -status.txt \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse-plaintextnames/status.txt b/tests/example_filesystems/v1.1-reverse-plaintextnames/status.txt deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/v1.1-reverse-plaintextnames/status.txt +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/v1.1-reverse/.gocryptfs.reverse.conf b/tests/example_filesystems/v1.1-reverse/.gocryptfs.reverse.conf deleted file mode 100644 index 9095ef5..0000000 --- a/tests/example_filesystems/v1.1-reverse/.gocryptfs.reverse.conf +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Creator": "gocryptfs v1.1-beta1-33-gf054353-dirty", - "EncryptedKey": "GD7CQzhKMs4r8B/jc7eNNAffyMn4Z2A0tH9EC50y6v5y6amJdU6NQK5xLPWpWSc45si5L26VOVhT8dhz", - "ScryptObject": { - "Salt": "G/Cpk6TnscJzx1fae9t8guejwoPk1lsGkkcljIMjJdw=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames", - "AESSIV" - ] -} \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse/abs b/tests/example_filesystems/v1.1-reverse/abs deleted file mode 120000 index e1740fa..0000000 --- a/tests/example_filesystems/v1.1-reverse/abs +++ /dev/null @@ -1 +0,0 @@ -/a/b/c/d \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/example_filesystems/v1.1-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/v1.1-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/v1.1-reverse/rel b/tests/example_filesystems/v1.1-reverse/rel deleted file mode 120000 index 8279c75..0000000 --- a/tests/example_filesystems/v1.1-reverse/rel +++ /dev/null @@ -1 +0,0 @@ -status.txt \ No newline at end of file diff --git a/tests/example_filesystems/v1.1-reverse/status.txt b/tests/example_filesystems/v1.1-reverse/status.txt deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/v1.1-reverse/status.txt +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/v1.3-reverse/.gocryptfs.reverse.conf b/tests/example_filesystems/v1.3-reverse/.gocryptfs.reverse.conf deleted file mode 100644 index 4241280..0000000 --- a/tests/example_filesystems/v1.3-reverse/.gocryptfs.reverse.conf +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Creator": "gocryptfs v1.3", - "EncryptedKey": "o6yhOpoaToWqs7iZMgx2J+dmrRcL8TOjqd7ntlqy4Y/g/ygNRADJXGITtEIDukf7FbGyn2JNZtWs/4ZOgLNmYw==", - "ScryptObject": { - "Salt": "8a64zpm0vXFg9uretHuwbxijzQmx2QF4hKOYM0yK/S8=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "HKDF", - "DirIV", - "EMENames", - "LongNames", - "Raw64", - "AESSIV" - ] -} diff --git a/tests/example_filesystems/v1.3-reverse/abs b/tests/example_filesystems/v1.3-reverse/abs deleted file mode 120000 index e1740fa..0000000 --- a/tests/example_filesystems/v1.3-reverse/abs +++ /dev/null @@ -1 +0,0 @@ -/a/b/c/d \ No newline at end of file diff --git a/tests/example_filesystems/v1.3-reverse/dir1/dir2/file b/tests/example_filesystems/v1.3-reverse/dir1/dir2/file deleted file mode 100644 index e64c723ad5aeec49f2d1447b9f523fe09c522566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10000 zcmeIu0Sy2E0K%a6Pi+o2h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM O7%*VKfB^#r{s#sp5C8!H diff --git a/tests/example_filesystems/v1.3-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/example_filesystems/v1.3-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/v1.3-reverse/longname_255_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/v1.3-reverse/rel b/tests/example_filesystems/v1.3-reverse/rel deleted file mode 120000 index 8279c75..0000000 --- a/tests/example_filesystems/v1.3-reverse/rel +++ /dev/null @@ -1 +0,0 @@ -status.txt \ No newline at end of file diff --git a/tests/example_filesystems/v1.3-reverse/status.txt b/tests/example_filesystems/v1.3-reverse/status.txt deleted file mode 100644 index 68300b8..0000000 --- a/tests/example_filesystems/v1.3-reverse/status.txt +++ /dev/null @@ -1 +0,0 @@ -It works! diff --git a/tests/example_filesystems/v1.3/gocryptfs.conf b/tests/example_filesystems/v1.3/gocryptfs.conf deleted file mode 100644 index e6b4aec..0000000 --- a/tests/example_filesystems/v1.3/gocryptfs.conf +++ /dev/null @@ -1,20 +0,0 @@ -{ - "Creator": "gocryptfs v1.2.1-23-gd78a8d1-dirty", - "EncryptedKey": "WGhKfCvV/LBZdwpWz3Fs+9UFUJbtTooBiZMuthd8Hz3nq0g+zU7H+68ZkrCLdcC08PUNHVbvDdlGpRh0czbbyA==", - "ScryptObject": { - "Salt": "LkS43fRGN3SfcFbVhPLo3uKh3h+4LYLNlqEUf+JIWf4=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames", - "Raw64", - "HKDF" - ] -} diff --git a/tests/example_filesystems/v1.3/gocryptfs.diriv b/tests/example_filesystems/v1.3/gocryptfs.diriv deleted file mode 100644 index d507f03..0000000 --- a/tests/example_filesystems/v1.3/gocryptfs.diriv +++ /dev/null @@ -1,2 +0,0 @@ -N8mm$;;\ -6 \ No newline at end of file diff --git a/tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs b/tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs deleted file mode 100644 index e5c0a582d4b02dffe5517ae47381baf7b28a5519..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0?#k!scOBN{=!!11vnm2qQ7k;v+!w+WDZ+? Sz)j_98<7 diff --git a/tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs.name b/tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs.name deleted file mode 100644 index c0aceae..0000000 --- a/tests/example_filesystems/v1.3/gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs.name +++ /dev/null @@ -1 +0,0 @@ -K9GWSIcjWZgvd3TpclEcrWUw_QmLRotdyEFxkXLyVDiLZlqC6oJ_LcouD_9ZTkFL72LaB3P0nx7dMeyjd9m1k_aLS1Mqbnj5F7PwiVSVnKugBsooIa7enIuH1x_riP9ppeDEUNPsDXtDlqESe75rc9dYptYmYC_MsG3ve8n78YCU5h8fUBL271v2yv8Tbgc0u7d5ygLqPgTYdBMXsSgOXxviMU73y9UlA3DWfDArb36N0FHpiiphXgu6_uzckiig9o-SFv-o2eyyKjHNClsWAP32opj-T4DuWU7Q_5858hiedJISv8zI0NTYYkeCluSZ_EkIXidzKB5vcswetGSJmg \ No newline at end of file diff --git a/tests/example_filesystems/v1.3/gv65k_g2NQyBsSv_5dzMVQ b/tests/example_filesystems/v1.3/gv65k_g2NQyBsSv_5dzMVQ deleted file mode 120000 index 79876d9..0000000 --- a/tests/example_filesystems/v1.3/gv65k_g2NQyBsSv_5dzMVQ +++ /dev/null @@ -1 +0,0 @@ -ra2NOzuRPOI9tj-Hc7RIYJcZNCJKWY1bo4681ABZi5eEMkK9iZCQ8A \ No newline at end of file diff --git a/tests/example_filesystems/v1.3/mGj2_hdnHe34Sp0iIQUwuw b/tests/example_filesystems/v1.3/mGj2_hdnHe34Sp0iIQUwuw deleted file mode 100644 index d653f786de470d13623c8151cfab55ae99ed514d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0zO?85mY6&shSe#)Fnyt=f9U&@R>y>-mEAAfaQWq%)~ML$$HMOr0j1- SY~on*;3y0s6)iWmCsJ@vT^xG= diff --git a/tests/example_filesystems/v1.3/uD4PVrDBY5y2k_qLKNOFvA b/tests/example_filesystems/v1.3/uD4PVrDBY5y2k_qLKNOFvA deleted file mode 120000 index d121261..0000000 --- a/tests/example_filesystems/v1.3/uD4PVrDBY5y2k_qLKNOFvA +++ /dev/null @@ -1 +0,0 @@ -pAes0cNmAncysGM-LxZ-jXvIpdLwc2qQNXvrEw0l1-dM5X74-kBXgF7P \ No newline at end of file diff --git a/tests/fsck/broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q b/tests/fsck/broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/CMyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A b/tests/fsck/broken_fs_v1.4/CMyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/Ef-68icxbQ-TuvmnWHuItB1BeLB92dNCXMXiz2M-zPI b/tests/fsck/broken_fs_v1.4/Ef-68icxbQ-TuvmnWHuItB1BeLB92dNCXMXiz2M-zPI deleted file mode 100644 index 84b9a705dafc961fdbfff84b5b66cc5eefba41af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18 acmZQzI(BA#cxAcIzvoXy3vCuAh5`UlObEIF diff --git a/tests/fsck/broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw b/tests/fsck/broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw deleted file mode 120000 index 7bbd005..0000000 --- a/tests/fsck/broken_fs_v1.4/GUvJFSfy7S1AXUdy4pDRLw +++ /dev/null @@ -1 +0,0 @@ -RFPnVN8r1HjIrFVJ8PffC7ObzAIeBx3DQh8FbgvmbT8Ho8mU \ No newline at end of file diff --git a/tests/fsck/broken_fs_v1.4/K2m0E6qzIfoLkVZJanoUiQ/mWEr9JLch2FW40qhbnPgpg b/tests/fsck/broken_fs_v1.4/K2m0E6qzIfoLkVZJanoUiQ/mWEr9JLch2FW40qhbnPgpg deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/OtrNpznB8aMTKPi6bopM2g b/tests/fsck/broken_fs_v1.4/OtrNpznB8aMTKPi6bopM2g deleted file mode 100644 index dab7c69a8278bcdf34d0eb92ba0cab70da662c04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63 zcmV-F0KoqM0x1s-oWd$zBlx@uf^kT8zW<#t2B6Gt`I}Q!aBJ7r#K;TBwF66R^!<^n Vf&yBUt!t$&)mZlM{+t>MU@4I09-#mL diff --git a/tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/_y58usbKXq_YRPMKfC3TNw b/tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/_y58usbKXq_YRPMKfC3TNw deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/gocryptfs.diriv b/tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/gocryptfs.diriv deleted file mode 100644 index 178763a..0000000 --- a/tests/fsck/broken_fs_v1.4/PnkpLqHimGudw4C3jFY-Yw/gocryptfs.diriv +++ /dev/null @@ -1,2 +0,0 @@ -f` -{g*@w6 \ No newline at end of file diff --git a/tests/fsck/broken_fs_v1.4/V5DjvW5BXlGl1yCIJn4lPgdjdMvW_LUfc7G-R8W1cZ0 b/tests/fsck/broken_fs_v1.4/V5DjvW5BXlGl1yCIJn4lPgdjdMvW_LUfc7G-R8W1cZ0 deleted file mode 100644 index 6101efa5a935f7523eeb1a480242d27e31f54ae9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8 PcmZQzdUe_RZczyU4$K42 diff --git a/tests/fsck/broken_fs_v1.4/b00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ b/tests/fsck/broken_fs_v1.4/b00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/ejZ3FX0zlFTpSfv-FBJ2u3ojwSN1XSqpNpCHxa5VGWw b/tests/fsck/broken_fs_v1.4/ejZ3FX0zlFTpSfv-FBJ2u3ojwSN1XSqpNpCHxa5VGWw deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1 IcmZPo000310RR91 diff --git a/tests/fsck/broken_fs_v1.4/gocryptfs.conf b/tests/fsck/broken_fs_v1.4/gocryptfs.conf deleted file mode 100644 index cedf571..0000000 --- a/tests/fsck/broken_fs_v1.4/gocryptfs.conf +++ /dev/null @@ -1,20 +0,0 @@ -{ - "Creator": "gocryptfs v1.4.4-13-ga4f3a7d-dirty", - "EncryptedKey": "yfnIx9uKv2ZX80KXOlfb4fWws3RNqvcjsx/Ajr0x4pRfg8NLqhWRpEUWGk8NSdVFXKWVDgdhSoYkbfVnFXl07g==", - "ScryptObject": { - "Salt": "R78m123zJxxO6uU1bg6/0azppry1FoGdH1/Op1xFq+4=", - "N": 65536, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "HKDF", - "DirIV", - "EMENames", - "LongNames", - "Raw64" - ] -} diff --git a/tests/fsck/broken_fs_v1.4/gocryptfs.diriv b/tests/fsck/broken_fs_v1.4/gocryptfs.diriv deleted file mode 100644 index d7bbcda..0000000 --- a/tests/fsck/broken_fs_v1.4/gocryptfs.diriv +++ /dev/null @@ -1,2 +0,0 @@ -8]jM -PgD \ No newline at end of file diff --git a/tests/fsck/broken_fs_v1.4/iI0MtUdzELPeOAZYwYZFee169hpGgd3l2PXQBcc9sl4 b/tests/fsck/broken_fs_v1.4/iI0MtUdzELPeOAZYwYZFee169hpGgd3l2PXQBcc9sl4 deleted file mode 120000 index 4b707cb..0000000 --- a/tests/fsck/broken_fs_v1.4/iI0MtUdzELPeOAZYwYZFee169hpGgd3l2PXQBcc9sl4 +++ /dev/null @@ -1 +0,0 @@ -%%%broken_symlink%%% \ No newline at end of file diff --git a/tests/fsck/broken_fs_v1.4/invalid_file_name.3 b/tests/fsck/broken_fs_v1.4/invalid_file_name.3 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/invalid_file_name_2 b/tests/fsck/broken_fs_v1.4/invalid_file_name_2 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/invalid_file_name____1 b/tests/fsck/broken_fs_v1.4/invalid_file_name____1 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A b/tests/fsck/broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A deleted file mode 100644 index 8621a03..0000000 --- a/tests/fsck/broken_fs_v1.4/qOA8a4yuvgbMFpz7277R8A +++ /dev/null @@ -1 +0,0 @@ -XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/tests/fsck/broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA b/tests/fsck/broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA deleted file mode 120000 index 158f38a..0000000 --- a/tests/fsck/broken_fs_v1.4/s-P7PcQDUcVkoeMDnC3EYA +++ /dev/null @@ -1 +0,0 @@ -Qso5-4WJ2iAxF674mUarvuNbIMTLSJLqfEh3Chq3I_Rm2sY2 \ No newline at end of file diff --git a/tests/fsck/broken_fs_v1.4/trqecbMNXdzLqzpk7fSfKw/gocryptfs.diriv b/tests/fsck/broken_fs_v1.4/trqecbMNXdzLqzpk7fSfKw/gocryptfs.diriv deleted file mode 100644 index 41f0034..0000000 --- a/tests/fsck/broken_fs_v1.4/trqecbMNXdzLqzpk7fSfKw/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -Wc diff --git a/tests/fsck/broken_fs_v1.4/vDKs8a7UtM3PmEKk9wlPcA b/tests/fsck/broken_fs_v1.4/vDKs8a7UtM3PmEKk9wlPcA deleted file mode 100644 index 1dff1a1e46ff9a7f7b02bed545117b3e03249f2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77 zcmV-T0J8r80(W*G#64!|!Qdr7Q3oPQ6jDx)Z=sUlq=6f7r5R?=XBP*KN@Crt^k!<% jvH;+NtXBfYm&k9eJM#Twchsi@QPN}D7FYn8q#>>f8p$Is diff --git a/tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/gocryptfs.diriv b/tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/gocryptfs.diriv deleted file mode 100644 index 29198ce..0000000 --- a/tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -LO/;pl]nfooo diff --git a/tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/uC2yqKyQUXSJF-YF1Ya5nQ b/tests/fsck/broken_fs_v1.4/yrwcjj2qoC4IYvhw9sbfRg/uC2yqKyQUXSJF-YF1Ya5nQ deleted file mode 100644 index e69de29..0000000 diff --git a/tests/fsck/fsck_test.go b/tests/fsck/fsck_test.go deleted file mode 100644 index 3aaba9e..0000000 --- a/tests/fsck/fsck_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package fsck - -import ( - "encoding/base64" - "os" - "os/exec" - "runtime" - "strings" - "syscall" - "testing" - "time" - - "github.com/pkg/xattr" - - "github.com/rfjakob/gocryptfs/internal/exitcodes" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func dec64(in string) (out []byte) { - out, err := base64.RawURLEncoding.DecodeString(in) - if err != nil { - panic(err) - } - return out -} - -func TestBrokenFsV14(t *testing.T) { - // git does not save extended attributes, so we apply them here. - // xattr_good - xattr.Set("broken_fs_v1.4/6nGs4Ugr3EAHd0KzkyLZ-Q", - "user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2Kg", - dec64("hxnZvXSkDicfwVS9w4r1yYkFF61Qou6NaL-VhObYEdu6kuM")) - // xattr_corrupt_name - xattr.Set("broken_fs_v1.4/CMyUifVTjW5fsgXonWBT_RDkvLkdGrLttkZ45T3Oi3A", - "user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2K0", - dec64("QHUMDTgbnl8Sv_A2dFQic_G2vN4_gmDna3651JAhF7OZ-YI")) - // xattr_corrupt_value - xattr.Set("broken_fs_v1.4/b00sbnGXGToadr01GHZaYQn8tjyRhe1OXNBZoQtMlcQ", - "user.gocryptfs.0a5e7yWl0SGUGeWB0Sy2Kg", - dec64("A0hvCePeKpL8bCpijhDKtf7cIijXYQsPnEbNJ84M2ONW0dd")) - - cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", "broken_fs_v1.4") - 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 { - t.Fatal(err) - } - var fsNames []string - entries, err := dirfd.Readdir(0) - if err != nil { - t.Fatal(err) - } - for _, e := range entries { - if !e.IsDir() { - continue - } - if strings.Contains(e.Name(), "reverse") { - continue - } - if e.Name() == "content" { - continue - } - fsNames = append(fsNames, e.Name()) - } - for _, n := range fsNames { - t.Logf("Checking %q", n) - path := "../example_filesystems/" + n - cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", path) - outBin, err := cmd.CombinedOutput() - out := string(outBin) - code := test_helpers.ExtractCmdExitCode(err) - if code == exitcodes.DeprecatedFS { - continue - } - if code != 0 { - t.Log(out) - t.Errorf("fsck returned code %d but fs should be clean", code) - } - } - dirfd.Close() -} - -// TestTerabyteFile verifies that fsck does something intelligent when it hits -// a 1-terabyte sparse file (trying to read the whole file is not intelligent). -func TestTerabyteFile(t *testing.T) { - if runtime.GOOS != "linux" { - t.Skipf("Only linux supports SEEK_DATA") - } - cDir := test_helpers.InitFS(t) - pDir := cDir + ".mnt" - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test") - defer test_helpers.UnmountErr(pDir) - veryBigFile := pDir + "/veryBigFile" - fd, err := os.Create(veryBigFile) - if err != nil { - t.Fatal(err) - } - defer fd.Close() - var oneTiB int64 = 1024 * 1024 * 1024 * 1024 - _, err = fd.WriteAt([]byte("foobar"), oneTiB) - if err != nil { - t.Fatal(err) - } - fi, err := fd.Stat() - if err != nil { - t.Fatal(err) - } - t.Logf("size=%d, running fsck", fi.Size()) - cmd := exec.Command(test_helpers.GocryptfsBinary, "-fsck", "-extpass", "echo test", cDir) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - cmd.Start() - timer := time.AfterFunc(10*time.Second, func() { - t.Error("timeout, sending SIGINT") - syscall.Kill(cmd.Process.Pid, syscall.SIGINT) - }) - cmd.Wait() - timer.Stop() -} diff --git a/tests/fsck/run_fsck.bash b/tests/fsck/run_fsck.bash deleted file mode 100755 index 9637381..0000000 --- a/tests/fsck/run_fsck.bash +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec ../../gocryptfs -fsck -extpass "echo test" broken_fs_v1.4 diff --git a/tests/fuse-unmount.bash b/tests/fuse-unmount.bash deleted file mode 100755 index debae29..0000000 --- a/tests/fuse-unmount.bash +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -eu -# -# Compatibility wrapper around "fusermount" on Linux and "umount" on -# Mac OS X and friends. -# -# This script can be sourced or executed directly. -# -function fuse-unmount { - local MYNAME=$(basename "$BASH_SOURCE") - if [[ $# -eq 0 ]] ; then - echo "$MYNAME: missing argument" - exit 1 - fi - if [[ $OSTYPE == linux* ]] ; then - fusermount -u "$@" - else - # Mountpoint is in last argument, ignore anything else - # (like additional flags for fusermount). - local MNT=${@:$#} - umount "$MNT" - fi -} -# If the process name and the source file name is identical -# we have been executed, not sourced. -if [[ $(basename "$0") == $(basename "$BASH_SOURCE") ]] ; then - fuse-unmount "$@" -fi diff --git a/tests/hkdf_sanity/broken_content/gocryptfs.conf b/tests/hkdf_sanity/broken_content/gocryptfs.conf deleted file mode 100644 index 205f3ad..0000000 --- a/tests/hkdf_sanity/broken_content/gocryptfs.conf +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Creator": "gocryptfs v1.2.1-32-g14038a1-dirty", - "EncryptedKey": "b3888jnQC5GYem+YGtUkOTS13/YCOfA6J0/bkftfEoNA9fZTN2xMGw4c+LK+emg4L6P2wGvm44RUqCfFfgowxw==", - "ScryptObject": { - "Salt": "7YnR8bF7TzYNP5mIwmpQ1qj4e/QZkbH92Hx7YQctIZQ=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "HKDF", - "PlaintextNames" - ] -} diff --git a/tests/hkdf_sanity/broken_content/status.txt b/tests/hkdf_sanity/broken_content/status.txt deleted file mode 100644 index 30d42f7f01be89acba707923a5b0e595c07b378c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0?JBbu30eZEe(D;qA+xCrD#p6oFqzLN>z4wQ`R2YR5M@|`N3MaPSMbH SHi>82&a|p!`t~>T$cCu}>m4fq diff --git a/tests/hkdf_sanity/broken_names/L3yg-cJYAInDGg4TcjXrnw b/tests/hkdf_sanity/broken_names/L3yg-cJYAInDGg4TcjXrnw deleted file mode 100644 index 7ba2789f70f97cdba1c5c0002c7a0444b87bbb67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60 zcmV-C0K@+P0z_&n(B(lC(+AIMMnAdz&0o SaVW3$=4M|@NH3jjVsOe?Y8*iT diff --git a/tests/hkdf_sanity/broken_names/gocryptfs.conf b/tests/hkdf_sanity/broken_names/gocryptfs.conf deleted file mode 100644 index f0b1509..0000000 --- a/tests/hkdf_sanity/broken_names/gocryptfs.conf +++ /dev/null @@ -1,20 +0,0 @@ -{ - "Creator": "gocryptfs v1.2.1-32-g14038a1-dirty", - "EncryptedKey": "0ymk/BtKEN1KmRLMquLinLIzXDaf+GLuP2f9R4VbLOglim9nXd5WxkCFl0DQg0J2FtCEke9MQBaCfL5OTJdR4g==", - "ScryptObject": { - "Salt": "tCrF2o5GoOyQt0LAlCWk47hyJsF5K6ID9uPzjTSBbh8=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "HKDF", - "DirIV", - "EMENames", - "LongNames", - "Raw64" - ] -} diff --git a/tests/hkdf_sanity/broken_names/gocryptfs.diriv b/tests/hkdf_sanity/broken_names/gocryptfs.diriv deleted file mode 100644 index 24f3d28..0000000 --- a/tests/hkdf_sanity/broken_names/gocryptfs.diriv +++ /dev/null @@ -1 +0,0 @@ -%Cx(E!dц \ No newline at end of file diff --git a/tests/hkdf_sanity/sanity_test.go b/tests/hkdf_sanity/sanity_test.go deleted file mode 100644 index b382861..0000000 --- a/tests/hkdf_sanity/sanity_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// We test two filesystems that have the "HKDF" feature flag in their config file -// set, but the actual file contents and names are encrypted with HKDF disabled. -// This test verifies that the "HKDF" feature flag in the config file takes effect. -package hkdf_sanity - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func TestBrokenContent(t *testing.T) { - cDir := "broken_content" - pDir := test_helpers.TmpDir + "/" + cDir - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", "-wpanic=false") - _, err := ioutil.ReadFile(pDir + "/status.txt") - if err == nil { - t.Error("this should fail") - } - test_helpers.UnmountPanic(pDir) -} - -func TestBrokenNames(t *testing.T) { - cDir := "broken_names" - pDir := test_helpers.TmpDir + "/" + cDir - test_helpers.MountOrFatal(t, cDir, pDir, "-extpass", "echo test", "-wpanic=false") - _, err := os.Stat(pDir + "/status.txt") - if err == nil { - t.Error("this should fail") - } - test_helpers.UnmountPanic(pDir) -} diff --git a/tests/len2elen.sh b/tests/len2elen.sh deleted file mode 100755 index 86f2119..0000000 --- a/tests/len2elen.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -eu -# -# Check plaintext file name length -> encrypted file name length relation -# -# Part of the gocryptfs test suite -# https://nuetzlich.net/gocryptfs/ - -NAME="x" -LEN=1 - -if [[ ! -f a/gocryptfs.conf ]] ; then - echo "fatal: must have gocryptfs dir 'a' mounted at 'b'" - exit 1 -fi -if ! mountpoint b > /dev/null ; then - echo "fatal: must have gocryptfs dir 'a' mounted at 'b'" - exit 1 -fi - -rm -f b/* - -while [[ $LEN -le 255 ]]; do - touch b/$NAME || break - ELEN=$(ls a | wc -L) - echo $LEN $ELEN - rm b/$NAME - NAME="${NAME}x" - LEN=${#NAME} -done diff --git a/tests/matrix/atime_darwin.go b/tests/matrix/atime_darwin.go deleted file mode 100644 index 5f89c69..0000000 --- a/tests/matrix/atime_darwin.go +++ /dev/null @@ -1,9 +0,0 @@ -package matrix - -import ( - "syscall" -) - -func extractAtimeMtime(st syscall.Stat_t) [2]syscall.Timespec { - return [2]syscall.Timespec{st.Atimespec, st.Mtimespec} -} diff --git a/tests/matrix/atime_linux.go b/tests/matrix/atime_linux.go deleted file mode 100644 index fb7b94f..0000000 --- a/tests/matrix/atime_linux.go +++ /dev/null @@ -1,9 +0,0 @@ -package matrix - -import ( - "syscall" -) - -func extractAtimeMtime(st syscall.Stat_t) [2]syscall.Timespec { - return [2]syscall.Timespec{st.Atim, st.Mtim} -} diff --git a/tests/matrix/concurrency_test.go b/tests/matrix/concurrency_test.go deleted file mode 100644 index 24d4578..0000000 --- a/tests/matrix/concurrency_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package matrix - -import ( - "bytes" - "io" - "log" - "os" - "sync" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -// https://github.com/rfjakob/gocryptfs/issues/363 -// -// Note: this test calls log.Fatal() instead of t.Fatal() because apparently, -// calling t.Fatal() from a goroutine hangs the test. -func TestConcurrentReadWrite(t *testing.T) { - var wg sync.WaitGroup - fn := test_helpers.DefaultPlainDir + "/TestConcurrentReadWrite" - if f, err := os.Create(fn); err != nil { - t.Fatal(err) - } else { - f.Close() - } - buf := make([]byte, 100) - content := []byte("1234567890") - threads := 10 - loops := 30 - for i := 0; i < threads; i++ { - // Reader thread - wg.Add(1) - go func() { - fRd, err := os.Open(fn) - if err != nil { - log.Fatal(err) - } - for j := 0; j < loops; j++ { - n, err := fRd.ReadAt(buf, 0) - if err != nil && err != io.EOF { - log.Fatal(err) - } - if n != 0 && n != 10 { - log.Fatalf("strange read length: %d", n) - } - } - fRd.Close() - wg.Done() - }() - - // Writer thread - wg.Add(1) - go func() { - fWr, err := os.OpenFile(fn, os.O_RDWR, 0700) - if err != nil { - log.Fatal(err) - } - for j := 0; j < loops; j++ { - err = fWr.Truncate(0) - if err != nil { - log.Fatal(err) - } - _, err = fWr.WriteAt(content, 0) - if err != nil { - log.Fatal(err) - } - } - fWr.Close() - wg.Done() - }() - } - wg.Wait() -} - -// https://github.com/rfjakob/gocryptfs/issues/363 -// -// Note: this test calls log.Fatal() instead of t.Fatal() because apparently, -// calling t.Fatal() from a goroutine hangs the test. -func TestConcurrentReadCreate(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/TestConcurrentReadCreate" - content := []byte("1234567890") - loops := 100 - var wg sync.WaitGroup - // "Create()" thread - wg.Add(1) - go func() { - for i := 0; i < loops; i++ { - f, err := os.Create(fn) - if err != nil { - log.Fatal(err) - } - _, err = f.Write(content) - if err != nil { - log.Fatal(err) - } - f.Close() - syscall.Unlink(fn) - } - wg.Done() - }() - // "Reader" thread - wg.Add(1) - go func() { - buf0 := make([]byte, 100) - for i := 0; i < loops; i++ { - f, err := os.Open(fn) - if err != nil { - i++ - continue - } - n, err := f.Read(buf0) - if err == io.EOF { - i++ - continue - } - if err != nil { - log.Fatal(err) - } - buf := buf0[:n] - if bytes.Compare(buf, content) != 0 { - log.Fatal("content mismatch") - } - f.Close() - } - wg.Done() - }() - wg.Wait() -} - -// TestInoReuse tries to uncover problems when a file gets replaced by -// a directory with the same inode number (and vice versa). -// -// So far, it only has triggered warnings like this -// -// go-fuse: warning: Inode.Path: inode i4201033 is orphaned, replacing segment with ".go-fuse.5577006791947779410/deleted" -// -// but none of the "blocked waiting for FORGET". -func TestInoReuse(t *testing.T) { - var wg sync.WaitGroup - fn := test_helpers.DefaultPlainDir + "/" + t.Name() - - wg.Add(1) - go func() { - for i := 0; i < 1000; i++ { - fd, err := syscall.Creat(fn, 0600) - if err == syscall.EISDIR { - continue - } - if err != nil { - t.Error(err) - break - } - var st syscall.Stat_t - syscall.Fstat(fd, &st) - if i%2 == 0 { - syscall.Close(fd) - syscall.Unlink(fn) - } else { - syscall.Unlink(fn) - syscall.Close(fd) - - } - } - wg.Done() - }() - - wg.Add(1) - go func() { - for i := 0; i < 1000; i++ { - err := syscall.Mkdir(fn, 0700) - if err == syscall.EEXIST { - continue - } - if err != nil { - t.Error(err) - break - } - var st syscall.Stat_t - syscall.Stat(fn, &st) - syscall.Rmdir(fn) - } - wg.Done() - }() - wg.Wait() -} diff --git a/tests/matrix/dir_test.go b/tests/matrix/dir_test.go deleted file mode 100644 index 2f7a034..0000000 --- a/tests/matrix/dir_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package matrix - -import ( - "fmt" - "os" - "os/exec" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -// Test Mkdir and Rmdir -func TestMkdirRmdir(t *testing.T) { - test_helpers.TestMkdirRmdir(t, test_helpers.DefaultPlainDir) -} - -// Overwrite an empty directory with another directory -func TestDirOverwrite(t *testing.T) { - dir1 := test_helpers.DefaultPlainDir + "/DirOverwrite1" - dir2 := test_helpers.DefaultPlainDir + "/DirOverwrite2" - err := os.Mkdir(dir1, 0777) - if err != nil { - t.Fatal(err) - } - err = os.Mkdir(dir2, 0777) - if err != nil { - t.Fatal(err) - } - err = syscall.Rename(dir1, dir2) - if err != nil { - t.Fatal(err) - } -} - -// Test that we can create and remove a directory regardless of the permission it has -// https://github.com/rfjakob/gocryptfs/issues/354 -func TestRmdirPerms(t *testing.T) { - for _, perm := range []uint32{0000, 0100, 0200, 0300, 0400, 0500, 0600, 0700} { - dir := fmt.Sprintf("TestRmdir%#o", perm) - path := test_helpers.DefaultPlainDir + "/" + dir - err := syscall.Mkdir(path, perm) - if err != nil { - t.Fatalf("Mkdir %q: %v", dir, err) - } - err = syscall.Rmdir(path) - if err != nil { - t.Fatalf("Rmdir %q: %v", dir, err) - } - } -} - -// TestHaveDotdot checks that we have "." and ".." in a directory. -// (gocryptfs v2.0-beta1 did not!) -func TestHaveDotdot(t *testing.T) { - dir1 := test_helpers.DefaultPlainDir + "/TestHaveDotdot" - err := os.Mkdir(dir1, 0700) - if err != nil { - t.Fatal(err) - } - // All Go readdir functions filter out "." and "..". - // Fall back to "ls -a" which does not. - out, err := exec.Command("ls", "-a", dir1).CombinedOutput() - if err != nil { - t.Fatal(err) - } - have := string(out) - want := ".\n..\n" - if have != want { - t.Errorf("have=%q want=%q", have, want) - } -} diff --git a/tests/matrix/fallocate_test.go b/tests/matrix/fallocate_test.go deleted file mode 100644 index dde0685..0000000 --- a/tests/matrix/fallocate_test.go +++ /dev/null @@ -1,175 +0,0 @@ -package matrix - -import ( - "os" - "runtime" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -const ( - // From man statfs - TMPFS_MAGIC = 0x01021994 - EXT4_SUPER_MAGIC = 0xef53 -) - -// isWellKnownFS decides if the backing filesystem is well-known. -// The expected allocated sizes are only valid on tmpfs and ext4. btrfs -// gives different results, but that's not an error. -func isWellKnownFS(fn string) bool { - var fs syscall.Statfs_t - err := syscall.Statfs(fn, &fs) - if err != nil { - panic(err) - } - if fs.Type == EXT4_SUPER_MAGIC || fs.Type == TMPFS_MAGIC { - return true - } - return false -} - -const FALLOC_DEFAULT = 0x00 -const FALLOC_FL_KEEP_SIZE = 0x01 - -func TestFallocate(t *testing.T) { - if runtime.GOOS == "darwin" { - t.Skipf("OSX does not support fallocate") - } - fn := test_helpers.DefaultPlainDir + "/fallocate" - file, err := os.Create(fn) - if err != nil { - t.FailNow() - } - defer file.Close() - wellKnown := isWellKnownFS(test_helpers.DefaultCipherDir) - fd := int(file.Fd()) - nBytes := test_helpers.Du(t, fd) - if nBytes != 0 { - t.Fatalf("Empty file has %d bytes", nBytes) - } - // Allocate 30 bytes, keep size - // gocryptfs || (0 blocks) - // ext4 | d | (1 block) - // ^ d = data block - err = syscallcompat.Fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, 30) - if err != nil { - t.Error(err) - } - var want int64 - nBytes = test_helpers.Du(t, fd) - want = 4096 - if nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - test_helpers.VerifySize(t, fn, 0) - // Three ciphertext blocks. The middle one should be a file hole. - // gocryptfs | h | h | d| (1 block) - // ext4 | d | h | d | (2 blocks) - // ^ h = file hole - // (Note that gocryptfs blocks are slightly bigger than the ext4 blocks, - // but the last one is partial) - err = file.Truncate(9000) - if err != nil { - t.Fatal(err) - } - nBytes = test_helpers.Du(t, fd) - want = 2 * 4096 - if wellKnown && nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - if md5 := test_helpers.Md5fn(fn); md5 != "5420afa22f6423a9f59e669540656bb4" { - t.Errorf("Wrong md5 %s", md5) - } - // Allocate the whole file space - // gocryptfs | h | h | d| (1 block) - // ext4 | d | d | d | (3 blocks - err = syscallcompat.Fallocate(fd, FALLOC_DEFAULT, 0, 9000) - if err != nil { - t.Fatal(err) - } - nBytes = test_helpers.Du(t, fd) - want = 3 * 4096 - if nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - // Neither apparent size nor content should have changed - test_helpers.VerifySize(t, fn, 9000) - if md5 := test_helpers.Md5fn(fn); md5 != "5420afa22f6423a9f59e669540656bb4" { - t.Errorf("Wrong md5 %s", md5) - } - - // Partial block on the end. The first ext4 block is dirtied by the header. - // gocryptfs | h | h | d| (1 block) - // ext4 | d | h | d | (2 blocks) - file.Truncate(0) - file.Truncate(9000) - nBytes = test_helpers.Du(t, fd) - want = 2 * 4096 - if wellKnown && nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - // Allocate 10 bytes in the second block - // gocryptfs | h | h | d| (1 block) - // ext4 | d | d | d | (3 blocks) - syscallcompat.Fallocate(fd, FALLOC_DEFAULT, 5000, 10) - nBytes = test_helpers.Du(t, fd) - want = 3 * 4096 - if wellKnown && nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - // Neither apparent size nor content should have changed - test_helpers.VerifySize(t, fn, 9000) - if md5 := test_helpers.Md5fn(fn); md5 != "5420afa22f6423a9f59e669540656bb4" { - t.Errorf("Wrong md5 %s", md5) - } - // Grow the file to 4 blocks - // gocryptfs | h | h | d |d| (2 blocks) - // ext4 | d | d | d | d | (4 blocks) - syscallcompat.Fallocate(fd, FALLOC_DEFAULT, 15000, 10) - nBytes = test_helpers.Du(t, fd) - want = 4 * 4096 - if wellKnown && nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - test_helpers.VerifySize(t, fn, 15010) - if md5 := test_helpers.Md5fn(fn); md5 != "c4c44c7a41ab7798a79d093eb44f99fc" { - t.Errorf("Wrong md5 %s", md5) - } - // Shrinking a file using fallocate should have no effect - for _, off := range []int64{0, 10, 2000, 5000} { - for _, sz := range []int64{0, 1, 42, 6000} { - syscallcompat.Fallocate(fd, FALLOC_DEFAULT, off, sz) - test_helpers.VerifySize(t, fn, 15010) - if md5 := test_helpers.Md5fn(fn); md5 != "c4c44c7a41ab7798a79d093eb44f99fc" { - t.Errorf("Wrong md5 %s", md5) - } - } - } - // We used to allocate 18 bytes too much: - // https://github.com/rfjakob/gocryptfs/issues/311 - // - // 8110 bytes of plaintext should get us exactly 8192 bytes of ciphertext. - err = file.Truncate(0) - if err != nil { - t.Fatal(err) - } - err = syscallcompat.Fallocate(fd, FALLOC_DEFAULT, 0, 8110) - if err != nil { - t.Fatal(err) - } - nBytes = test_helpers.Du(t, fd) - want = 8192 - if nBytes != want { - t.Errorf("Expected %d allocated bytes, have %d", want, nBytes) - } - // Cleanup - syscall.Unlink(fn) - if !wellKnown { - // Even though most tests have been executed still, inform the user - // that some were disabled - t.Skipf("backing fs is not ext4 or tmpfs, skipped some disk-usage checks\n") - } -} diff --git a/tests/matrix/matrix_test.go b/tests/matrix/matrix_test.go deleted file mode 100644 index 6622213..0000000 --- a/tests/matrix/matrix_test.go +++ /dev/null @@ -1,903 +0,0 @@ -// Tests run for (almost all) combinations of openssl, aessiv, plaintextnames. -package matrix - -// File reading, writing, modification, truncate -// -// Runs everything four times, for all combinations of -// "-plaintextnames" and "-openssl". -// -// Test Matrix: -// openssl=true openssl=false -// plaintextnames=false X X -// plaintextnames=true X X - -import ( - "bytes" - "flag" - "fmt" - "io/ioutil" - "math/rand" - "os" - "os/exec" - "path/filepath" - "runtime" - "sync" - "syscall" - "testing" - - "golang.org/x/sys/unix" - - "github.com/rfjakob/gocryptfs/internal/stupidgcm" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -// Several tests need to be aware if plaintextnames is active or not, so make this -// a global variable -var testcase testcaseMatrix - -type testcaseMatrix struct { - plaintextnames bool - openssl string - aessiv bool - raw64 bool - extraArgs []string -} - -var matrix = []testcaseMatrix{ - // Normal - {false, "auto", false, false, nil}, - {false, "true", false, false, nil}, - {false, "false", false, false, nil}, - // Plaintextnames - {true, "true", false, false, nil}, - {true, "false", false, false, nil}, - // AES-SIV (does not use openssl, no need to test permutations) - {false, "auto", true, false, nil}, - {true, "auto", true, false, nil}, - // Raw64 - {false, "auto", false, true, nil}, - // -serialize_reads - {false, "auto", false, false, []string{"-serialize_reads"}}, - {false, "auto", false, false, []string{"-sharedstorage"}}, -} - -// This is the entry point for the tests -func TestMain(m *testing.M) { - // Make "testing.Verbose()" return the correct value - flag.Parse() - for _, testcase = range matrix { - if testcase.openssl == "true" && stupidgcm.BuiltWithoutOpenssl { - continue - } - if testing.Verbose() { - fmt.Printf("matrix: testcase = %#v\n", testcase) - } - test_helpers.ResetTmpDir(!testcase.plaintextnames) - opts := []string{"-zerokey"} - //opts = append(opts, "-fusedebug") - opts = append(opts, fmt.Sprintf("-openssl=%v", testcase.openssl)) - opts = append(opts, fmt.Sprintf("-plaintextnames=%v", testcase.plaintextnames)) - opts = append(opts, fmt.Sprintf("-aessiv=%v", testcase.aessiv)) - opts = append(opts, fmt.Sprintf("-raw64=%v", testcase.raw64)) - opts = append(opts, testcase.extraArgs...) - test_helpers.MountOrExit(test_helpers.DefaultCipherDir, test_helpers.DefaultPlainDir, opts...) - before := test_helpers.ListFds(0, test_helpers.TmpDir) - r := m.Run() - // Catch fd leaks in the tests. NOTE: this does NOT catch leaks in - // the gocryptfs FUSE process, but only in the tests that access it! - // All fds that point outside TmpDir are not interesting (the Go test - // infrastucture creates temporary log files we don't care about). - after := test_helpers.ListFds(0, test_helpers.TmpDir) - if len(before) != len(after) { - fmt.Printf("fd leak in test process? before, after:\n%v\n%v\n", before, after) - os.Exit(1) - } - test_helpers.UnmountPanic(test_helpers.DefaultPlainDir) - if r != 0 { - os.Exit(r) - } - } - os.Exit(0) -} - -// Write `n` random bytes to filename `fn`, read again, compare hash -func testWriteN(t *testing.T, fn string, n int) string { - file, err := os.Create(test_helpers.DefaultPlainDir + "/" + fn) - if err != nil { - t.Fatal(err) - } - - d := make([]byte, n) - for i := range d { - // Fill with pattern - d[i] = byte(rand.Int()) - } - _, err = file.Write(d) - if err != nil { - t.Fatal(err) - } - err = file.Close() - if err != nil { - t.Fatal(err) - } - - test_helpers.VerifySize(t, test_helpers.DefaultPlainDir+"/"+fn, n) - - hashWant := test_helpers.Md5hex(d) - - hashActual := test_helpers.Md5fn(test_helpers.DefaultPlainDir + "/" + fn) - - if hashActual != hashWant { - t.Errorf("Wrong content, hashWant=%s hashActual=%s", hashWant, hashActual) - } - - return hashActual -} - -func TestWrite10(t *testing.T) { - testWriteN(t, "10", 10) -} - -func TestWrite100(t *testing.T) { - testWriteN(t, "100", 100) -} - -func TestWrite1M(t *testing.T) { - testWriteN(t, "1M", 1024*1024) -} - -func TestWrite100x100(t *testing.T) { - hashWant := testWriteN(t, "100x100", 100) - // Read and check 100 times to catch race conditions - var i int - for i = 0; i < 100; i++ { - hashActual := test_helpers.Md5fn(test_helpers.DefaultPlainDir + "/100x100") - if hashActual != hashWant { - fmt.Printf("Read corruption in loop #%d: have=%s want=%s\n", i, hashActual, hashWant) - t.FailNow() - } else { - //fmt.Print(".") - } - } -} - -func TestWrite10Tight(t *testing.T) { - path := test_helpers.DefaultPlainDir + "/TestWrite10Tight" - content := make([]byte, 10) - buf := make([]byte, 100) - for i := 0; i < 100; i++ { - file, err := os.Create(path) - if err != nil { - t.Fatal(err) - } - _, err = file.Write(content) - if err != nil { - t.Fatal(err) - } - err = file.Close() - if err != nil { - t.Fatal(err) - } - file, err = os.Open(path) - if err != nil { - t.Fatal(err) - } - n, err := file.Read(buf) - if err != nil { - t.Fatal(err) - } - if n != 10 { - t.Fatalf("want 10 bytes, got %d", n) - } - err = file.Close() - if err != nil { - t.Fatal(err) - } - err = os.Remove(path) - if err != nil { - t.Fatal() - } - } -} - -// Hint for calculating reference md5sums: -// dd if=/dev/zero count=1 bs=XYZ | md5sum -func TestTruncate(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/truncate" - file, err := os.Create(fn) - if err != nil { - t.FailNow() - } - defer file.Close() - // Grow to two blocks - file.Truncate(7000) - test_helpers.VerifySize(t, fn, 7000) - if md5 := test_helpers.Md5fn(fn); md5 != "95d4ec7038e3e4fdbd5f15c34c3f0b34" { - t.Errorf("Wrong md5 %s", md5) - } - // Shrink - needs RMW - file.Truncate(6999) - test_helpers.VerifySize(t, fn, 6999) - if md5 := test_helpers.Md5fn(fn); md5 != "35fd15873ec6c35380064a41b9b9683b" { - t.Errorf("Wrong md5 %s", md5) - } - // Shrink to one partial block - file.Truncate(465) - test_helpers.VerifySize(t, fn, 465) - if md5 := test_helpers.Md5fn(fn); md5 != "a1534d6e98a6b21386456a8f66c55260" { - t.Errorf("Wrong md5 %s", md5) - } - // Grow to exactly one block - file.Truncate(4096) - test_helpers.VerifySize(t, fn, 4096) - if md5 := test_helpers.Md5fn(fn); md5 != "620f0b67a91f7f74151bc5be745b7110" { - t.Errorf("Wrong md5 %s", md5) - } - // Truncate to zero - file.Truncate(0) - test_helpers.VerifySize(t, fn, 0) - // Grow to 10MB (creates file holes) - var sz int - sz = 10 * 1024 * 1024 - file.Truncate(int64(sz)) - test_helpers.VerifySize(t, fn, sz) - if md5 := test_helpers.Md5fn(fn); md5 != "f1c9645dbc14efddc7d8a322685f26eb" { - t.Errorf("Wrong md5 %s", md5) - } - // Grow to 10MB + 100B (partial block on the end) - sz = 10*1024*1024 + 100 - file.Truncate(int64(sz)) - test_helpers.VerifySize(t, fn, sz) - if md5 := test_helpers.Md5fn(fn); md5 != "c23ea79b857b91a7ff07c6ecf185f1ca" { - t.Errorf("Wrong md5 %s", md5) - } - // Grow to 20MB (creates file holes, partial block on the front) - sz = 20 * 1024 * 1024 - file.Truncate(int64(sz)) - test_helpers.VerifySize(t, fn, sz) - if md5 := test_helpers.Md5fn(fn); md5 != "8f4e33f3dc3e414ff94e5fb6905cba8c" { - t.Errorf("Wrong md5 %s", md5) - } -} - -func TestAppend(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/append" - file, err := os.Create(fn) - if err != nil { - t.FailNow() - } - defer file.Close() - data := []byte("testdata123456789") // length 17 - var buf bytes.Buffer - var hashWant string - for i := 0; i <= 500; i++ { - file.Write(data) - buf.Write(data) - hashWant = test_helpers.Md5hex(buf.Bytes()) - hashActual := test_helpers.Md5fn(fn) - if hashWant != hashActual { - t.FailNow() - } - } - - // Overwrite with the same data - // Hash must stay the same - file.Seek(0, 0) - for i := 0; i <= 500; i++ { - file.Write(data) - hashActual := test_helpers.Md5fn(fn) - if hashWant != hashActual { - t.FailNow() - } - } -} - -// Create a file with holes by writing to offset 0 (block #0) and -// offset 4096 (block #1). -func TestFileHoles(t *testing.T) { - fn := test_helpers.DefaultPlainDir + "/fileholes" - file, err := os.Create(fn) - if err != nil { - t.Errorf("file create failed") - } - defer file.Close() - foo := []byte("foo") - file.Write(foo) - file.WriteAt(foo, 4096) - _, err = ioutil.ReadFile(fn) - if err != nil { - t.Error(err) - } -} - -// sContains - does the slice of strings "haystack" contain "needle"? -func sContains(haystack []string, needle string) bool { - for _, element := range haystack { - if element == needle { - return true - } - } - return false -} - -func TestRmwRace(t *testing.T) { - - runtime.GOMAXPROCS(10) - - fn := test_helpers.DefaultPlainDir + "/rmwrace" - f1, err := os.Create(fn) - if err != nil { - t.Fatalf("file create failed") - } - defer f1.Close() - f2, err := os.Create(fn) - if err != nil { - t.Fatalf("file create failed") - } - defer f2.Close() - - oldBlock := bytes.Repeat([]byte("o"), 4096) - - newBlock := bytes.Repeat([]byte("n"), 4096) - - shortBlock := bytes.Repeat([]byte("s"), 16) - - mergedBlock := make([]byte, 4096) - copy(mergedBlock, newBlock) - copy(mergedBlock[4080:], shortBlock) - - goodMd5 := make(map[string]int) - - for i := 0; i < 1000; i++ { - // Reset to [ooooooooo] - _, err = f1.WriteAt(oldBlock, 0) - if err != nil { - t.Fatalf("Write failed") - } - - var wg sync.WaitGroup - wg.Add(2) - - // Write to the end of the file, [....ssss] - go func() { - f1.WriteAt(shortBlock, 4080) - wg.Done() - }() - - // Overwrite to [nnnnnnn] - go func() { - f2.WriteAt(newBlock, 0) - wg.Done() - }() - - wg.Wait() - - // The file should be either: - // [nnnnnnnnnn] (md5: 6c1660fdabccd448d1359f27b3db3c99) or - // [nnnnnnssss] (md5: da885006a6a284530a427c73ce1e5c32) - // but it must not be - // [oooooossss] - - buf, _ := ioutil.ReadFile(fn) - m := test_helpers.Md5hex(buf) - goodMd5[m] = goodMd5[m] + 1 - - /* - if m == "6c1660fdabccd448d1359f27b3db3c99" { - fmt.Println(hex.Dump(buf)) - t.FailNow() - } - */ - } -} - -// With "--plaintextnames", the name "/gocryptfs.conf" is reserved. -// Otherwise there should be no restrictions. -func TestFiltered(t *testing.T) { - filteredFile := test_helpers.DefaultPlainDir + "/gocryptfs.conf" - file, err := os.Create(filteredFile) - if testcase.plaintextnames && err == nil { - t.Errorf("should have failed but didn't") - } else if !testcase.plaintextnames && err != nil { - t.Error(err) - } - file.Close() - - err = os.Remove(filteredFile) - if testcase.plaintextnames && err == nil { - t.Errorf("should have failed but didn't") - } else if !testcase.plaintextnames && err != nil { - t.Error(err) - } -} - -func TestFilenameEncryption(t *testing.T) { - file, err := os.Create(test_helpers.DefaultPlainDir + "/TestFilenameEncryption.txt") - file.Close() - if err != nil { - t.Fatal(err) - } - _, err = os.Stat(test_helpers.DefaultCipherDir + "/TestFilenameEncryption.txt") - if testcase.plaintextnames && err != nil { - t.Errorf("plaintextnames not working: %v", err) - } else if !testcase.plaintextnames && err == nil { - t.Errorf("file name encryption not working") - } -} - -// Test Rename -func TestRename(t *testing.T) { - test_helpers.TestRename(t, test_helpers.DefaultPlainDir) -} - -// Test that names of all lengths work -func TestNameLengths(t *testing.T) { - f, err := os.Open(test_helpers.DefaultPlainDir) - if err != nil { - t.Fatal(err) - } - entries, err := f.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - f.Close() - cnt1 := len(entries) - - wd := test_helpers.DefaultPlainDir + "/" - name := "x" - for len(name) < 2000 { - f, err := os.Create(wd + name + "x") - if err != nil { - break - } - name = name + "x" - f.Close() - f, err = os.Open(test_helpers.DefaultPlainDir) - if err != nil { - t.Fatal(err) - } - // In v1.7-rc2, we had a bug that allowed creation of too-long names. - // This threw errors in like this in READDIR: - // - // OpenDir ".": invalid entry "gocryptfs.longname.wrE-izsR9ciEkP7JSCFDrk_d_Nj4mQo1dGY6hjuixAU=": - // Could not read .name: ReadLongName: size=345 > limit=344 - // - entries, err = f.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - f.Close() - cnt2 := len(entries) - if cnt2 != cnt1+1 { - t.Fatalf("len=%d: expected %d dir entries, have %d: %v", len(name), cnt1+1, cnt2, entries) - } - err = syscall.Unlink(wd + name) - if err != nil { - t.Fatal(err) - } - } - if len(name) != 255 { - t.Errorf("maxlen=%d", len(name)) - } -} - -func TestLongNames(t *testing.T) { - fi, err := ioutil.ReadDir(test_helpers.DefaultCipherDir) - if err != nil { - t.Fatal(err) - } - cnt1 := len(fi) - wd := test_helpers.DefaultPlainDir + "/" - // Create file with long name - n255x := string(bytes.Repeat([]byte("x"), 255)) - f, err := os.Create(wd + n255x) - if err != nil { - t.Fatalf("Could not create n255x: %v", err) - } - f.Close() - if !test_helpers.VerifyExistence(t, wd+n255x) { - t.Errorf("n255x is not in directory listing") - } - // Rename long to long (target does not exist) - n255y := string(bytes.Repeat([]byte("y"), 255)) - err = os.Rename(wd+n255x, wd+n255y) - if err != nil { - t.Fatalf("Could not rename n255x to n255y: %v", err) - } - if !test_helpers.VerifyExistence(t, wd+n255y) { - t.Errorf("n255y is not in directory listing") - } - // Rename long to long (target exists) - f, err = os.Create(wd + n255x) - if err != nil { - t.Fatalf("Could not create n255x: %v", err) - } - f.Close() - err = os.Rename(wd+n255x, wd+n255y) - if err != nil { - t.Fatalf("Could not rename n255x to n255y: %v", err) - } - if !test_helpers.VerifyExistence(t, wd+n255y) { - t.Errorf("n255y is not in directory listing") - } - // Rename long to short (target does not exist) - err = os.Rename(wd+n255y, wd+"short") - if err != nil { - t.Fatalf("Could not rename n255y to short: %v", err) - } - if !test_helpers.VerifyExistence(t, wd+"short") { - t.Errorf("short is not in directory listing") - } - // Rename long to short (target exists) - f, err = os.Create(wd + n255y) - if err != nil { - t.Fatalf("Could not create n255y: %v", err) - } - f.Close() - err = os.Rename(wd+n255y, wd+"short") - if err != nil { - t.Fatalf("Could not rename n255y to short: %v", err) - } - if !test_helpers.VerifyExistence(t, wd+"short") { - t.Errorf("short is not in directory listing") - } - // Rename short to long (target does not exist) - err = os.Rename(wd+"short", wd+n255x) - if err != nil { - t.Fatalf("Could not rename short to n255x: %v", err) - } - if !test_helpers.VerifyExistence(t, wd+n255x) { - t.Errorf("255x is not in directory listing II") - } - // Rename short to long (target exists) - f, err = os.Create(wd + "short") - if err != nil { - t.Fatalf("Could not create short: %v", err) - } - f.Close() - err = os.Rename(wd+"short", wd+n255x) - if err != nil { - t.Fatalf("Could not rename short to n255x: %v", err) - } - if !test_helpers.VerifyExistence(t, wd+n255x) { - t.Errorf("n255x is not in directory listing") - } - // Unlink - err = syscall.Unlink(wd + n255x) - if err != nil { - t.Fatalf("Could not unlink n255x: %v", err) - } - if test_helpers.VerifyExistence(t, wd+n255x) { - t.Errorf("n255x still there after unlink") - } - // Long symlink - n255s := string(bytes.Repeat([]byte("s"), 255)) - err = os.Symlink("/", wd+n255s) - if err != nil { - t.Fatal(err) - } - if !test_helpers.VerifyExistence(t, wd+n255s) { - t.Errorf("n255s is not in directory listing") - } - err = syscall.Unlink(wd + n255s) - if err != nil { - t.Error(err) - } - // Long dir - n255d := string(bytes.Repeat([]byte("d"), 255)) - err = os.Mkdir(wd+n255d, 0777) - if err != nil { - t.Fatal(err) - } - err = syscall.Rmdir(wd + n255d) - if err != nil { - t.Error(err) - } - // Check for orphaned files - fi, err = ioutil.ReadDir(test_helpers.DefaultCipherDir) - if err != nil { - t.Fatal(err) - } - cnt2 := len(fi) - if cnt1 != cnt2 { - t.Errorf("Leftover files, cnt1=%d cnt2=%d", cnt1, cnt2) - } -} - -// Create hard link with long name. -// This was broken up to v1.2. -func TestLongLink(t *testing.T) { - wd := test_helpers.DefaultPlainDir + "/" - target := wd + "TestLongLink.target" - f, err := os.Create(target) - if err != nil { - t.Fatalf("%v", err) - } - f.Close() - l255 := string(bytes.Repeat([]byte("l"), 255)) - err = os.Link(target, wd+l255) - if err != nil { - t.Error(err) - } -} - -func TestLchown(t *testing.T) { - name := test_helpers.DefaultPlainDir + "/symlink" - err := os.Symlink("/target/does/not/exist", name) - if err != nil { - t.Fatal(err) - } - err = os.Chown(name, os.Getuid(), os.Getgid()) - if err == nil { - t.Error("Chown on dangling symlink should fail") - } - err = os.Lchown(name, os.Getuid(), os.Getgid()) - if err != nil { - t.Error(err) - } -} - -// Set nanoseconds by path, symlink -func TestUtimesNanoSymlink(t *testing.T) { - if runtime.GOOS == "darwin" { - t.Skipf("MacOS \"touch\" does not support \"--no-dereference\"") - } - path := test_helpers.DefaultPlainDir + "/utimesnano_symlink" - err := os.Symlink("/some/nonexisting/file", path) - if err != nil { - t.Fatal(err) - } - // syscall.UtimesNano does not provide a way to pass AT_SYMLINK_NOFOLLOW, - // so we call the external utility "touch", which does. - cmd := exec.Command("touch", "--no-dereference", path) - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - err = cmd.Run() - if err != nil { - t.Error(err) - } -} - -type utimesTestcaseStruct struct { - // Input atime and mtime - in [2]syscall.Timespec - // Expected output atime and mtime - out [2]syscall.Timespec -} - -// compareTimespec return true if the two passed Timespec are identical. -func compareTimespec(want syscall.Timespec, actual syscall.Timespec) bool { - if want.Sec != actual.Sec { - return false - } - if want.Nsec != actual.Nsec { - return false - } - return true -} - -const _UTIME_OMIT = ((1 << 30) - 2) - -// doTestUtimesNano verifies that setting nanosecond-precision times on "path" -// works correctly. Pass "/proc/self/fd/N" to test a file descriptor. -func doTestUtimesNano(t *testing.T, path string) { - utimeTestcases := []utimesTestcaseStruct{ - { - in: [2]syscall.Timespec{{Sec: 50, Nsec: 0}, {Sec: 51, Nsec: 0}}, - out: [2]syscall.Timespec{{Sec: 50, Nsec: 0}, {Sec: 51, Nsec: 0}}, - }, - { - in: [2]syscall.Timespec{{Sec: 1, Nsec: 2}, {Sec: 3, Nsec: 4}}, - out: [2]syscall.Timespec{{Sec: 1, Nsec: 2}, {Sec: 3, Nsec: 4}}, - }, - { - in: [2]syscall.Timespec{{Sec: 7, Nsec: 8}, {Sec: 99, Nsec: _UTIME_OMIT}}, - out: [2]syscall.Timespec{{Sec: 7, Nsec: 8}, {Sec: 3, Nsec: 4}}, - }, - { - in: [2]syscall.Timespec{{Sec: 99, Nsec: _UTIME_OMIT}, {Sec: 5, Nsec: 6}}, - out: [2]syscall.Timespec{{Sec: 7, Nsec: 8}, {Sec: 5, Nsec: 6}}, - }, - } - if runtime.GOOS == "darwin" { - // darwin neither supports UTIME_OMIT nor nanoseconds (!?) - utimeTestcases = utimeTestcases[:1] - } - for i, tc := range utimeTestcases { - err := syscall.UtimesNano(path, tc.in[:]) - if err != nil { - t.Fatalf("%q: %v", path, err) - } - var st syscall.Stat_t - err = syscall.Stat(path, &st) - if err != nil { - t.Fatal(err) - } - want := tc.out - have := extractAtimeMtime(st) - if !compareTimespec(want[0], have[0]) { - t.Errorf("Testcase %d: atime: want=%+v, have=%+v", i, want[0], have[0]) - } - if !compareTimespec(want[1], have[1]) { - t.Errorf("Testcase %d: mtime: want=%+v, have=%+v", i, want[1], have[1]) - } - } -} - -// Set nanoseconds by path, normal file -func TestUtimesNano(t *testing.T) { - path := test_helpers.DefaultPlainDir + "/utimesnano" - err := ioutil.WriteFile(path, []byte("foobar"), 0600) - if err != nil { - t.Fatal(err) - } - doTestUtimesNano(t, path) -} - -// Set nanoseconds by fd -func TestUtimesNanoFd(t *testing.T) { - if runtime.GOOS == "darwin" { - t.Skipf("MacOS does not have /proc") - } - path := test_helpers.DefaultPlainDir + "/utimesnanofd" - f, err := os.Create(path) - if err != nil { - t.Fatal(err) - } - defer f.Close() - procPath := fmt.Sprintf("/proc/self/fd/%d", f.Fd()) - doTestUtimesNano(t, procPath) -} - -// Make sure the Mknod call works by creating a fifo (named pipe) -func TestMkfifo(t *testing.T) { - path := test_helpers.DefaultPlainDir + "/fifo1" - err := syscall.Mkfifo(path, 0700) - if err != nil { - t.Fatal(err) - } - path = test_helpers.DefaultPlainDir + "/gocryptfs.longname.XXX" - err = syscall.Mkfifo(path, 0700) - if err != nil { - t.Fatal(err) - } - err = os.Remove(path) - if err != nil { - t.Fatal(err) - } -} - -// TestMagicNames verifies that "magic" names are handled correctly -// https://github.com/rfjakob/gocryptfs/issues/174 -func TestMagicNames(t *testing.T) { - names := []string{"warmup1", "warmup2", "gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs", "gocryptfs.diriv"} - for _, n := range names { - t.Logf("Testing n=%q", n) - p := test_helpers.DefaultPlainDir + "/" + n - // Create file - err := ioutil.WriteFile(p, []byte("xxxxxxx"), 0200) - if err != nil { - t.Fatalf("creating file %q failed: %v", n, err) - } - // Rename magic to normal - err = os.Rename(p, test_helpers.DefaultPlainDir+"/x") - if err != nil { - t.Fatalf("rename 1 failed: %v", err) - } - // Rename normal to magic - err = os.Rename(test_helpers.DefaultPlainDir+"/x", p) - if err != nil { - t.Fatalf("rename 2 failed: %v", err) - } - // Unlink - err = syscall.Unlink(p) - if err != nil { - t.Fatal(err) - } - // Mkdir - err = os.Mkdir(p, 0700) - if err != nil { - t.Fatal(err) - } - // Rmdir - err = syscall.Rmdir(p) - if err != nil { - t.Fatal(err) - } - // Symlink - err = syscall.Symlink("xxxyyyyzzz", p) - if err != nil { - t.Fatal(err) - } - syscall.Unlink(p) - // Link - target := test_helpers.DefaultPlainDir + "/linktarget" - err = ioutil.WriteFile(target, []byte("yyyyy"), 0600) - if err != nil { - t.Fatal(err) - } - err = syscall.Link(target, p) - if err != nil { - t.Fatal(err) - } - } -} - -// Test that chmod works correctly -func TestChmod(t *testing.T) { - path := test_helpers.DefaultPlainDir + "/" + t.Name() - file, err := os.Create(path) - if err != nil { - t.Fatal(err) - } - file.Close() - modes := []os.FileMode{0777, 0707, 0606, 0666, 0444, 0000, 0111, 0123, 0321} - for _, modeWant := range modes { - fi, err := os.Stat(path) - if err != nil { - t.Fatal(err) - } - err = syscall.Chmod(path, uint32(modeWant)) - if err != nil { - t.Errorf("chmod %03o -> %03o failed: %v", fi.Mode(), modeWant, err) - continue - } - fi, err = os.Stat(path) - if err != nil { - t.Fatal(err) - } - modeHave := fi.Mode() - if modeHave != modeWant { - t.Errorf("modeHave %#o != modeWant %#o", modeHave, modeWant) - } - } -} - -// Test that access(2) works correctly -func TestAccess(t *testing.T) { - path := test_helpers.DefaultPlainDir + "/" + t.Name() - file, err := os.Create(path) - if err != nil { - t.Fatal(err) - } - defer file.Close() - - err = unix.Access(path, unix.F_OK) - if err != nil { - t.Error(err) - } - err = unix.Access(path, unix.R_OK) - if err != nil { - t.Error(err) - } - err = unix.Access(path, unix.X_OK) - if err == nil { - t.Error("X_OK should have failed") - } -} - -func TestStatfs(t *testing.T) { - var st syscall.Statfs_t - syscall.Statfs(test_helpers.DefaultPlainDir, &st) - if st.Bsize == 0 { - t.Errorf("statfs reports size zero: %#v", st) - } -} - -// gocryptfs 2.0 reported the ciphertext size on symlink creation, causing -// confusion: https://github.com/rfjakob/gocryptfs/issues/574 -func TestSymlinkSize(t *testing.T) { - p := filepath.Join(test_helpers.DefaultPlainDir, t.Name()) - // SYMLINK reports the size to the kernel - if err := syscall.Symlink("foo", p); err != nil { - t.Fatal(err) - } - // Kernel serves us this value from the attr cache - var st syscall.Stat_t - if err := syscall.Lstat(p, &st); err != nil { - t.Fatal(err) - } - if st.Size != 3 { - t.Errorf("wrong size: have %d, want %d", st.Size, 3) - } -} diff --git a/tests/maxlen.bash b/tests/maxlen.bash deleted file mode 100755 index fb133e0..0000000 --- a/tests/maxlen.bash +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -eu -# -# Find out the maximum supported filename length and print it. -# -# Part of the gocryptfs test suite -# https://nuetzlich.net/gocryptfs/ - -NAME="maxlen." -LEN=0 - -while [ $LEN -le 10000 ]; do - touch $NAME 2> /dev/null || break - rm $NAME - LEN=${#NAME} - NAME="${NAME}x" -done - -echo $LEN diff --git a/tests/plaintextnames/file_holes_test.go b/tests/plaintextnames/file_holes_test.go deleted file mode 100644 index 5de0152..0000000 --- a/tests/plaintextnames/file_holes_test.go +++ /dev/null @@ -1,153 +0,0 @@ -package plaintextnames - -import ( - "fmt" - "math/rand" - "os" - "os/exec" - "syscall" - "testing" - "time" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" - - "github.com/rfjakob/gocryptfs/contrib/findholes/holes" -) - -func findHolesPretty(t *testing.T, path string) string { - f, err := os.Open(path) - if err != nil { - t.Fatal(err) - } - defer f.Close() - - segments, err := holes.Find(int(f.Fd())) - if err != nil { - t.Fatal(err) - } - - return holes.PrettyPrint(segments) -} - -func doTestFileHoleCopy(t *testing.T, name string, writeOffsets []int64) { - n := "TestFileHoleCopy." + name - pPath := []string{pDir + "/" + n} - cPath := []string{cDir + "/" + n} - - os.Remove(pPath[0]) - holes.Create(pPath[0]) - - // expected md6 - md5 := test_helpers.Md5fn(pPath[0]) - - pSegments := []string{findHolesPretty(t, pPath[0])} - cSegments := []string{findHolesPretty(t, cPath[0])} - - // create 5 more copies - for i := 1; i < 5; i++ { - pPath = append(pPath, fmt.Sprintf("%s.%d", pPath[0], i)) - cPath = append(cPath, fmt.Sprintf("%s.%d", cPath[0], i)) - - out, err := exec.Command("cp", "--sparse=auto", pPath[i-1], pPath[i]).CombinedOutput() - if err != nil { - t.Fatal(string(out)) - } - - tmp := test_helpers.Md5fn(pPath[0]) - if tmp != md5 { - t.Errorf("pPath[%d]: wrong md5, have %s, want %s", i, tmp, md5) - } - - pSegments = append(pSegments, findHolesPretty(t, pPath[i])) - cSegments = append(cSegments, findHolesPretty(t, cPath[i])) - } - - // "cp --sparse=auto" checks of the file has fewer blocks on disk than it - // should have for its size. Only then it will try to create a sparse copy. - var st syscall.Stat_t - err := syscall.Stat(pPath[0], &st) - if err != nil { - t.Fatal(err) - } - // convert 512 byte blocks to 4k blocks - blocks4k := st.Blocks / 8 - // For more than a few fragments, ext4 allocates one extra block - blocks4k++ - if blocks4k >= (st.Size+4095)/4096 { - t.Logf("file will look non-sparse to cp, skipping segment check") - return - } - - // Check that size on disk stays the same across copies - var st0 syscall.Stat_t - if err := syscall.Stat(pPath[0], &st0); err != nil { - t.Fatal(err) - } - for i := range pSegments { - var st syscall.Stat_t - if err := syscall.Stat(pPath[i], &st); err != nil { - t.Fatal(err) - } - // Size on disk fluctuates by +-4kB due to different number of extents - // (looking at "filefrag -v", it seems like ext4 needs 4kB extra once - // you have >=4 extents) - if st.Blocks != st0.Blocks && st.Blocks != st0.Blocks-8 && st.Blocks != st0.Blocks+8 { - t.Errorf("size changed: st0.Blocks=%d st%d.Blocks=%d", st0.Blocks, i, st.Blocks) - } - } - - // Check that hole/data segments stays the same across copies - out := "" - same := true - for i := range pSegments { - out += fmt.Sprintf("pSegments[%d]:\n%s\n", i, pSegments[i]) - if i < len(pSegments)-1 { - if pSegments[i+1] != pSegments[i] { - same = false - t.Errorf("error: pSegments[%d] is different than pSegments[%d]!", i, i+1) - } - } - } - out += "------------------------------------\n" - for i := range cSegments { - out += fmt.Sprintf("cSegments[%d]:\n%s\n", i, cSegments[i]) - if i < len(pSegments)-1 { - if cSegments[i+1] != cSegments[i] { - same = false - t.Errorf("error: cSegments[%d] is different than cSegments[%d]!", i, i+1) - } - } - } - if !same { - t.Log(out) - } -} - -// TestFileHoleCopy creates a sparse times, copies it a few times, and check if -// the copies are the same (including the location of holes and data sections). -// -// The test runs with -plaintextnames because that makes it easier to manipulate -// cipherdir directly. -func TestFileHoleCopy(t *testing.T) { - // | 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" - c0 := []int64{10000, 30000} - if !t.Run("c0", func(t *testing.T) { doTestFileHoleCopy(t, name, c0) }) { - t.Log("aborting further subtests") - return - } - - rand.Seed(time.Now().UnixNano()) - for k := 0; k < 100; k++ { - c1 := make([]int64, 10) - for i := range c1 { - c1[i] = int64(rand.Int31n(60000)) - } - name := fmt.Sprintf("k%d", k) - if !t.Run(name, func(t *testing.T) { doTestFileHoleCopy(t, name, c1) }) { - t.Log("aborting further subtests") - return - } - } -} diff --git a/tests/plaintextnames/plaintextnames_test.go b/tests/plaintextnames/plaintextnames_test.go deleted file mode 100644 index e3cf953..0000000 --- a/tests/plaintextnames/plaintextnames_test.go +++ /dev/null @@ -1,127 +0,0 @@ -package plaintextnames - -// integration tests that target plaintextnames specifically - -import ( - "fmt" - "io/ioutil" - "os" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/internal/configfile" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -var cDir string -var pDir string - -var testPw = []byte("test") - -// Create and mount "-plaintextnames" fs -func TestMain(m *testing.M) { - cDir = test_helpers.InitFS(nil, "-plaintextnames") - pDir = cDir + ".mnt" - test_helpers.MountOrExit(cDir, pDir, "-extpass", "echo test") - r := m.Run() - test_helpers.UnmountPanic(pDir) - os.Exit(r) -} - -// Only the PlaintextNames feature flag should be set -func TestFlags(t *testing.T) { - _, cf, err := configfile.LoadAndDecrypt(cDir+"/gocryptfs.conf", testPw) - if err != nil { - t.Fatal(err) - } - if !cf.IsFeatureFlagSet(configfile.FlagPlaintextNames) { - t.Error("PlaintextNames flag should be set but isn't") - } - if cf.IsFeatureFlagSet(configfile.FlagEMENames) || cf.IsFeatureFlagSet(configfile.FlagDirIV) { - t.Error("FlagEMENames and FlagDirIV should be not set") - } -} - -// gocryptfs.diriv should NOT be created -func TestDirIV(t *testing.T) { - _, err := os.Stat(cDir + "/gocryptfs.diriv") - if err == nil { - t.Errorf("gocryptfs.diriv should not be created in the top directory") - } - err = os.Mkdir(pDir+"/dir1", 0777) - if err != nil { - t.Error(err) - } - _, err = os.Stat(pDir + "/dir1/gocryptfs.diriv") - if err == nil { - t.Errorf("gocryptfs.diriv should not be created in a subdirectory") - } -} - -// With "-plaintextnames", the name "/gocryptfs.conf" is reserved, but everything -// else should work. -func TestFiltered(t *testing.T) { - filteredFile := pDir + "/gocryptfs.conf" - err := ioutil.WriteFile(filteredFile, []byte("foo"), 0777) - if err == nil { - t.Errorf("should have failed but didn't") - } - err = os.Remove(filteredFile) - if err == nil { - t.Errorf("should have failed but didn't") - } - err = ioutil.WriteFile(pDir+"/gocryptfs.diriv", []byte("foo"), 0777) - if err != nil { - t.Error(err) - } - subDir, err := ioutil.TempDir(pDir, "") - if err != nil { - t.Fatal(err) - } - fd, err := os.Create(subDir + "/gocryptfs.conf") - if err != nil { - t.Error(err) - } else { - fd.Close() - } -} - -// TestInoReuseEvil makes it appear that a directory and a file share the -// same inode number. -// Only works on filesystems that recycle inode numbers (ext4 does), -// and then the test causes a hang with these messages: -// -// go-fuse: blocked for 5 seconds waiting for FORGET on i4329366 -// go-fuse: blocked for 11 seconds waiting for FORGET on i4329366 -// go-fuse: blocked for 17 seconds waiting for FORGET on i4329366 -// [...] -// -// The test runs with -plaintextnames because that makes it easier to manipulate -// cipherdir directly. -func TestInoReuseEvil(t *testing.T) { - for i := 0; i < 2; i++ { - n := fmt.Sprintf("%s.%d", t.Name(), i) - pPath := pDir + "/" + n - cPath := cDir + "/" + n - if err := syscall.Mkdir(pPath, 0700); err != nil { - t.Fatal(err) - } - var st syscall.Stat_t - syscall.Stat(pPath, &st) - t.Logf("dir ino = %d", st.Ino) - // delete the dir "behind our back" - if err := syscall.Rmdir(cPath); err != nil { - t.Fatal(err) - } - // create a new file that will likely get the same inode number - pPath2 := pPath + "2" - fd, err := syscall.Creat(pPath2, 0600) - if err != nil { - t.Fatal(err) - } - defer syscall.Close(fd) - syscall.Fstat(fd, &st) - t.Logf("file ino = %d", st.Ino) - } -} diff --git a/tests/reverse/correctness_test.go b/tests/reverse/correctness_test.go deleted file mode 100644 index 9d7a1c8..0000000 --- a/tests/reverse/correctness_test.go +++ /dev/null @@ -1,297 +0,0 @@ -package reverse_test - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "syscall" - "testing" - - "golang.org/x/sys/unix" - - "github.com/rfjakob/gocryptfs/ctlsock" - "github.com/rfjakob/gocryptfs/internal/syscallcompat" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -// TestLongnameStat checks that file names of all sizes (1 to 255) show up in -// the decrypted reverse view (dirC, mounted in TestMain). -func TestLongnameStat(t *testing.T) { - for i := 1; i <= 255; i++ { - name := string(bytes.Repeat([]byte("x"), i)) - fd, err := os.Create(dirA + "/" + name) - if err != nil { - t.Fatal(err) - } - fd.Close() - path := dirC + "/" + name - if !test_helpers.VerifyExistence(t, path) { - t.Fatalf("failed to verify %q", path) - } - test_helpers.VerifySize(t, path, 0) - // A large number of longname files is a performance problem in - // reverse mode. Move the file out of the way once we are done with it - // to speed up the test (2 seconds -> 0.2 seconds). - // We do NOT unlink it because ext4 reuses inode numbers immediately, - // which will cause "Found linked inode, but Nlink == 1" warnings and - // file not found errors. - // TODO: This problem should be handled at the go-fuse level. - syscall.Rename(dirA+"/"+name, test_helpers.TmpDir+"/"+fmt.Sprintf("x%d", i)) - } -} - -func TestSymlinks(t *testing.T) { - target := "/" - os.Symlink(target, dirA+"/symlink") - cSymlink := dirC + "/symlink" - _, err := os.Lstat(cSymlink) - if err != nil { - t.Errorf("Lstat: %v", err) - } - _, err = os.Stat(cSymlink) - if err != nil { - t.Errorf("Stat: %v", err) - } - actualTarget, err := os.Readlink(cSymlink) - if err != nil { - t.Fatal(err) - } - if target != actualTarget { - t.Errorf("wrong symlink target: want=%q have=%q", target, actualTarget) - } -} - -// Symbolic link dentry sizes should be set to the length of the string -// that contains the target path. -func TestSymlinkDentrySize(t *testing.T) { - if plaintextnames { - t.Skip("this only tests encrypted names") - } - symlink := "a_symlink" - - mnt, err := ioutil.TempDir(test_helpers.TmpDir, "reverse_mnt_") - if err != nil { - t.Fatal(err) - } - - sock := mnt + ".sock" - test_helpers.MountOrFatal(t, "ctlsock_reverse_test_fs", mnt, "-reverse", "-extpass", "echo test", "-ctlsock="+sock) - defer test_helpers.UnmountPanic(mnt) - - req := ctlsock.RequestStruct{EncryptPath: symlink} - symlinkResponse := test_helpers.QueryCtlSock(t, sock, req) - if symlinkResponse.ErrNo != 0 { - t.Errorf("Encrypt: %q ErrNo=%d ErrText=%s", symlink, symlinkResponse.ErrNo, symlinkResponse.ErrText) - } - - fi, err := os.Lstat(mnt + "/" + symlinkResponse.Result) - if err != nil { - t.Fatalf("Lstat: %v", err) - } - - target, err := os.Readlink(mnt + "/" + symlinkResponse.Result) - if err != nil { - t.Errorf("Readlink: %v", err) - } - - if fi.Size() != int64(len(target)) { - t.Errorf("Lstat reports that symbolic link %q's dentry size is %d, but this does not "+ - "match the length of the string returned by readlink, which is %d.", - symlink, fi.Size(), len(target)) - } -} - -// .gocryptfs.reverse.conf in the plaintext dir should be visible as -// gocryptfs.conf -func TestConfigMapping(t *testing.T) { - c := dirB + "/gocryptfs.conf" - if !test_helpers.VerifyExistence(t, c) { - t.Errorf("%s missing", c) - } - data, err := ioutil.ReadFile(c) - if err != nil { - t.Fatal(err) - } - if len(data) == 0 { - t.Errorf("empty file") - } -} - -// Check that the access() syscall works on virtual files -func TestAccessVirtual(t *testing.T) { - if plaintextnames { - t.Skip("test makes no sense for plaintextnames") - } - var R_OK uint32 = 4 - var W_OK uint32 = 2 - var X_OK uint32 = 1 - fn := dirB + "/gocryptfs.diriv" - err := syscall.Access(fn, R_OK) - if err != nil { - t.Errorf("%q should be readable, but got error: %v", fn, err) - } - err = syscall.Access(fn, W_OK) - if err == nil { - t.Errorf("should NOT be writeable") - } - err = syscall.Access(fn, X_OK) - if err == nil { - t.Errorf("should NOT be executable") - } -} - -// Check that the access() syscall works on regular files -func TestAccess(t *testing.T) { - f, err := os.Create(dirA + "/testaccess1") - if err != nil { - t.Fatal(err) - } - f.Close() - f, err = os.Open(dirB) - if err != nil { - t.Fatal(err) - } - defer f.Close() - names, err := f.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - for _, n := range names { - // Check if file exists - this should never fail - err = syscallcompat.Faccessat(unix.AT_FDCWD, dirB+"/"+n, unix.F_OK) - if err != nil { - t.Errorf("%s: %v", n, err) - } - // Check if file is readable - err = syscallcompat.Faccessat(unix.AT_FDCWD, dirB+"/"+n, unix.R_OK) - if err != nil { - t.Logf("%s: %v", n, err) - } - } -} - -// Opening a nonexistent file name should return ENOENT -// and not EBADMSG or EIO or anything else. -func TestEnoent(t *testing.T) { - fn := dirB + "/TestEnoent" - _, err := syscall.Open(fn, syscall.O_RDONLY, 0) - if err != syscall.ENOENT { - t.Errorf("want ENOENT, got: %v", err) - } -} - -// If the symlink target gets too long due to base64 encoding, we should -// return ENAMETOOLONG instead of having the kernel reject the data and -// returning an I/O error to the user. -// https://github.com/rfjakob/gocryptfs/issues/167 -func TestTooLongSymlink(t *testing.T) { - var err error - var l int - fn := dirA + "/TooLongSymlink" - // Try 4000 first (works on ext4 and tmpfs), then retry with 1000 (XFS and - // Darwin have a limit of about 1024) - for _, l = range []int{4000, 1000} { - target := string(bytes.Repeat([]byte("x"), l)) - err = os.Symlink(target, fn) - if err == nil { - break - } - } - if err != nil { - t.Fatal(err) - } - // save later tests the trouble of dealing with ENAMETOOLONG errors - defer func() { - os.Remove(fn) - // immediately create a new symlink so the inode number is not - // reused for something else - os.Symlink("/tmp", fn) - }() - t.Logf("Created symlink of length %d", l) - _, err = os.Readlink(dirC + "/TooLongSymlink") - if err == nil { - return - } - err2 := err.(*os.PathError) - if err2.Err != syscall.ENAMETOOLONG { - t.Errorf("Expected %q error, got %q instead", syscall.ENAMETOOLONG, - err2.Err) - } -} - -// Test that we can traverse a directory with 0100 permissions -// (execute but no read). This used to be a problem as OpenDirNofollow opened -// all directories in the path with O_RDONLY. Now it uses O_PATH, which only needs -// the executable bit. -func Test0100Dir(t *testing.T) { - dir := dirA + "/" + t.Name() - err := os.Mkdir(dir, 0700) - if err != nil { - t.Fatal(err) - } - file := dir + "/hello" - err = ioutil.WriteFile(file, []byte("hello"), 0600) - if err != nil { - t.Fatal(err) - } - err = os.Chmod(dir, 0100) - if err != nil { - t.Fatal(err) - } - - fileReverse := dirC + "/" + t.Name() + "/hello" - fd, err := os.Open(fileReverse) - // Make sure the dir can be removed after the test is done - os.Chmod(dir, 0700) - if err != nil { - t.Fatal(err) - } - fd.Close() -} - -func TestStatfs(t *testing.T) { - var st syscall.Statfs_t - syscall.Statfs(dirB, &st) - if st.Bsize == 0 { - t.Errorf("statfs reports size zero: %#v", st) - } -} - -// TestSeekData tests that fs.FileLseeker is implemented -func TestSeekData(t *testing.T) { - if !plaintextnames { - t.Skip() - } - - fn := filepath.Join(dirA, t.Name()) - f, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - var oneTiB int64 = 1024 * 1024 * 1024 * 1024 - if _, err = f.Seek(oneTiB, 0); err != nil { - t.Fatal(err) - } - if _, err = f.Write([]byte("foo")); err != nil { - t.Fatal(err) - } - f.Close() - - const SEEK_DATA = 3 - - fn2 := filepath.Join(dirB, t.Name()) - f, err = os.Open(fn2) - if err != nil { - t.Fatal(err) - } - off, err := f.Seek(1024*1024, SEEK_DATA) - if err != nil { - t.Fatal(err) - } - if off < oneTiB-1024*1024 { - t.Errorf("off=%d, expected=%d\n", off, oneTiB) - } - f.Close() -} diff --git a/tests/reverse/ctlsock_reverse_test_fs/.gocryptfs.reverse.conf b/tests/reverse/ctlsock_reverse_test_fs/.gocryptfs.reverse.conf deleted file mode 100644 index 9bd9259..0000000 --- a/tests/reverse/ctlsock_reverse_test_fs/.gocryptfs.reverse.conf +++ /dev/null @@ -1,19 +0,0 @@ -{ - "Creator": "gocryptfs v1.1.1-16-g75ebb28-dirty", - "EncryptedKey": "cE5bhCZl1iL1tn0dNLh8aQy8n55NMbojmmkwM8iM8/y0uChO0CGaK16sNHffAKJ++qH287JlCpk/BFyi", - "ScryptObject": { - "Salt": "yUxEmtl4KeUkCxL8b6aYcEGVtFe2NAlwy0WsFLt8p+Y=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "DirIV", - "EMENames", - "LongNames", - "AESSIV" - ] -} diff --git a/tests/reverse/ctlsock_reverse_test_fs/a_symlink b/tests/reverse/ctlsock_reverse_test_fs/a_symlink deleted file mode 120000 index 06ed148..0000000 --- a/tests/reverse/ctlsock_reverse_test_fs/a_symlink +++ /dev/null @@ -1 +0,0 @@ -dir/dir/file \ No newline at end of file diff --git a/tests/reverse/ctlsock_reverse_test_fs/dir/dir/file b/tests/reverse/ctlsock_reverse_test_fs/dir/dir/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/ctlsock_reverse_test_fs/dir/file b/tests/reverse/ctlsock_reverse_test_fs/dir/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/ctlsock_reverse_test_fs/dir/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/ctlsock_reverse_test_fs/dir/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/ctlsock_reverse_test_fs/file b/tests/reverse/ctlsock_reverse_test_fs/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/ctlsock_reverse_test_fs/longdir.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file b/tests/reverse/ctlsock_reverse_test_fs/longdir.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/ctlsock_reverse_test_fs/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/ctlsock_reverse_test_fs/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/ctlsock_test.go b/tests/reverse/ctlsock_test.go deleted file mode 100644 index ecb0b96..0000000 --- a/tests/reverse/ctlsock_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package reverse_test - -import ( - "io/ioutil" - "syscall" - "testing" - - "github.com/rfjakob/gocryptfs/ctlsock" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -var ctlSockTestCases = [][]string{ - {"4RQq1dJlfvQPaVU5Xypf0w==", "file"}, - {"gocryptfs.longname.ZQCAoi5li3xvDZRO8McBV0L_kzJc4IcAOEzuW-2S1Y4=", "longfile." + x240}, - {"v6puXntoQOk7Mhl8zJ4Idg==", "dir"}, - {"v6puXntoQOk7Mhl8zJ4Idg==/UVy2gV0RQTUC8AE4wYoMwg==", "dir/file"}, - {"v6puXntoQOk7Mhl8zJ4Idg==/fvHFLHlxHCQ7EpVMJu0AZg==", "dir/dir"}, - {"v6puXntoQOk7Mhl8zJ4Idg==/fvHFLHlxHCQ7EpVMJu0AZg==/_4uudIGniACke55JoDsqDA==", "dir/dir/dir"}, - {"v6puXntoQOk7Mhl8zJ4Idg==/fvHFLHlxHCQ7EpVMJu0AZg==/QvPahkkeVRKTw2kdZFZxwQ==", "dir/dir/file"}, - {"v6puXntoQOk7Mhl8zJ4Idg==/gocryptfs.longname.y6rxCn6Id8hIZL2t_STpdLZpu-aE2HpprJR25xD60mk=", "dir/longfile." + x240}, - {"gocryptfs.longname.cvRximo1ATRJVEzw_V9MZieHFlod9y2iv2Sug1kbiTE=", "longdir." + x240}, - {"gocryptfs.longname.cvRximo1ATRJVEzw_V9MZieHFlod9y2iv2Sug1kbiTE=/-LMdFgFt6UxO-z5iJvuC9w==", "longdir." + x240 + "/dir"}, - {"gocryptfs.longname.cvRximo1ATRJVEzw_V9MZieHFlod9y2iv2Sug1kbiTE=/rBPJYAzcHWLdPj1T8kgh8A==", "longdir." + x240 + "/file"}, -} - -// Test DecryptPath and EncryptPath -func TestCtlSockPathOps(t *testing.T) { - if plaintextnames { - t.Skip("this only tests encrypted names") - } - mnt, err := ioutil.TempDir(test_helpers.TmpDir, "reverse_mnt_") - if err != nil { - t.Fatal(err) - } - sock := mnt + ".sock" - test_helpers.MountOrFatal(t, "ctlsock_reverse_test_fs", mnt, "-reverse", "-extpass", "echo test", "-ctlsock="+sock) - defer test_helpers.UnmountPanic(mnt) - var req ctlsock.RequestStruct - var response ctlsock.ResponseStruct - for i, tc := range ctlSockTestCases { - // Decrypt - req = ctlsock.RequestStruct{DecryptPath: tc[0]} - response = test_helpers.QueryCtlSock(t, sock, req) - if response.ErrNo != 0 { - t.Errorf("Testcase %d Decrypt: %q ErrNo=%d ErrText=%s", i, tc[0], response.ErrNo, response.ErrText) - } else if response.Result != tc[1] { - t.Errorf("Testcase %d Decrypt: Want %q got %q", i, tc[1], response.Result) - } - // Encrypt - req = ctlsock.RequestStruct{EncryptPath: tc[1]} - response = test_helpers.QueryCtlSock(t, sock, req) - if response.ErrNo != 0 { - t.Errorf("Testcase %d Encrypt: %q ErrNo=%d ErrText=%s", i, tc[0], response.ErrNo, response.ErrText) - } else if response.Result != tc[0] { - t.Errorf("Testcase %d Encrypt: Want %q got %q", i, tc[1], response.Result) - } - } - // At this point the longname parent cache should be populated. - // Check that we do not mix up information for different directories. - req = ctlsock.RequestStruct{DecryptPath: "gocryptfs.longname.y6rxCn6Id8hIZL2t_STpdLZpu-aE2HpprJR25xD60mk="} - response = test_helpers.QueryCtlSock(t, sock, req) - if response.ErrNo != int32(syscall.ENOENT) { - t.Errorf("File should not exist: ErrNo=%d ErrText=%s", response.ErrNo, response.ErrText) - } - req = ctlsock.RequestStruct{DecryptPath: "v6puXntoQOk7Mhl8zJ4Idg==/gocryptfs.longname.ZQCAoi5li3xvDZRO8McBV0L_kzJc4IcAOEzuW-2S1Y4="} - response = test_helpers.QueryCtlSock(t, sock, req) - if response.ErrNo != int32(syscall.ENOENT) { - t.Errorf("File should not exist: ErrNo=%d ErrText=%s", response.ErrNo, response.ErrText) - } -} - -// We should not panic when somebody feeds requests that make no sense -func TestCtlSockCrash(t *testing.T) { - if plaintextnames { - t.Skip("this only tests encrypted names") - } - mnt, err := ioutil.TempDir(test_helpers.TmpDir, "reverse_mnt_") - if err != nil { - t.Fatal(err) - } - sock := mnt + ".sock" - test_helpers.MountOrFatal(t, "ctlsock_reverse_test_fs", mnt, "-reverse", "-extpass", "echo test", "-ctlsock="+sock, - "-wpanic=0", "-nosyslog=0") - defer test_helpers.UnmountPanic(mnt) - // Try to crash it - req := ctlsock.RequestStruct{DecryptPath: "gocryptfs.longname.XXX_TestCtlSockCrash_XXX.name"} - test_helpers.QueryCtlSock(t, sock, req) -} diff --git a/tests/reverse/exclude_test.go b/tests/reverse/exclude_test.go deleted file mode 100644 index c493d95..0000000 --- a/tests/reverse/exclude_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package reverse_test - -import ( - "io/ioutil" - "path/filepath" - "testing" - - "github.com/rfjakob/gocryptfs/ctlsock" - "github.com/rfjakob/gocryptfs/internal/nametransform" - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -const xxx = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - -/* -tree exclude_test_fs -exclude_test_fs/ -├── bkp1~ -├── dir1 -│ ├── file1 -│ ├── file2 -│ ├── exclude -│ ├── longbkp1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx~ -│ ├── longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ ├── longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ ├── longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ └── subdir1 -│ ├── exclude -│ └── subdir2 -│ └── exclude -├── dir2 -│ ├── file -│ ├── longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ │ └── file -│ ├── longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ └── subdir -│ └── file -├── file1 -├── file2 -├── longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ └── file1 -├── longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -│ ├── bkp~ -│ └── file -├── longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -├── longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -└── longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -*/ - -func ctlsockEncryptPath(t *testing.T, sock string, path string) string { - req := ctlsock.RequestStruct{EncryptPath: path} - response := test_helpers.QueryCtlSock(t, sock, req) - if response.ErrNo != 0 { - t.Fatal(response) - } - return response.Result -} - -func testExclude(t *testing.T, flag string) { - pPatterns := []string{ - "file1", // matches file1 anywhere - "!longdir1" + xxx + "/file1", // ! includes an otherwise file - "file2/", // a trailing slash matches only a directory - "dir1/file2", // matches file2 inside dir1 anywhere - "#file2", // comments are ignored - "dir2", // excludes the whole directory - "longfile2" + xxx, // matches longfile2 anywhere - "/longfile3" + xxx, // a leading / anchors the match at the root - "*~", // wildcards are supported - "dir1/**/exclude", // ** matches any number of directories - } - pOk := []string{ - "file2", - "dir1/longfile1" + xxx, - "dir1/longfile3" + xxx, - "longdir1" + xxx, - "longdir1" + xxx + "/file1", - "longdir2" + xxx + "/file", - "longfile1" + xxx, - } - pExclude := []string{ - "bkp1~", - "dir1/file1", - "dir1/file2", - "dir1/exclude", - "dir1/longbkp1" + xxx + "~", - "dir1/longfile2" + xxx, - "dir1/subdir1/exclude", - "dir1/subdir1/subdir2/exclude", - "dir2", - "dir2/file", - "dir2/longdir1" + xxx + "/file", - "dir2/longfile." + xxx, - "dir2/subdir", - "dir2/subdir/file", - "file1", - "longdir2" + xxx + "/bkp~", - "longfile2" + xxx, - "longfile3" + xxx, - } - // Mount reverse fs - mnt, err := ioutil.TempDir(test_helpers.TmpDir, "TestExclude") - if err != nil { - t.Fatal(err) - } - sock := mnt + ".sock" - cliArgs := []string{"-reverse", "-extpass", "echo test", "-ctlsock", sock} - for _, v := range pPatterns { - cliArgs = append(cliArgs, flag, v) - } - if plaintextnames { - cliArgs = append(cliArgs, "-config", "exclude_test_fs/.gocryptfs.reverse.conf.plaintextnames") - } - test_helpers.MountOrFatal(t, "exclude_test_fs", mnt, cliArgs...) - defer test_helpers.UnmountPanic(mnt) - // Get encrypted version of "ok" and "excluded" paths - cOk := encryptExcludeTestPaths(t, sock, pOk) - cExclude := encryptExcludeTestPaths(t, sock, pExclude) - // Check that "excluded" paths are not there and "ok" paths are there - for _, v := range cExclude { - t.Logf("File %q should be invisible", v) - if test_helpers.VerifyExistence(t, mnt+"/"+v) { - t.Errorf("File %q is visible, but should be excluded", v) - } - if nametransform.IsLongContent(filepath.Base(v)) { - - } - } - for _, v := range cOk { - t.Logf("File %q should be visible", v) - if !test_helpers.VerifyExistence(t, mnt+"/"+v) { - t.Errorf("File %q is hidden, but should be visible", v) - } - } -} - -// encryptExcludeTestPaths is used by testExclude() to encrypt the lists of -// testcase paths -func encryptExcludeTestPaths(t *testing.T, socket string, pRelPaths []string) (out []string) { - for _, pRelPath := range pRelPaths { - cRelPath := ctlsockEncryptPath(t, socket, pRelPath) - out = append(out, cRelPath) - if !plaintextnames && nametransform.IsLongContent(filepath.Base(cRelPath)) { - // If we exclude - // gocryptfs.longname.3vZ_r3eDPb1_fL3j5VA4rd_bcKWLKT9eaxOVIGK5HFA - // we should also exclude - // gocryptfs.longname.3vZ_r3eDPb1_fL3j5VA4rd_bcKWLKT9eaxOVIGK5HFA.name - out = append(out, cRelPath+nametransform.LongNameSuffix) - } - } - return out -} - -func TestExclude(t *testing.T) { - testExclude(t, "-exclude-wildcard") - testExclude(t, "-ew") -} diff --git a/tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf b/tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf deleted file mode 100644 index 835d11c..0000000 --- a/tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf +++ /dev/null @@ -1,21 +0,0 @@ -{ - "Creator": "gocryptfs v1.5-41-gf48b731-dirty", - "EncryptedKey": "FkACqloUeFZesem0UzRD3ezLXtPl8wIAxEHoIEfZxFdLMQeWOxqtw5xopJagDWE/GI1VFSUIrJIIIwwgMipmYA==", - "ScryptObject": { - "Salt": "UVfIgV31uj/voHWI4GqGwsTcbVKyYDOWvbleqJKhZbk=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "HKDF", - "DirIV", - "EMENames", - "LongNames", - "Raw64", - "AESSIV" - ] -} diff --git a/tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf.plaintextnames b/tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf.plaintextnames deleted file mode 100644 index 9cb762c..0000000 --- a/tests/reverse/exclude_test_fs/.gocryptfs.reverse.conf.plaintextnames +++ /dev/null @@ -1,18 +0,0 @@ -{ - "Creator": "gocryptfs v1.5-41-gf48b731-dirty", - "EncryptedKey": "wAmckZb7QsIv/GCdkhb5ep8TwJa44qhnswn5tbER6Tifk8TbUmkwBTceaTtYfHAnTQ48q9mnIlcN9cfbNe5oPw==", - "ScryptObject": { - "Salt": "o5XJ78TgG85zZXRnU55ZqHhKLbPge6jsyDiqrLvSqe0=", - "N": 1024, - "R": 8, - "P": 1, - "KeyLen": 32 - }, - "Version": 2, - "FeatureFlags": [ - "GCMIV128", - "HKDF", - "PlaintextNames", - "AESSIV" - ] -} diff --git a/tests/reverse/exclude_test_fs/bkp1~ b/tests/reverse/exclude_test_fs/bkp1~ deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/exclude b/tests/reverse/exclude_test_fs/dir1/exclude deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/file1 b/tests/reverse/exclude_test_fs/dir1/file1 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/file2 b/tests/reverse/exclude_test_fs/dir1/file2 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/longbkp1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx~ b/tests/reverse/exclude_test_fs/dir1/longbkp1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx~ deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/dir1/longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/dir1/longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/dir1/longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/subdir1/exclude b/tests/reverse/exclude_test_fs/dir1/subdir1/exclude deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir1/subdir1/subdir2/exclude b/tests/reverse/exclude_test_fs/dir1/subdir1/subdir2/exclude deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir2/file b/tests/reverse/exclude_test_fs/dir2/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir2/longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file b/tests/reverse/exclude_test_fs/dir2/longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir2/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/dir2/longfile.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/dir2/subdir/file b/tests/reverse/exclude_test_fs/dir2/subdir/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/file1 b/tests/reverse/exclude_test_fs/file1 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/file2 b/tests/reverse/exclude_test_fs/file2 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file1 b/tests/reverse/exclude_test_fs/longdir1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file1 deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/bkp~ b/tests/reverse/exclude_test_fs/longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/bkp~ deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file b/tests/reverse/exclude_test_fs/longdir2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/file deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/longfile1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/longfile2xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/exclude_test_fs/longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx b/tests/reverse/exclude_test_fs/longfile3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted file mode 100644 index e69de29..0000000 diff --git a/tests/reverse/inomap_test.go b/tests/reverse/inomap_test.go deleted file mode 100644 index e6fc525..0000000 --- a/tests/reverse/inomap_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package reverse_test - -import ( - "bytes" - "os" - "strings" - "syscall" - "testing" -) - -// findIno looks for the file having inode number `ino` in `dir`. -// Returns "" if not found. -func findIno(dir string, ino uint64) string { - fd, err := os.Open(dir) - if err != nil { - return "" - } - dirents, err := fd.Readdirnames(0) - if err != nil { - return "" - } - fd.Close() - for _, entry := range dirents { - var st syscall.Stat_t - err = syscall.Lstat(dir+"/"+entry, &st) - if err != nil { - continue - } - if ino == st.Ino { - return entry - } - } - return "" -} - -// TestVirtualFileIno creates a directory tree like this: -// -// TestVirtualFileIno <---- parent -// └── xxxxxxx[...] <---- child -// -// Which looks like this encrypted: -// -// OLUKdPMg6l87EiKVlufgwIkQL8MD6JdUgOR3a8nEZ-w <---- parent -// ├── gocryptfs.diriv <---- diriv -// ├── gocryptfs.longname.e31v1ax4h_F0l4jhlN8kCjaWWMq8rO9VVBZ15IYsV50 <---- child -// └── gocryptfs.longname.e31v1ax4h_F0l4jhlN8kCjaWWMq8rO9VVBZ15IYsV50.name <---- name -// -// It verifies that the inode numbers match what we expect. -func TestVirtualFileIno(t *testing.T) { - if plaintextnames { - t.Skip("plaintextnames mode does not have virtual files") - } - - type inoTable struct { - parent uint64 - diriv uint64 - child uint64 - name uint64 - } - var origInos inoTable - var cipherInos inoTable - - parent := dirA + "/TestVirtualFileIno" - name := string(bytes.Repeat([]byte("x"), 240)) - err := os.MkdirAll(parent+"/"+name, 0700) - if err != nil { - t.Fatal(err) - } - var st syscall.Stat_t - err = syscall.Lstat(parent+"/"+name, &st) - if err != nil { - t.Fatal(err) - } - origInos.child = st.Ino - // get inode number of plain parent - err = syscall.Lstat(parent, &st) - if err != nil { - t.Fatal(err) - } - origInos.parent = st.Ino - // find it in encrypted `dirB` - fd, err := os.Open(dirB) - if err != nil { - t.Fatal(err) - } - dirents, err := fd.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - fd.Close() - encryptedParent := findIno(dirB, origInos.parent) - if encryptedParent == "" { - t.Fatalf("could not find ino %d in %q", origInos.parent, dirB) - } - encryptedParent = dirB + "/" + encryptedParent - err = syscall.Stat(encryptedParent, &st) - if err != nil { - t.Fatal(err) - } - cipherInos.parent = st.Ino - fd, err = os.Open(encryptedParent) - if err != nil { - t.Fatal(err) - } - dirents, err = fd.Readdirnames(0) - if err != nil { - t.Fatal(err) - } - fd.Close() - for _, entry := range dirents { - var st2 syscall.Stat_t - err = syscall.Lstat(encryptedParent+"/"+entry, &st2) - if err != nil { - t.Errorf("stat %q: %v", entry, err) - continue - } - if entry == "gocryptfs.diriv" { - cipherInos.diriv = st2.Ino - } else if strings.HasSuffix(entry, ".name") { - cipherInos.name = st2.Ino - } else { - cipherInos.child = st2.Ino - } - } - if origInos.parent != cipherInos.parent { - t.Errorf("parent ino mismatch: %d != %d", origInos.parent, cipherInos.parent) - } - if origInos.parent == cipherInos.diriv { - t.Errorf("diriv ino collision: %d == %d", origInos.parent, cipherInos.diriv) - } - // Lower 48 bits should come from the backing file - const mask = 0xffffffffffff - if origInos.parent&mask != cipherInos.diriv&mask { - t.Errorf("diriv ino mismatch: %#x vs %#x", origInos.parent, cipherInos.diriv) - } - if origInos.child != cipherInos.child { - t.Errorf("child ino mismatch: %d vs %d", origInos.child, cipherInos.child) - } - 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) - } -} diff --git a/tests/reverse/linux-tarball-test.bash b/tests/reverse/linux-tarball-test.bash deleted file mode 100755 index 26bbb6c..0000000 --- a/tests/reverse/linux-tarball-test.bash +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -eu - -cd "$(dirname "$0")" -MD5="$PWD/../stress_tests/linux-3.0.md5sums" -MYNAME=$(basename "$0") -source ../fuse-unmount.bash - -# Setup dirs -../dl-linux-tarball.bash -cd /tmp -WD=$(mktemp -d /tmp/$MYNAME.XXX) - -# Cleanup trap -trap "set +u; cd /; fuse-unmount -z $WD/c; fuse-unmount -z $WD/b; rm -rf $WD" EXIT - -cd $WD -mkdir a b c -echo "Extracting tarball" -tar -x -f /tmp/linux-3.0.tar.gz -C a -echo "Mounting a -> b -> c chain" -# Init "a" -gocryptfs -q -extpass="echo test" -reverse -init -scryptn=10 a -# Reverse-mount "a" on "b" -gocryptfs -q -extpass="echo test" -reverse a b -# Forward-mount "b" on "c" -gocryptfs -q -extpass="echo test" b c -# Check md5 sums -cd c -echo "Checking md5 sums" -set -o pipefail -md5sum -c $MD5 | pv -l -s 36782 -N "files checked" | (grep -v ": OK" || true) diff --git a/tests/reverse/longname_perf_test.go b/tests/reverse/longname_perf_test.go deleted file mode 100644 index 1707cea..0000000 --- a/tests/reverse/longname_perf_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package reverse_test - -import ( - "fmt" - "os" - "testing" -) - -func genName(i int, postfix string) string { - return fmt.Sprintf("%04d.%s", i, postfix) -} - -// Create 10000 files with long names -func generateLongnameFiles(dir string) { - for i := 0; i < 100000; i++ { - n := genName(i, x240) - f, err := os.Create(dir + "/" + n) - if err != nil { - panic(err) - } - f.Close() - } -} - -func BenchmarkLongnameStat(b *testing.B) { - // Setup - generateLongnameFiles(dirA) - dirFd, err := os.Open(dirB) - if err != nil { - b.Fatal(err) - } - encryptedNames, err := dirFd.Readdirnames(-1) - if err != nil { - b.Fatal(err) - } - l := len(encryptedNames) - dirFd.Close() - // Benchmark - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := os.Stat(dirB + "/" + encryptedNames[i%l]) - if err != nil { - b.Fatal(err) - } - } - // Cleanup - b.StopTimer() - os.RemoveAll(dirA) - os.Mkdir(dirA, 0700) -} diff --git a/tests/reverse/main_test.go b/tests/reverse/main_test.go deleted file mode 100644 index 3425289..0000000 --- a/tests/reverse/main_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package reverse_test - -import ( - "bytes" - "os" - "testing" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -var x240 = string(bytes.Repeat([]byte("x"), 240)) -var plaintextnames bool - -// dirA is a normal directory -var dirA string - -// dirB is the reverse mount backed by dirA -var dirB string - -// dirC is a forward mount backed by dirB -var dirC string - -// Create directory "dirA", mount it reverse to "dirB", mount it forward -// to "dirC". -func TestMain(m *testing.M) { - var r int - for _, plaintextnames = range []bool{false, true} { - argsA := []string{"-reverse"} - if plaintextnames { - argsA = append(argsA, "-plaintextnames") - } - dirA = test_helpers.InitFS(nil, argsA...) - dirB = test_helpers.TmpDir + "/b" - dirC = test_helpers.TmpDir + "/c" - if err := os.Mkdir(dirB, 0700); err != nil { - panic(err) - } - if err := os.Mkdir(dirC, 0700); err != nil { - panic(err) - } - test_helpers.MountOrExit(dirA, dirB, "-reverse", "-extpass", "echo test") - test_helpers.MountOrExit(dirB, dirC, "-extpass", "echo test") - r = m.Run() - test_helpers.UnmountPanic(dirC) - test_helpers.UnmountPanic(dirB) - - os.RemoveAll(dirA) - os.RemoveAll(dirB) - os.RemoveAll(dirC) - - if r != 0 { - os.Exit(r) - } - } - os.Exit(r) -} diff --git a/tests/reverse/xattr_test.go b/tests/reverse/xattr_test.go deleted file mode 100644 index 8002604..0000000 --- a/tests/reverse/xattr_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package reverse_test - -import ( - "fmt" - "io/ioutil" - "path/filepath" - "syscall" - "testing" - - "github.com/pkg/xattr" -) - -func xattrSupported(path string) bool { - _, err := xattr.LGet(path, "user.xattrSupported-dummy-value") - if err == nil { - return true - } - err2 := err.(*xattr.Error) - if err2.Err == syscall.EOPNOTSUPP { - return false - } - return true -} - -func TestXattrList(t *testing.T) { - t.Skip("TODO: not implemented yet in reverse mode") - - if !xattrSupported(dirA) { - t.Skip() - } - fnA := filepath.Join(dirA, t.Name()) - err := ioutil.WriteFile(fnA, nil, 0700) - if err != nil { - t.Fatalf("creating empty file failed: %v", err) - } - val := []byte("xxxxxxxxyyyyyyyyyyyyyyyzzzzzzzzzzzzz") - num := 20 - var namesA map[string]string - for i := 1; i <= num; i++ { - attr := fmt.Sprintf("user.TestXattrList.%02d", i) - err = xattr.LSet(fnA, attr, val) - if err != nil { - t.Fatal(err) - } - namesA[attr] = string(val) - } - fnC := filepath.Join(dirC, t.Name()) - tmp, err := xattr.LList(fnC) - if err != nil { - t.Fatal(err) - } - var namesC map[string]string - for _, n := range tmp { - namesC[n] = string(val) - } - if len(namesA) != len(namesC) { - t.Errorf("wrong number of names, want=%d have=%d", len(namesA), len(namesC)) - } - for i := range namesC { - valA := namesA[i] - valC := namesC[i] - if valC != valA { - t.Errorf("mismatch on attr %q: valA = %q, valC = %q", i, valA, valC) - } - } -} diff --git a/tests/root_test/root_test.go b/tests/root_test/root_test.go deleted file mode 100644 index 8547e4e..0000000 --- a/tests/root_test/root_test.go +++ /dev/null @@ -1,313 +0,0 @@ -// Package root_test contains tests that need root -// permissions to run -package root_test - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "sync" - "syscall" - "testing" - - "golang.org/x/sys/unix" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -func asUser(uid int, gid int, supplementaryGroups []int, f func() error) error { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - err := unix.Setgroups(supplementaryGroups) - if err != nil { - return err - } - defer func() { - err = unix.Setgroups(nil) - if err != nil { - panic(err) - } - }() - err = unix.Setregid(-1, gid) - if err != nil { - return err - } - defer func() { - err = unix.Setregid(-1, 0) - if err != nil { - panic(err) - } - }() - err = unix.Setreuid(-1, uid) - if err != nil { - return err - } - defer func() { - err = unix.Setreuid(-1, 0) - if err != nil { - panic(err) - } - }() - - ret := f() - - // Also reset the saved user id (suid) and saved group id (sgid) to prevent - // bizarre failures in later tests. - // - // Yes, the kernel checks that *all of them* match: - // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/fuse/dir.c?h=v5.12-rc2#n1193 - // - // How to check: - // ps -o tid,pid,euid,ruid,suid,egid,rgid,sgid,cmd -eL - err = unix.Setresuid(0, 0, 0) - if err != nil { - panic(err) - } - err = unix.Setresgid(0, 0, 0) - if err != nil { - panic(err) - } - - return ret -} - -func TestSupplementaryGroups(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("must run as root") - } - cDir := test_helpers.InitFS(t) - os.Chmod(cDir, 0755) - pDir := cDir + ".mnt" - test_helpers.MountOrFatal(t, cDir, pDir, "-allow_other", "-extpass=echo test") - defer test_helpers.UnmountPanic(pDir) - - // We need an unrestricted umask - syscall.Umask(0000) - - dir1 := pDir + "/dir1" - err := os.Mkdir(dir1, 0770) - if err != nil { - t.Fatal(err) - } - err = os.Chown(dir1, 0, 1234) - if err != nil { - t.Fatal(err) - } - - err = asUser(1235, 1235, []int{1234}, func() error { return os.Mkdir(dir1+"/dir2", 0700) }) - if err != nil { - t.Error(err) - } - - err = asUser(1235, 1235, []int{1234}, func() error { - f, err := os.Create(dir1 + "/file1") - if err == nil { - f.Close() - } - return err - }) - if err != nil { - t.Error(err) - } -} - -func writeTillFull(t *testing.T, path string) (int, syscall.Errno) { - runtime.LockOSThread() - defer runtime.UnlockOSThread() - - fd, err := syscall.Creat(path, 0600) - if err != nil { - return 0, err.(syscall.Errno) - } - defer syscall.Close(fd) - // Write in 100.000 byte-blocks, which is not aligend to the - // underlying block size - buf := make([]byte, 100000) - var sz int - for { - n, err := syscall.Write(fd, buf) - if err != nil { - return sz, err.(syscall.Errno) - } - sz += n - } - return sz, 0 -} - -// TestDiskFull needs root permissions because it creates a loop disk -func TestDiskFull(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("must run as root") - } - - // Create 10 MB file full of zeros - ext4img := filepath.Join(test_helpers.TmpDir, t.Name()+".ext4") - f, err := os.Create(ext4img) - if err != nil { - t.Fatal(err) - } - defer f.Close() - err = f.Truncate(10 * 1024 * 1024) - if err != nil { - t.Fatal(err) - } - - // Format as ext4 - cmd := exec.Command("mkfs.ext4", ext4img) - out, err := cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - t.Fatal(err) - } - - // Mount ext4 - ext4mnt := ext4img + ".mnt" - err = os.Mkdir(ext4mnt, 0600) - if err != nil { - t.Fatal(err) - } - cmd = exec.Command("mount", ext4img, ext4mnt) - out, err = cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - t.Fatal(err) - } - defer syscall.Unlink(ext4img) - defer syscall.Unmount(ext4mnt, 0) - - // gocryptfs -init - cipherdir := ext4mnt + "/a" - if err = os.Mkdir(cipherdir, 0600); err != nil { - t.Fatal(err) - } - cmd = exec.Command(test_helpers.GocryptfsBinary, "-q", "-init", "-extpass", "echo test", "-scryptn=10", cipherdir) - out, err = cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - t.Fatal(err) - } - - // Mount gocryptfs - mnt := ext4mnt + "/b" - test_helpers.MountOrFatal(t, cipherdir, mnt, "-extpass", "echo test") - defer test_helpers.UnmountPanic(mnt) - - // Write till we get ENOSPC - var err1, err2 error - var sz1, sz2 int - var wg sync.WaitGroup - wg.Add(2) - go func() { - sz1, err1 = writeTillFull(t, mnt+"/foo1") - wg.Done() - }() - go func() { - sz2, err2 = writeTillFull(t, mnt+"/foo2") - wg.Done() - }() - wg.Wait() - if err1 != syscall.ENOSPC || err2 != syscall.ENOSPC { - t.Fatalf("err1=%v, err2=%v", err1, err2) - } - t.Logf("sz1=%d, sz2=%d", sz1, sz2) - - foo1, err := ioutil.ReadFile(mnt + "/foo1") - if err != nil { - t.Fatal(err) - } - if len(foo1) != sz1 { - t.Fail() - } - - foo2, err := ioutil.ReadFile(mnt + "/foo2") - if err != nil { - t.Fatal(err) - } - if len(foo2) != sz2 { - t.Fail() - } -} - -func TestAcl(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("must run as root") - } - cDir := test_helpers.InitFS(t) - os.Chmod(cDir, 0755) - pDir := cDir + ".mnt" - test_helpers.MountOrFatal(t, cDir, pDir, "-allow_other", "-acl", "-extpass=echo test") - defer test_helpers.UnmountPanic(pDir) - - f1 := pDir + "/f1" - if err := ioutil.WriteFile(f1, []byte("hello world\n"), 000); err != nil { - t.Fatal(err) - } - - openUser1234 := func(rwMode int) error { - return asUser(1234, 1234, nil, func() error { - fd, err := syscall.Open(f1, rwMode, 0) - if err != nil { - return err - } - defer syscall.Close(fd) - buf := make([]byte, 100) - if rwMode == syscall.O_RDONLY || rwMode == syscall.O_RDWR { - _, err = syscall.Read(fd, buf) - if err != nil { - return err - } - } - if rwMode == syscall.O_WRONLY || rwMode == syscall.O_RDWR { - _, err = syscall.Write(fd, buf) - if err != nil { - return err - } - } - return err - }) - } - - dumpAcl := func() { - out, err := exec.Command("getfacl", f1).CombinedOutput() - if err != nil { - t.Fatal(err) - } - t.Log(string(out)) - } - - if err := openUser1234(syscall.O_RDONLY); err == nil { - t.Error("this should have failed") - dumpAcl() - } - - // Allow read - out, err := exec.Command("setfacl", "-m", "u:1234:r", f1).CombinedOutput() - if err != nil { - t.Fatal(string(out)) - } - if err := openUser1234(syscall.O_RDONLY); err != nil { - t.Errorf("O_RDONLY should have worked, but got error: %v", err) - dumpAcl() - } - if err := openUser1234(syscall.O_WRONLY); err == nil { - t.Error("O_WRONLY should have failed") - dumpAcl() - } - - // Allow write - out, err = exec.Command("setfacl", "-m", "u:1234:w", f1).CombinedOutput() - if err != nil { - t.Fatal(string(out)) - } - if err := openUser1234(syscall.O_WRONLY); err != nil { - t.Errorf("O_WRONLY should have worked, but got error: %v", err) - dumpAcl() - } - if err := openUser1234(syscall.O_RDONLY); err == nil { - t.Error("O_RDONLY should have failed") - dumpAcl() - } -} diff --git a/tests/sharedstorage/sharedstorage_test.go b/tests/sharedstorage/sharedstorage_test.go deleted file mode 100644 index 8f46c0d..0000000 --- a/tests/sharedstorage/sharedstorage_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// test gocryptfs cipherdir mounted multiple times at the same time -package sharedstorage - -import ( - "fmt" - "os" - "testing" - "time" - - "golang.org/x/sys/unix" - - "github.com/rfjakob/gocryptfs/tests/test_helpers" -) - -var flagSharestorage bool - -// EntryTimeout is 1 second, give the kernel 1.1 second to actually -// expire an entry. The tests fail sometime with 1.0 second! -const waitForExpire = time.Second + 100*time.Millisecond - -func TestMain(m *testing.M) { - ret := 0 - flagSharestorage = false - ret += m.Run() - flagSharestorage = true - ret += m.Run() - os.Exit(ret) -} - -type testCase struct { - t *testing.T - - cipherdir string - mnt1 string - mnt2 string -} - -func newTestCase(t *testing.T) *testCase { - tc := testCase{} - tc.cipherdir = test_helpers.InitFS(t) - tc.mnt1 = tc.cipherdir + ".mnt1" - tc.mnt2 = tc.cipherdir + ".mnt2" - mountSharedstorage(t, tc.cipherdir, tc.mnt1) - mountSharedstorage(t, tc.cipherdir, tc.mnt2) - t.Logf("newTestCase: sharedstorage=%v cipherdir=%q", flagSharestorage, tc.cipherdir) - return &tc -} - -func (tc *testCase) cleanup() { - for _, mnt := range []string{tc.mnt1, tc.mnt2} { - err := test_helpers.UnmountErr(mnt) - if err != nil { - tc.t.Error(err) - } - } -} - -// mountSharedstorage mounts `cipherdir` on `mnt` with or without the -// `-sharedstorage` flag, depending on the global var `flagSharestorage`. -func mountSharedstorage(t *testing.T, cipherdir string, mnt string) { - args := []string{"-extpass=echo test"} - if flagSharestorage { - args = append(args, "-sharedstorage") - } - test_helpers.MountOrFatal(t, cipherdir, mnt, args...) -} - -func TestDirUnlink(t *testing.T) { - tc := newTestCase(t) - defer tc.cleanup() - - // Create dir via mnt1 - if err := unix.Mkdir(tc.mnt1+"/foo", 0700); err != nil { - t.Fatal(err) - } - // Replace dir with file via mnt2 - if err := unix.Rmdir(tc.mnt2 + "/foo"); err != nil { - t.Fatal(err) - } - if fd, err := unix.Creat(tc.mnt2+"/foo", 0600); err != nil { - t.Fatal(err) - } else { - unix.Close(fd) - } - // Try to unlink via mnt1 - if err := unix.Unlink(tc.mnt1 + "/foo"); err != nil { - // Must work with -sharedstorage - if flagSharestorage { - t.Fatal(err) - } else { - // Must always work after cache timeout - time.Sleep(waitForExpire) - if err := unix.Unlink(tc.mnt1 + "/foo"); err != nil { - t.Fatal(err) - } - } - } -} - -// TestStaleHardlinks always failed before -// https://review.gerrithub.io/c/hanwen/go-fuse/+/513646/2 -func TestStaleHardlinks(t *testing.T) { - tc := newTestCase(t) - defer tc.cleanup() - - link0 := tc.mnt1 + "/link0" - if fd, err := unix.Creat(link0, 0600); err != nil { - t.Fatal(err) - } else { - unix.Close(fd) - } - // Create hardlinks via mnt1 - for i := 1; i < 20; i++ { - linki := fmt.Sprintf(tc.mnt1+"/link%d", i) - if err := unix.Link(link0, linki); err != nil { - t.Fatal(err) - } - } - // Delete hardlinks via mnt2 - for i := 1; i < 20; i++ { - linki := fmt.Sprintf(tc.mnt2+"/link%d", i) - if err := unix.Unlink(linki); err != nil { - t.Fatal(err) - } - } - // Open link0 via mnt1 - fd, err := unix.Open(link0, unix.O_RDONLY, 0) - if err != nil { - // Must work with -sharedstorage - if flagSharestorage { - t.Fatal(err) - } else { - // Must always work after cache timeout - time.Sleep(waitForExpire) - fd, err = unix.Open(link0, unix.O_RDONLY, 0) - if err != nil { - t.Fatal(err) - } - } - } - unix.Close(fd) -} diff --git a/tests/sshfs-benchmark.bash b/tests/sshfs-benchmark.bash deleted file mode 100755 index 13c7a0f..0000000 --- a/tests/sshfs-benchmark.bash +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -set -eu - -function cleanup { - cd "$LOCAL_TMP" - fusermount -u gocryptfs.mnt - rm -Rf "$SSHFS_TMP" - fusermount -u sshfs.mnt - cd / - rm -Rf "$LOCAL_TMP" -} - -function prepare_mounts { - LOCAL_TMP=$(mktemp -d -t "$MYNAME.XXX") - cd $LOCAL_TMP - echo "working directory: $PWD" - mkdir sshfs.mnt gocryptfs.mnt - sshfs $HOST:/tmp sshfs.mnt - echo "sshfs mounted: $HOST:/tmp -> sshfs.mnt" - trap cleanup EXIT - SSHFS_TMP=$(mktemp -d "sshfs.mnt/$MYNAME.XXX") - mkdir $SSHFS_TMP/gocryptfs.crypt - gocryptfs -q -init -extpass "echo test" -scryptn=10 $SSHFS_TMP/gocryptfs.crypt - gocryptfs -q -extpass "echo test" $SSHFS_TMP/gocryptfs.crypt gocryptfs.mnt - echo "gocryptfs mounted: $SSHFS_TMP/gocryptfs.crypt -> gocryptfs.mnt" -} - -function etime { - T=$(/usr/bin/time -f %e -o /dev/stdout "$@") - LC_ALL=C printf %20.2f "$T" -} - -MYNAME=$(basename "$0") -HOST=$1 - -prepare_mounts - -echo -echo "$MYNAME: sshfs gocryptfs-on-sshfs" -echo -n "git init " -etime git init -q "$SSHFS_TMP/git1" -etime git init -q gocryptfs.mnt/git1 -echo - -git init -q git2 -echo -n "rsync " -etime rsync -a --no-group git2 "$SSHFS_TMP" -etime rsync -a --no-group git2 gocryptfs.mnt -echo - -echo -n "rm -R " -etime rm -R "$SSHFS_TMP/git1" "$SSHFS_TMP/git2" -etime rm -R gocryptfs.mnt/git1 gocryptfs.mnt/git2 -echo - -echo -n "mkdir " -pushd "$SSHFS_TMP" > /dev/null -etime mkdir $(seq 1 20) -popd > /dev/null -cd gocryptfs.mnt -etime mkdir $(seq 1 20) -cd .. -echo - -echo -n "rmdir " -pushd "$SSHFS_TMP" > /dev/null -etime rmdir $(seq 1 20) -popd > /dev/null -cd gocryptfs.mnt -etime rmdir $(seq 1 20) -cd .. -echo - -echo -n "touch " -pushd "$SSHFS_TMP" > /dev/null -etime touch $(seq 101 120) -popd > /dev/null -cd gocryptfs.mnt -etime touch $(seq 101 120) -cd .. -echo - -echo -n "rm " -pushd "$SSHFS_TMP" > /dev/null -etime rm $(seq 101 120) -popd > /dev/null -cd gocryptfs.mnt -etime rm $(seq 101 120) -cd .. -echo diff --git a/tests/stress_tests/extractloop.bash b/tests/stress_tests/extractloop.bash deleted file mode 100755 index cdd0c25..0000000 --- a/tests/stress_tests/extractloop.bash +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/bash -# -# Mount a gocryptfs filesystem somewhere on /tmp, then run two parallel -# infinite loops inside that do the following: -# 1) Extract linux-3.0.tar.gz -# 2) Verify the md5sums -# 3) Delete, go to (1) -# -# This test is good at discovering inode-related memory leaks because it creates -# huge numbers of files. -# -# See Documentation/extractloop.md for example output. - -if [[ -z $TMPDIR ]]; then - TMPDIR=/var/tmp - export TMPDIR -fi - -set -eu - -# Run at low priority to not annoy the user too much -renice 19 $$ - -cd "$(dirname "$0")" -MD5="$PWD/linux-3.0.md5sums" -MYNAME=$(basename "$0") -source ../fuse-unmount.bash - -# Setup dirs -../dl-linux-tarball.bash -cd $TMPDIR -EXTRACTLOOP_TMPDIR=$TMPDIR/extractloop_tmpdir -mkdir -p $EXTRACTLOOP_TMPDIR -CRYPT=$(mktemp -d $EXTRACTLOOP_TMPDIR/XXX) -CSV=$CRYPT.csv -MNT=$CRYPT.mnt -mkdir $MNT - -function check_md5sums { - if command -v md5sum > /dev/null ; then - md5sum --status -c $1 - else - # MacOS / darwin which do not have the md5sum utility - # installed by default - echo "Skipping md5sum (not installed). Hint: brew install md5sha1sum" - fi -} - -# Mount -FSPID=0 -FS="" -if [ $# -eq 1 ] && [ "$1" == "-encfs" ]; then - FS=encfs - echo "Testing EncFS" - encfs --extpass="echo test" --standard $CRYPT $MNT > /dev/null -elif [ $# -eq 1 ] && [ "$1" == "-loopback" ]; then - FS=loopback - echo "Testing go-fuse loopback" - rm -f /tmp/loopback*.memprof - loopback -memprofile=/tmp/loopback $MNT $CRYPT & - FSPID=$(jobs -p) - disown -else - FS=gocryptfs - echo "Testing gocryptfs" - gocryptfs -q -init -extpass="echo test" -scryptn=10 $CRYPT - gocryptfs -q -extpass="echo test" -nosyslog -fg $CRYPT $MNT & - FSPID=$(jobs -p) - disown - #gocryptfs -q -extpass="echo test" -nosyslog -memprofile /tmp/extractloop-mem $CRYPT $MNT -fi -echo "Test dir: $CRYPT" -# Sleep to make sure the FS is already mounted on MNT -sleep 1 -cd $MNT - -ln -v -sTf $CSV /tmp/extractloop.csv 2> /dev/null || true # fails on MacOS, ignore - -# Cleanup trap -# Note: gocryptfs may have already umounted itself because bash relays SIGINT -# Just ignore unmount errors. -trap cleanup_exit EXIT - -function cleanup_exit { - if [[ $FS == loopback ]]; then - # SIGUSR1 causes loopback to write the memory profile to disk - kill -USR1 $FSPID - fi - cd / - rm -Rf $CRYPT - fuse-unmount -z $MNT || true - rmdir $MNT -} - -function loop { - ID=$1 - mkdir $ID - cd $ID - - echo "[looper $ID] Starting" - - N=1 - RSS=0 - while true - do - t1=$SECONDS - tar xf /tmp/linux-3.0.tar.gz --exclude linux-3.0/arch/microblaze/boot/dts/system.dts - # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - # Exclude the one symlink in the tarball - causes problems on MacOS: "Can't set permissions to 0755" - check_md5sums $MD5 - rm -R linux-3.0 - t2=$SECONDS - delta=$((t2-t1)) - if [[ $FSPID -gt 0 && -d /proc ]]; then - RSS=$(grep VmRSS /proc/$FSPID/status | tr -s ' ' | cut -f2 -d ' ') - echo "$N,$SECONDS,$RSS,$delta" >> $CSV - fi - echo "[looper $ID] Iteration $N done, $delta seconds, RSS $RSS kiB" - let N=$N+1 - done -} - -function memprof { - while true; do - kill -USR1 $FSPID - sleep 60 - done -} - -loop 1 & -loop 2 & -if [[ $FS == loopback ]]; then - memprof & -fi -wait diff --git a/tests/stress_tests/extractloop_plot_csv.m b/tests/stress_tests/extractloop_plot_csv.m deleted file mode 100755 index be80656..0000000 --- a/tests/stress_tests/extractloop_plot_csv.m +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/octave - -r=csvread('/tmp/extractloop.csv'); -figure('Position',[100,100,1600,800]); -[hAx,hLine1,hLine2] = plotyy(r(:,2), r(:,3)/1024, r(:,2), r(:,4)); -xlabel('Runtime (seconds)') -set(hLine1, 'LineWidth', 1, 'Marker', 'o', 'MarkerSize', 10) -% LineWidth also sets the marker drawing thickness -set(hLine2, 'LineWidth', 1, 'LineStyle', 'none', 'Marker', '*', 'MarkerSize', 10) -ylabel(hAx(1), 'RSS (MiB)') -ylabel(hAx(2), 'Iteration Time (seconds)') -grid on; -drawnow; -disp('press enter to exit'); -input(''); diff --git a/tests/stress_tests/fsstress-encfs.bash b/tests/stress_tests/fsstress-encfs.bash deleted file mode 120000 index 139d4ec..0000000 --- a/tests/stress_tests/fsstress-encfs.bash +++ /dev/null @@ -1 +0,0 @@ -fsstress-gocryptfs.bash \ No newline at end of file diff --git a/tests/stress_tests/fsstress-gocryptfs.bash b/tests/stress_tests/fsstress-gocryptfs.bash deleted file mode 100755 index 26e7bc0..0000000 --- a/tests/stress_tests/fsstress-gocryptfs.bash +++ /dev/null @@ -1,112 +0,0 @@ -#!/bin/bash -# -# Mount a go-fuse loopback filesystem in /tmp and run fsstress against it -# in an infinite loop, only exiting on errors. -# -# When called as "fsstress-gocryptfs.bash", a gocryptfs filesystem is tested -# instead. -# -# This test used to fail on older go-fuse versions after a few iterations with -# errors like this: -# "rm: cannot remove ‘/tmp/b/fsstress.2/pd/d1XXX/f4a’: No such file or directory" -# -# Nowadays it should pass an indefinite number of iterations. - -set -eu - -# Init variables to default values if unset or empty -export TMPDIR=${TMPDIR:-/var/tmp} -DEBUG=${DEBUG:-0} - -cd "$(dirname "$0")" -MYNAME=$(basename $0) -source ../fuse-unmount.bash - -# fsstress binary -FSSTRESS=$HOME/fuse-xfstests/ltp/fsstress -if [[ ! -x $FSSTRESS ]] -then - echo "$MYNAME: fsstress binary not found at $FSSTRESS" - echo "Please clone and compile https://github.com/rfjakob/fuse-xfstests" - exit 1 -fi - -# Backing directory -DIR=$(mktemp -d $TMPDIR/$MYNAME.XXX) -# Mountpoint -MNT="$DIR.mnt" -mkdir $MNT - -# Set the GOPATH variable to the default if it is empty -GOPATH=$(go env GOPATH) - -# Clean up old mounts -for i in $(mount | cut -d" " -f3 | grep $TMPDIR/$MYNAME) ; do - fusermount -u $i -done - -# FS-specific compile and mount -if [[ $MYNAME = fsstress-loopback.bash ]]; then - echo -n "Recompile go-fuse loopback: " - cd $GOPATH/src/github.com/hanwen/go-fuse/example/loopback - git describe - go build && go install - OPTS="-q" - if [[ $DEBUG -eq 1 ]]; then - OPTS="-debug" - fi - $GOPATH/bin/loopback $OPTS "$MNT" "$DIR" & - disown -elif [[ $MYNAME = fsstress-gocryptfs.bash ]]; then - echo "Recompile gocryptfs" - cd $GOPATH/src/github.com/rfjakob/gocryptfs - ./build.bash # also prints the version - $GOPATH/bin/gocryptfs -q -init -extpass "echo test" -scryptn=10 $DIR - $GOPATH/bin/gocryptfs -q -extpass "echo test" -nosyslog -fusedebug=$DEBUG $DIR $MNT -elif [[ $MYNAME = fsstress-encfs.bash ]]; then - encfs --extpass "echo test" --standard $DIR $MNT -else - echo Unknown mode: $MYNAME - exit 1 -fi - -sleep 0.5 -echo -n "Waiting for mount: " -while ! grep "$(basename $MNT) fuse" /proc/self/mounts > /dev/null -do - sleep 1 - echo -n x -done -echo " ok: $MNT" - -# Cleanup trap -trap "kill %1 ; cd / ; fuse-unmount -z $MNT ; rm -rf $DIR $MNT" EXIT - -echo "Starting fsstress loop" -N=1 -while true -do - echo "$N ................................. $(date)" - mkdir $MNT/fsstress.1 - echo -n " fsstress.1 " - $FSSTRESS -r -m 8 -n 1000 -d $MNT/fsstress.1 & - wait - - mkdir $MNT/fsstress.2 - echo -n " fsstress.2 " - $FSSTRESS -p 20 -r -m 8 -n 1000 -d $MNT/fsstress.2 & - wait - - mkdir $MNT/fsstress.3 - echo -n " fsstress.3 " - $FSSTRESS -p 4 -z -f rmdir=10 -f link=10 -f creat=10 -f mkdir=10 \ - -f rename=30 -f stat=30 -f unlink=30 -f truncate=20 -m 8 \ - -n 1000 -d $MNT/fsstress.3 & - wait - - echo " rm" - rm -Rf $MNT/* - - let N=$N+1 -done - diff --git a/tests/stress_tests/fsstress-loopback.bash b/tests/stress_tests/fsstress-loopback.bash deleted file mode 120000 index 139d4ec..0000000 --- a/tests/stress_tests/fsstress-loopback.bash +++ /dev/null @@ -1 +0,0 @@ -fsstress-gocryptfs.bash \ No newline at end of file diff --git a/tests/stress_tests/fsstress.collect-crashes.sh b/tests/stress_tests/fsstress.collect-crashes.sh deleted file mode 100755 index f315e2e..0000000 --- a/tests/stress_tests/fsstress.collect-crashes.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -cd ~/go/src/github.com/rfjakob/gocryptfs/tests/stress_tests || exit 1 -export TMPDIR=/mnt/ext4-ramdisk -# Check that TMPDIR is writeable -touch "$TMPDIR/$$" || exit 1 -rm "$TMPDIR/$$" -LOGDIR=/tmp/$$ -mkdir "$LOGDIR" || exit 1 -echo "Logging to LOGDIR=$LOGDIR, TMPDIR=$TMPDIR" -for i in $(seq 1 1000) ; do - set -x - LOG="$LOGDIR/fsstress.log.$(date --iso).$i" - if [[ -e $LOG ]]; then - continue - fi - rm -Rf "$TMPDIR"/fsstress* - # 100000 lines ...... ~7 MB - # 1000000 lines ..... ~70 MB - # 10000000 lines .... ~700 MB - DEBUG=1 ./fsstress-loopback.bash 2>&1 | tail -1000000 > "$LOG" -done diff --git a/tests/stress_tests/linux-3.0.md5sums b/tests/stress_tests/linux-3.0.md5sums deleted file mode 100644 index 2cf6349..0000000 --- a/tests/stress_tests/linux-3.0.md5sums +++ /dev/null @@ -1,36782 +0,0 @@ -8a44756de487a56f758ee3dc5d8b1a3f linux-3.0/virt/kvm/kvm_main.c -11b17bdc92061b7b96f7bc3e3653db86 linux-3.0/virt/kvm/irq_comm.c -b806e1725ecc18c4c54c8286e75b86a9 linux-3.0/virt/kvm/iommu.c -89287c0ee08927ac145b6bbbff6973ae linux-3.0/virt/kvm/iodev.h -78b06af814b09a40205250e7ce006bc2 linux-3.0/virt/kvm/ioapic.h -260e5541c3ecfff787430025a115d26d linux-3.0/virt/kvm/ioapic.c -6a6ca7e99fb6a24f1c24d7a9d481c26f linux-3.0/virt/kvm/eventfd.c -fb14b17966783d02a91fc3fc14fb3a6d linux-3.0/virt/kvm/coalesced_mmio.h -55b7c8870ea82a5fbbf54b605acb1187 linux-3.0/virt/kvm/coalesced_mmio.c -335cd037b29751033a198be10597227b linux-3.0/virt/kvm/async_pf.h -25e3d49cb7854c78e00d206a9419d817 linux-3.0/virt/kvm/async_pf.c -2b5dd9a77921b3988322581c663db4bf linux-3.0/virt/kvm/assigned-dev.c -5482920d7d3d151945a4df62a1c7f024 linux-3.0/virt/kvm/Kconfig -047de60ac631660198bdf5ffbe6bb62f linux-3.0/usr/initramfs_data.S -4f8151ebfa1693b19858c5856b43660a linux-3.0/usr/gen_init_cpio.c -f9e1fdbf7a3ade26cd9842cc8506dad9 linux-3.0/usr/Makefile -01eef3783a737e087b97664ecd5faaa9 linux-3.0/usr/Kconfig -530ec264818fd9d7f0810479b3b4e360 linux-3.0/usr/.gitignore -683865743f1dc3997fc7a987a2998a1b linux-3.0/tools/virtio/virtio_test.c -db868860cf4da73eb6499489cc4e096a linux-3.0/tools/virtio/vhost_test/vhost_test.c -e6486bef9d9d891d8532d19c2581d3d8 linux-3.0/tools/virtio/vhost_test/Makefile -9b0e58084141e0dea3340bc3b1211b00 linux-3.0/tools/virtio/linux/virtio.h -50bec7f312c5d26b3db056275ffd7341 linux-3.0/tools/virtio/linux/slab.h -b0c3aee60e5132c53c2ad6b29b93fd49 linux-3.0/tools/virtio/linux/device.h -965a7f7e18ba6d2c0825285c5b58e0d1 linux-3.0/tools/virtio/Makefile -c987e0eec87236de3c1ee9df2f65bc28 linux-3.0/tools/usb/testusb.c -9f629e683ca39333b7f69d727ea9e0c6 linux-3.0/tools/usb/hcd-tests.sh -391b77acfb70985d3f670788f9ebd031 linux-3.0/tools/usb/ffs-test.c -092ccf63c2f76a4ce671981e0a4b3ecc linux-3.0/tools/usb/Makefile -1651035169ef26caf3f4f71ac3ae4f3d linux-3.0/tools/testing/ktest/sample.conf -34162d5a48aba4f15c3e89c22f54aa2e linux-3.0/tools/testing/ktest/ktest.pl -111e72b075bd0f17158cffaf3c445748 linux-3.0/tools/testing/ktest/compare-ktest-sample.pl -c2f4988d33bdf8710c3a83371f3cfecc linux-3.0/tools/slub/slabinfo.c -f9744712354b26eda999e4e751cbbbef linux-3.0/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c -fc632a92f1476cc2e69e3c4149ed0925 linux-3.0/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.8 -a5c7116b041fb4818b47af6df1396bf6 linux-3.0/tools/power/x86/x86_energy_perf_policy/Makefile -40337533c5345608c113c70fd8ba84e7 linux-3.0/tools/power/x86/turbostat/turbostat.c -90016eabb766a9edf4f8a8e3a33bce3f linux-3.0/tools/power/x86/turbostat/turbostat.8 -5d75eddb064a1b28956d05bf3dea82bd linux-3.0/tools/power/x86/turbostat/Makefile -f8214f548f349d425897fbfe65be6318 linux-3.0/tools/perf/util/xyarray.h -90c2dc70343ff978cceeb59787b6cd2b linux-3.0/tools/perf/util/xyarray.c -9d6feb3843bd718f8bc953e411fc15d2 linux-3.0/tools/perf/util/wrapper.c -27c4f08d17f80653d99432fa4613f03f linux-3.0/tools/perf/util/values.h -1ccbd280b50970cc1a6303b7e28189d0 linux-3.0/tools/perf/util/values.c -aaf94ff39fc686ce6b49d26691177775 linux-3.0/tools/perf/util/util.h -1cd2c147d326b4b1e0541afc937a254a linux-3.0/tools/perf/util/util.c -b2cd2a25ff8f913820e85230ead5de32 linux-3.0/tools/perf/util/usage.c -ae0f8e45c132c5d81e4e932186d280f5 linux-3.0/tools/perf/util/ui/util.h -16087df9dd67dd7cc945c92bf43c3db0 linux-3.0/tools/perf/util/ui/util.c -fe12940dfcf064637eaf8ef196821d71 linux-3.0/tools/perf/util/ui/ui.h -eed13ffa4256757394482782814626bb linux-3.0/tools/perf/util/ui/setup.c -e2ce908f0214c7c33194071232a24cbc linux-3.0/tools/perf/util/ui/progress.h -b4869f9fc603e9e7411356e19c523c17 linux-3.0/tools/perf/util/ui/progress.c -065c7a777cca0a1371b4c250cb8773b1 linux-3.0/tools/perf/util/ui/libslang.h -89472959a3ab96609acb44fd68c2fcdb linux-3.0/tools/perf/util/ui/helpline.h -9d8240be045ca8686583f27e31f1286f linux-3.0/tools/perf/util/ui/helpline.c -071fa1511cbb5b647298dea89b526b05 linux-3.0/tools/perf/util/ui/browsers/top.c -b6787c67527164c3627d03c46f599ab2 linux-3.0/tools/perf/util/ui/browsers/map.h -71490d5f10e2fef12862d0a8f97fe5f6 linux-3.0/tools/perf/util/ui/browsers/map.c -8bbe9afc81156c13620bdcb717632d66 linux-3.0/tools/perf/util/ui/browsers/hists.c -34bdee201a1830e5daaf388e1d5d4dbd linux-3.0/tools/perf/util/ui/browsers/annotate.c -ca7f4a70d8621b36e09b8ae13eeafa33 linux-3.0/tools/perf/util/ui/browser.h -38488cf8d4eded0d4a052cac606fbdb9 linux-3.0/tools/perf/util/ui/browser.c -f152c7fa179e1b64508583f82fe81b3e linux-3.0/tools/perf/util/types.h -8255bedda876262117c36d4ebfabeee0 linux-3.0/tools/perf/util/trace-event.h -a11a6b4989f700d5d750f68da4cf37f5 linux-3.0/tools/perf/util/trace-event-scripting.c -ec45047d29cb48d51fb78b73be936120 linux-3.0/tools/perf/util/trace-event-read.c -3b7d01975bb448392e7c1bd109547688 linux-3.0/tools/perf/util/trace-event-parse.c -3b76ac45bbfcd6577d312a86b11fb428 linux-3.0/tools/perf/util/trace-event-info.c -a338904b2f4241175c31ef880070f5c5 linux-3.0/tools/perf/util/top.h -edfc85fdd104d33b71be7e78dcd7a7c2 linux-3.0/tools/perf/util/top.c -95d594627a9567524cad3c5f1a19a963 linux-3.0/tools/perf/util/thread_map.h -54e1fee3778469f862fc2071393d2bf6 linux-3.0/tools/perf/util/thread_map.c -5d8b4a93366d6c09847df3167c41daa0 linux-3.0/tools/perf/util/thread.h -f2566439d697770ee419a515f17cc870 linux-3.0/tools/perf/util/thread.c -4353817e9444200f8daafe4cc336245b linux-3.0/tools/perf/util/symbol.h -ed6bbcf665cb44265fd42c7da152c8fb linux-3.0/tools/perf/util/symbol.c -0930549b78d76bfeb2e6b1f3fd1d992a linux-3.0/tools/perf/util/svghelper.h -967d2f0168866591ea0fee83d68b76a9 linux-3.0/tools/perf/util/svghelper.c -6a70054800a47878263641ad3bd27f13 linux-3.0/tools/perf/util/strlist.h -b44f063cf1c8b3853df04c493222cdd9 linux-3.0/tools/perf/util/strlist.c -677719d9975a83e5961e3f2e9a4fd0a9 linux-3.0/tools/perf/util/string.c -3e453c2e89cdd10a967beb599ff4f5ab linux-3.0/tools/perf/util/strfilter.h -683288a8170da2ada41629585f01b442 linux-3.0/tools/perf/util/strfilter.c -0f24c529687cc306396ffdd4c74f4eba linux-3.0/tools/perf/util/strbuf.h -d8594be92a6cb36c0795a11b73035738 linux-3.0/tools/perf/util/strbuf.c -bf70243cf567a7b94022bce1751d0c53 linux-3.0/tools/perf/util/sort.h -c90256d7afb14587eeb8e58f646ac2f6 linux-3.0/tools/perf/util/sort.c -fbfebd61f4582411743caa639d1e6475 linux-3.0/tools/perf/util/sigchain.h -97611192bf70902c9aa1d7d8ec7fb791 linux-3.0/tools/perf/util/sigchain.c -bb39fad37100d36a84bedd73b5089c7a linux-3.0/tools/perf/util/setup.py -33b1a24a4e7fccb8b21a545244b13123 linux-3.0/tools/perf/util/session.h -ad787ecfe904fcdcb55492a4040b495c linux-3.0/tools/perf/util/session.c -f2a7e52e215780cb13de593eeec8780d linux-3.0/tools/perf/util/scripting-engines/trace-event-python.c -963c73397343bc8743906d7930b52f21 linux-3.0/tools/perf/util/scripting-engines/trace-event-perl.c -00a03b81b63f62997cf3e7c5e325a481 linux-3.0/tools/perf/util/run-command.h -6c13c4929ac7ed3682daad3a8fecf560 linux-3.0/tools/perf/util/run-command.c -8005e383cb16095095ad5722149cd395 linux-3.0/tools/perf/util/quote.h -fb53d259350a7b6fc10953415140efaa linux-3.0/tools/perf/util/quote.c -2b20cbcf39c0cb1f8ac745185b0c1229 linux-3.0/tools/perf/util/python.c -4e31e03166484444f238c8ebacb7fba1 linux-3.0/tools/perf/util/pstack.h -763f707263c68cb567d23e7b9c55e00c linux-3.0/tools/perf/util/pstack.c -5e6ceff892a7f172359ca61ea0607850 linux-3.0/tools/perf/util/probe-finder.h -50822b01a2c7fb20820776e0e8c0c83c linux-3.0/tools/perf/util/probe-finder.c -e1b8d87748fe2b2a5219da819110f814 linux-3.0/tools/perf/util/probe-event.h -d876320c52c0c2610b454099a2f141fc linux-3.0/tools/perf/util/probe-event.c -59cb18def9d8bd1b5b04d0a9a1677404 linux-3.0/tools/perf/util/path.c -911bde63991515b7608fe1e4c3eaf59f linux-3.0/tools/perf/util/parse-options.h -695e64d67dd04ea610dfc73957c141dc linux-3.0/tools/perf/util/parse-options.c -f1b81f7dace88fa8a12715b3a41c943f linux-3.0/tools/perf/util/parse-events.h -aac4d09d352f98cc58d979a46a5e841c linux-3.0/tools/perf/util/parse-events.c -dc856e507c1105eb3f2e4660a356a693 linux-3.0/tools/perf/util/pager.c -97130129524daf59c274427fa928afa7 linux-3.0/tools/perf/util/map.h -94efd228d9c019de996c47fd8040832a linux-3.0/tools/perf/util/map.c -cc33a420384a78c0ed8737724fc67410 linux-3.0/tools/perf/util/levenshtein.h -4488ae93a303f331fc6e679cd3589985 linux-3.0/tools/perf/util/levenshtein.c -a807dd338e16a75a16f4da08349a1b94 linux-3.0/tools/perf/util/include/linux/types.h -69d15c31527f01f99796d1e1a414e38f linux-3.0/tools/perf/util/include/linux/string.h -d9335a229992b0225cd12fd1b706705a linux-3.0/tools/perf/util/include/linux/rbtree.h -d29a0d3558d2e321bacfa9c4c403052f linux-3.0/tools/perf/util/include/linux/prefetch.h -bf0f255442e21bfe6021af139e91aa5a linux-3.0/tools/perf/util/include/linux/poison.h -3560c691066e59a53b8f980fc40a350c linux-3.0/tools/perf/util/include/linux/module.h -4215523f252c8e229bab367633362869 linux-3.0/tools/perf/util/include/linux/list.h -b006060ab731a89573b378aa95227a14 linux-3.0/tools/perf/util/include/linux/linkage.h -919a2056fc880f1e6549e28b8f1a2683 linux-3.0/tools/perf/util/include/linux/kernel.h -47ede60d0dd00b0669e4329fb859a7d3 linux-3.0/tools/perf/util/include/linux/hash.h -cf687f1506e6035c235c998141752bbc linux-3.0/tools/perf/util/include/linux/ctype.h -34e07f1daa502377dccafc130e567c93 linux-3.0/tools/perf/util/include/linux/const.h -e11894eaf87b9762a2d4956a9f7d6456 linux-3.0/tools/perf/util/include/linux/compiler.h -5b3702641c0a2779723e19d6d18408ac linux-3.0/tools/perf/util/include/linux/bitops.h -2c31af15add0e60b8b8880fdd83be745 linux-3.0/tools/perf/util/include/linux/bitmap.h -331e4ed3b5453ad70a7a24c23b8d4a20 linux-3.0/tools/perf/util/include/dwarf-regs.h -8ced7bfa99ec38686579bd464da28ebc linux-3.0/tools/perf/util/include/asm/uaccess.h -a863ef5f1c11831afdaf974a863a0a63 linux-3.0/tools/perf/util/include/asm/system.h -7b21b66ba6e9d79afaedf7ae0a17c827 linux-3.0/tools/perf/util/include/asm/swab.h -fdc86d546a33c1348a6b5fd0c5004c9e linux-3.0/tools/perf/util/include/asm/hweight.h -156543607cb7712d966590df378d99d6 linux-3.0/tools/perf/util/include/asm/dwarf2.h -a07a2ee4966f34f01011a77d7a78b154 linux-3.0/tools/perf/util/include/asm/cpufeature.h -89e4ca58025f5ec28310df44486bf510 linux-3.0/tools/perf/util/include/asm/byteorder.h -a5fb921d1d3bb430277b56af9cdf367d linux-3.0/tools/perf/util/include/asm/bug.h -7b21b66ba6e9d79afaedf7ae0a17c827 linux-3.0/tools/perf/util/include/asm/asm-offsets.h -26a809d7880dbdbf03e6664e7adaf183 linux-3.0/tools/perf/util/include/asm/alternative-asm.h -356d340d692d8afce0ddbf5731ab599d linux-3.0/tools/perf/util/hweight.c -a734133f6603d2002854d12532f5c6c9 linux-3.0/tools/perf/util/hist.h -278df634ca2e7ff2b8983bd3f42b2f1f linux-3.0/tools/perf/util/hist.c -eef7f3308b426fcf3a5511acd7f3a585 linux-3.0/tools/perf/util/help.h -f2fef39347d1ee80b98d1b737a26f135 linux-3.0/tools/perf/util/help.c -c8e006fa8c4ccffeed19f0572bf45a38 linux-3.0/tools/perf/util/header.h -1484d479d77752f7d0407ac688734713 linux-3.0/tools/perf/util/header.c -b70c1502642732639df06e50395838c2 linux-3.0/tools/perf/util/generate-cmdlist.sh -5d4d9dfee7c28134a3a24bc45166a71d linux-3.0/tools/perf/util/exec_cmd.h -a1f608c75c66822e6860e954649bcd34 linux-3.0/tools/perf/util/exec_cmd.c -25540eed5eba2cc5b055e722b9d5ebfb linux-3.0/tools/perf/util/evsel.h -7f9bb25644756342ad1ba0b5daee8f8d linux-3.0/tools/perf/util/evsel.c -c4dd1742b7c4ce8a0ceb7346a285d579 linux-3.0/tools/perf/util/evlist.h -d3978a55fb5ac4e922f551d55f264fb0 linux-3.0/tools/perf/util/evlist.c -d23e210dc8bdbc9280955f33a6c20811 linux-3.0/tools/perf/util/event.h -0a1301317c452bf3f74a8cf64eee208a linux-3.0/tools/perf/util/event.c -942dd3cd3bc14d3098fa1d8ecbe46a79 linux-3.0/tools/perf/util/environment.c -70c24620d29d81c8bca3d99367737572 linux-3.0/tools/perf/util/debugfs.h -7560f5c45ac1e89517d317a7e1d5538c linux-3.0/tools/perf/util/debugfs.c -93bb182dbc81d2ae2e542e8726c1750d linux-3.0/tools/perf/util/debug.h -4349c0faba1298273bca8d1f23702fae linux-3.0/tools/perf/util/debug.c -a94bafafc732ff16a7625aab71e53a16 linux-3.0/tools/perf/util/ctype.c -febe42237319d2802e38f6f49082a726 linux-3.0/tools/perf/util/cpumap.h -97fd10ab554d8f516e90328636a8e39e linux-3.0/tools/perf/util/cpumap.c -47d3dde38b91a1f829bcab154041e77e linux-3.0/tools/perf/util/config.c -cb83a74f6422b4ab0839da201862778a linux-3.0/tools/perf/util/color.h -186a3744f5176b63fef77922e6c84e00 linux-3.0/tools/perf/util/color.c -5bd4ed1243f792b66d0fc37d95f761a9 linux-3.0/tools/perf/util/cgroup.h -81d6bb6cec2a9098daf1f70ff798867f linux-3.0/tools/perf/util/cgroup.c -83ec1ebe9255598be889ef5649d2f5d2 linux-3.0/tools/perf/util/callchain.h -a4551bdfd47931396babbfa57f99df3e linux-3.0/tools/perf/util/callchain.c -d675a402ebf5b067448aaa0f42d5a828 linux-3.0/tools/perf/util/cache.h -01663d41bbd5ec2ab76e6d24fd9b9e9c linux-3.0/tools/perf/util/build-id.h -98e3e09e5b508d9fdb0178b36df52802 linux-3.0/tools/perf/util/build-id.c -fd0fa57669c128e50b2805884812c97e linux-3.0/tools/perf/util/bitmap.c -74489edf6a4de9abfc6288599fe27391 linux-3.0/tools/perf/util/annotate.h -051b18ebe1bbea9fa5a999596a032ec3 linux-3.0/tools/perf/util/annotate.c -7847df3719476d031225307476351c8b linux-3.0/tools/perf/util/alias.c -32421cca4eaacfc02ec3f50f8de32417 linux-3.0/tools/perf/util/abspath.c -47ac78c4d9144f8ab2fe4e431e062320 linux-3.0/tools/perf/util/PERF-VERSION-GEN -481c4fd645bd4c1d7ae2469f60fd0197 linux-3.0/tools/perf/scripts/python/syscall-counts.py -d6cc12bb444eb2798e1c9117d56ece12 linux-3.0/tools/perf/scripts/python/syscall-counts-by-pid.py -1dedfc25e63aed3849b8d2632b9f25f5 linux-3.0/tools/perf/scripts/python/sctop.py -eb6947be1ce66560795eeae0a6eb729c linux-3.0/tools/perf/scripts/python/sched-migration.py -f9b0b1011c1e6ebc032b148dd4ecbea2 linux-3.0/tools/perf/scripts/python/netdev-times.py -358c2cc8ea7f55856beb5a7ecf33c2ed linux-3.0/tools/perf/scripts/python/futex-contention.py -8bfed65ef51a1c54ca72aacbe2dd9d26 linux-3.0/tools/perf/scripts/python/failed-syscalls-by-pid.py -cf557a785ef7546e18c0c851d6e4e774 linux-3.0/tools/perf/scripts/python/check-perf-trace.py -7c6887d5738be8c92f9fd119af690e96 linux-3.0/tools/perf/scripts/python/bin/syscall-counts-report -61a81145b068412612ded7691a4d2330 linux-3.0/tools/perf/scripts/python/bin/syscall-counts-record -c6b25fa68c7d4b360fae3c801e746573 linux-3.0/tools/perf/scripts/python/bin/syscall-counts-by-pid-report -61a81145b068412612ded7691a4d2330 linux-3.0/tools/perf/scripts/python/bin/syscall-counts-by-pid-record -8ff62ed8c6eef5f07f0b067bb89e083a linux-3.0/tools/perf/scripts/python/bin/sctop-report -61a81145b068412612ded7691a4d2330 linux-3.0/tools/perf/scripts/python/bin/sctop-record -cded78feb4f7110a7a371d694e51697a linux-3.0/tools/perf/scripts/python/bin/sched-migration-report -ac9fa2d6d25fd94236de21b9b6b1f2eb linux-3.0/tools/perf/scripts/python/bin/sched-migration-record -c6326c54c275cc9657d7df097deae0a0 linux-3.0/tools/perf/scripts/python/bin/netdev-times-report -439e3fee34ecef92d106a5639f7a1372 linux-3.0/tools/perf/scripts/python/bin/netdev-times-record -bb6878c428ec3b49bbcdb8935af4c70d linux-3.0/tools/perf/scripts/python/bin/futex-contention-report -06326a949892ef3d1a7e0d1c2558ccb6 linux-3.0/tools/perf/scripts/python/bin/futex-contention-record -3010bda11c72c3905a21a51c37ecac34 linux-3.0/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report -29ad806ebf367d8955f920696aae0a3b linux-3.0/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record -05a0d525cb7b9fe466b6c33d984da6e7 linux-3.0/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py -cf3ab1af1b3edd9f30763a5abf1c9c7d linux-3.0/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py -c7a6239273f8989b7d1bd1e0f0ec0b6b linux-3.0/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py -e70accb6d24004c11aac13f06e2c8a3e linux-3.0/tools/perf/scripts/python/Perf-Trace-Util/Context.c -ac89ba60a8aad3826379e0aaf48fc5fc linux-3.0/tools/perf/scripts/perl/workqueue-stats.pl -af17763cbaa4079cff62ec1af1113dcc linux-3.0/tools/perf/scripts/perl/wakeup-latency.pl -80b48530e606a997390f869bc0701493 linux-3.0/tools/perf/scripts/perl/rwtop.pl -d473861fd7e73a1abc384f7a25867c37 linux-3.0/tools/perf/scripts/perl/rw-by-pid.pl -46513fc784eec1ca19c127a4280ac81e linux-3.0/tools/perf/scripts/perl/rw-by-file.pl -8d96f07ac80eb59ee37a48cbde2c215e linux-3.0/tools/perf/scripts/perl/failed-syscalls.pl -abd172f98b33ff8ae16f2c1e069bc3cb linux-3.0/tools/perf/scripts/perl/check-perf-trace.pl -f53739635695af80288ffdbe001e75fb linux-3.0/tools/perf/scripts/perl/bin/workqueue-stats-report -e9c224481bfa6353d92d96a700488b46 linux-3.0/tools/perf/scripts/perl/bin/workqueue-stats-record -04640f0efa55f5aa8f5b5df9fb5e14d0 linux-3.0/tools/perf/scripts/perl/bin/wakeup-latency-report -8f6c86f33c059dae01df5fbd26fb8828 linux-3.0/tools/perf/scripts/perl/bin/wakeup-latency-record -fe30bfbb976bb370aec28df22a78a22f linux-3.0/tools/perf/scripts/perl/bin/rwtop-report -5f66d67da405bdfb8d4be7fd7df9bd52 linux-3.0/tools/perf/scripts/perl/bin/rwtop-record -05569524632208cb2a585aeb56750756 linux-3.0/tools/perf/scripts/perl/bin/rw-by-pid-report -5f66d67da405bdfb8d4be7fd7df9bd52 linux-3.0/tools/perf/scripts/perl/bin/rw-by-pid-record -7a3282ec87197de664bdc3a64efa43ed linux-3.0/tools/perf/scripts/perl/bin/rw-by-file-report -4f5329582c4f5632ea91002bdce7b07b linux-3.0/tools/perf/scripts/perl/bin/rw-by-file-record -e8de6f7f10fb8024a73427db4943a8e2 linux-3.0/tools/perf/scripts/perl/bin/failed-syscalls-report -29ad806ebf367d8955f920696aae0a3b linux-3.0/tools/perf/scripts/perl/bin/failed-syscalls-record -0d1e56bd0e7fbe04a1df9cbfc5ac8b3b linux-3.0/tools/perf/scripts/perl/bin/check-perf-trace-record -002f01b29ee46d7838c43d3a03c02799 linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/typemap -ddf3a679721c41d678d642c7fa324ab6 linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm -39dd8463a0f410d526a4f56c1d0b596b linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm -1684dc43ade3aeb89219ef5ba826b528 linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm -df94b9d697ebf63f94e038177b79ad31 linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/README -79c62345857aff7f890fec80e70d01ac linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL -68890c4d69fde7b0aea586404dbba0d8 linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs -d12be9b8d9b2924ff05edbe515d4d686 linux-3.0/tools/perf/scripts/perl/Perf-Trace-Util/Context.c -9bf5153914d71d035d769a51622e9200 linux-3.0/tools/perf/python/twatch.py -adbcd9183fbce860a52d483e012365ac linux-3.0/tools/perf/perf.h -4b369f06efeda5180b0394902b5d6282 linux-3.0/tools/perf/perf.c -3b78dfa24b5976337033eff60d81b818 linux-3.0/tools/perf/perf-archive.sh -befa13ef6d09a7ab5bd51b01040a1045 linux-3.0/tools/perf/design.txt -9c1005db312825114dac05a84a6b4538 linux-3.0/tools/perf/config/utilities.mak -714102ed9321e5c14b73c39d2ff02a43 linux-3.0/tools/perf/config/feature-tests.mak -dd6d00111244feb2ea7d61727a3582c0 linux-3.0/tools/perf/command-list.txt -19f4ef68eab22eea73bc9f74917d9f60 linux-3.0/tools/perf/builtin.h -9a33e7ec6cb3268636ce98873fe14a09 linux-3.0/tools/perf/builtin-top.c -5ee019068da739beb54bfbff615876f9 linux-3.0/tools/perf/builtin-timechart.c -59ea86b65ee276b7e75a6d75ad41a09d linux-3.0/tools/perf/builtin-test.c -d70d11ee98c0e79040658cc565f4a3e3 linux-3.0/tools/perf/builtin-stat.c -52c39c8eb6e6a5dc68c6fe809d471583 linux-3.0/tools/perf/builtin-script.c -c0db5dc4afe875906bc0fa4e443b266c linux-3.0/tools/perf/builtin-sched.c -369ceec454813c43f04c411f1c4c7f99 linux-3.0/tools/perf/builtin-report.c -14f2247687e5b47084b5628abb71d849 linux-3.0/tools/perf/builtin-record.c -bbc85358a2914ba425c67735bfa4787d linux-3.0/tools/perf/builtin-probe.c -9089968321ea3bb1c63aec792a139ed6 linux-3.0/tools/perf/builtin-lock.c -1b0be23b7ecadb935890a63d5668380d linux-3.0/tools/perf/builtin-list.c -07418f6e009013f2f601b41ee0c1fc52 linux-3.0/tools/perf/builtin-kvm.c -dc6d0e2de313431f98d3fb7f2069f509 linux-3.0/tools/perf/builtin-kmem.c -d8c388b47e1ba901a9f9502d16e99747 linux-3.0/tools/perf/builtin-inject.c -47f22592e969e26b1d225ec44b017d74 linux-3.0/tools/perf/builtin-help.c -e75bb45a9513d57f22a4c77da696c6c6 linux-3.0/tools/perf/builtin-evlist.c -81f2449168f104652b0facfc37414b0c linux-3.0/tools/perf/builtin-diff.c -cc9a982718fb9e49de911d16c357d92a linux-3.0/tools/perf/builtin-buildid-list.c -b3f8a4494000510505f075f6b43efbab linux-3.0/tools/perf/builtin-buildid-cache.c -e5d1f524185c30ed129f2f52755bac23 linux-3.0/tools/perf/builtin-bench.c -556a7e970d9776c7f62b5731349b26dd linux-3.0/tools/perf/builtin-annotate.c -6a5e6e18ba5a8c74a2a3a87fe3007d53 linux-3.0/tools/perf/bench/sched-pipe.c -47c92d40435fffea05c6ab6ce80c19d8 linux-3.0/tools/perf/bench/sched-messaging.c -2c126acc3598ee4173da3c4794ebf97b linux-3.0/tools/perf/bench/mem-memcpy.c -6c32a99dca9aa1376cb436deeb6247ae linux-3.0/tools/perf/bench/mem-memcpy-x86-64-asm.S -014cbae3e3fd89c1bc20f6fdf94dcc0e linux-3.0/tools/perf/bench/mem-memcpy-x86-64-asm-def.h -13dbab6e6aae1a02c6f87c0927658c36 linux-3.0/tools/perf/bench/mem-memcpy-arch.h -d40da208df71e4fb7a06a3965075e952 linux-3.0/tools/perf/bench/bench.h -b2ab7f57bf7a1ed26b059bfa5a3f506f linux-3.0/tools/perf/arch/x86/util/dwarf-regs.c -1c250346ac6cd5f4a09950a317e6033d linux-3.0/tools/perf/arch/x86/Makefile -a09b2b36bb4662dab71b5c01e9d59879 linux-3.0/tools/perf/arch/sparc/util/dwarf-regs.c -1c250346ac6cd5f4a09950a317e6033d linux-3.0/tools/perf/arch/sparc/Makefile -3ac6491e10782befcffe08357ab604ad linux-3.0/tools/perf/arch/sh/util/dwarf-regs.c -1c250346ac6cd5f4a09950a317e6033d linux-3.0/tools/perf/arch/sh/Makefile -7d98c6aabb69d7685599cd5cb9cc41a3 linux-3.0/tools/perf/arch/s390/util/dwarf-regs.c -1c250346ac6cd5f4a09950a317e6033d linux-3.0/tools/perf/arch/s390/Makefile -fb786f11326d27a238de6f63e785b534 linux-3.0/tools/perf/arch/powerpc/util/dwarf-regs.c -1c250346ac6cd5f4a09950a317e6033d linux-3.0/tools/perf/arch/powerpc/Makefile -00591183541c19f66d374635b35ace97 linux-3.0/tools/perf/arch/arm/util/dwarf-regs.c -1c250346ac6cd5f4a09950a317e6033d linux-3.0/tools/perf/arch/arm/Makefile -1a9ae59993e7b3afa664cf7a613953bf linux-3.0/tools/perf/Makefile -d50c40968fbd6b1d8718c1777f92cb3d linux-3.0/tools/perf/MANIFEST -93c4cac962074eabd2f6cd8d1e71b691 linux-3.0/tools/perf/Documentation/perf.txt -c29e7d0624c1a8b1dd876bfaba101fb3 linux-3.0/tools/perf/Documentation/perf-top.txt -d992f18addd44b64c81364d106c66238 linux-3.0/tools/perf/Documentation/perf-timechart.txt -ec8df46a7da46fb4f501976265f6bb21 linux-3.0/tools/perf/Documentation/perf-test.txt -2c6ec87628d25168c11451ec43c89e63 linux-3.0/tools/perf/Documentation/perf-stat.txt -5c078d2b59855efda01367ddb4b53be8 linux-3.0/tools/perf/Documentation/perf-script.txt -70af2c62628d6be1595fcebddf74090e linux-3.0/tools/perf/Documentation/perf-script-python.txt -46114d1d5d37f4b783467fde2a8a7e7f linux-3.0/tools/perf/Documentation/perf-script-perl.txt -89aa7f1008b5771306c78675cdfceb1a linux-3.0/tools/perf/Documentation/perf-sched.txt -eb4eb2fe8a19ae98f1d655f75789d329 linux-3.0/tools/perf/Documentation/perf-report.txt -af3f32892c8a3d58c459f3f3896e931e linux-3.0/tools/perf/Documentation/perf-record.txt -b490300006cc6eccf413195e5c34cbec linux-3.0/tools/perf/Documentation/perf-probe.txt -ea10c6bcd557e127411d68ce64e27edd linux-3.0/tools/perf/Documentation/perf-lock.txt -8824d507310503d0d6aa95ffa5334c81 linux-3.0/tools/perf/Documentation/perf-list.txt -8e90f7d8519a9b924af0c11fe75e0214 linux-3.0/tools/perf/Documentation/perf-kvm.txt -ff6bfa8dceb6ef56fe599feeec371d85 linux-3.0/tools/perf/Documentation/perf-kmem.txt -1d9140fdfd8ebdc35076a3a3b2e46c85 linux-3.0/tools/perf/Documentation/perf-inject.txt -6c37c424f2eb440f27530ec27fac0b55 linux-3.0/tools/perf/Documentation/perf-help.txt -3231edcdf426a1028a5a21927d887c71 linux-3.0/tools/perf/Documentation/perf-evlist.txt -4ee566a8fb667cfeb29e0386cb4fa5d2 linux-3.0/tools/perf/Documentation/perf-diff.txt -c8c55623eb52344b3b68cd4ac92fd9da linux-3.0/tools/perf/Documentation/perf-buildid-list.txt -4bbe27358a402ae6b3cc6c6497e74652 linux-3.0/tools/perf/Documentation/perf-buildid-cache.txt -30d839e7ddb9bc232bd15278bf2a4bb1 linux-3.0/tools/perf/Documentation/perf-bench.txt -8e4619e7705c2ddf1764b0972935cc8c linux-3.0/tools/perf/Documentation/perf-archive.txt -d62feb1cbe803159a746d89e0aaaa5be linux-3.0/tools/perf/Documentation/perf-annotate.txt -2f1169639fd5fca33a754016e2d3b89e linux-3.0/tools/perf/Documentation/manpage-suppress-sp.xsl -83f296aa79af8109808364d2421c1140 linux-3.0/tools/perf/Documentation/manpage-normal.xsl -a19316b1e11e779e61c30dfbea0b92f6 linux-3.0/tools/perf/Documentation/manpage-bold-literal.xsl -d0c51aa8fb59ac8d47ac045858d90543 linux-3.0/tools/perf/Documentation/manpage-base.xsl -31936e12e13942a2f2e667b657cb7611 linux-3.0/tools/perf/Documentation/manpage-1.72.xsl -f633d43ab551f7dba36e767bf6ed1f3a linux-3.0/tools/perf/Documentation/examples.txt -c3356b4a7834944003d08ad169d09618 linux-3.0/tools/perf/Documentation/asciidoc.conf -58c212793d1da971c1945c29db0e3435 linux-3.0/tools/perf/Documentation/Makefile -faeb5b249d8ac1c2e437aee897c3ea99 linux-3.0/tools/perf/CREDITS -7c74275e315752ceacf3c8b738ac7aa2 linux-3.0/tools/perf/.gitignore -b703b236e488fb789abe1dc4a21e4d1f linux-3.0/tools/firewire/nosy-dump.h -e291e490458d6a64bd419ff0e6f742df linux-3.0/tools/firewire/nosy-dump.c -e3d2c299fe53f4ec3c6a6860e822b8bc linux-3.0/tools/firewire/list.h -67354cfe4aa922b3cfc3ebc921af4471 linux-3.0/tools/firewire/decode-fcp.c -ab3c4816906ad529fa30dba43d94a8bc linux-3.0/tools/firewire/Makefile -0bdac03006c7223e763ce3ebfc6b2598 linux-3.0/sound/usb/usx2y/usx2yhwdeppcm.h -10e5905f0aa667b89961df1b7df0af44 linux-3.0/sound/usb/usx2y/usx2yhwdeppcm.c -9a0fd42bf5c863a53546479a106faf4d linux-3.0/sound/usb/usx2y/usx2y.h -31c72b242a96274f8f0458a33ec39e31 linux-3.0/sound/usb/usx2y/usbusx2yaudio.c -e1326b764eee9116901aa066ddcab5b5 linux-3.0/sound/usb/usx2y/usbusx2y.h -1ebf5287ef80f5440957d152251b63ad linux-3.0/sound/usb/usx2y/usbusx2y.c -c921038ef91f5c4cb326ea84e3333415 linux-3.0/sound/usb/usx2y/usbus428ctldefs.h -11e7e4fa26130fe459282881a8beac24 linux-3.0/sound/usb/usx2y/usb_stream.h -99f5df9f5cc2f798a01830da6e1746e3 linux-3.0/sound/usb/usx2y/usb_stream.c -531354385d52e2e41a215d9e3d840303 linux-3.0/sound/usb/usx2y/usX2Yhwdep.h -0193f199d433a0924500bcc9c4d57b2e linux-3.0/sound/usb/usx2y/usX2Yhwdep.c -666ddef60c26da2ede7927c88eef312c linux-3.0/sound/usb/usx2y/us122l.h -6340574b5ff23673a1b2eca68ddf19a9 linux-3.0/sound/usb/usx2y/us122l.c -cd385bbdac95b601a1d425505cddf069 linux-3.0/sound/usb/usx2y/Makefile -c67d7ea6556a782f051f207d5925dbde linux-3.0/sound/usb/usbaudio.h -fb6ecbfc04e97a4c85e2eb2707e052ba linux-3.0/sound/usb/urb.h -05922156a57a876fe27aecff23fd8942 linux-3.0/sound/usb/urb.c -428238069b9ad00709112596e3956b66 linux-3.0/sound/usb/quirks.h -9480f1963c4e0e189d35e82df833a8a0 linux-3.0/sound/usb/quirks.c -040ab807b72561e4739dc8205a48a0b6 linux-3.0/sound/usb/quirks-table.h -ec80cf5b9d1fd20ed379530610b7584c linux-3.0/sound/usb/proc.h -a168f2e76ea9d342b3b8890ae29cf6a7 linux-3.0/sound/usb/proc.c -d1b5d46c62a18f67a983feaad933b9ab linux-3.0/sound/usb/power.h -76da4253e3c07626dfaee30766107ad2 linux-3.0/sound/usb/pcm.h -cdd4fc911c96b352dca8624fbaf6cd6a linux-3.0/sound/usb/pcm.c -944c77c7b5dea1afb2d7c29d018a9bdf linux-3.0/sound/usb/mixer_quirks.h -43b3f282acecf30ec809f0d3e9e0ff07 linux-3.0/sound/usb/mixer_quirks.c -e39d2dad088aa936c3e010de9a290b6f linux-3.0/sound/usb/mixer_maps.c -6f2bc0def2d854379e8ad47af4f3e139 linux-3.0/sound/usb/mixer.h -882d5b7b4c6853cfca112ae44205a2b7 linux-3.0/sound/usb/mixer.c -8c032db196836c5b0c9f00cf4f9577e7 linux-3.0/sound/usb/misc/ua101.c -b033844feb8127eae70a301b27c2afce linux-3.0/sound/usb/misc/Makefile -4f2ad22746a841f57c30d1b103a6e99f linux-3.0/sound/usb/midi.h -bc975de6f3dea59ef0da852946e02816 linux-3.0/sound/usb/midi.c -ae453b21a0d4766620e9b7fc8ff732c4 linux-3.0/sound/usb/helper.h -a0cb66e1abf7c5f334e928232d8b1551 linux-3.0/sound/usb/helper.c -a8309597e49a387034ffe70e1b4ff35d linux-3.0/sound/usb/format.h -967646dcbd64eebb5f08a10d12e60918 linux-3.0/sound/usb/format.c -419b72e17464db58b8a4cd302d415cf5 linux-3.0/sound/usb/endpoint.h -6a30480c77048e1e54454976d479ac20 linux-3.0/sound/usb/endpoint.c -c9aba805def6716594b1923a6f43ab51 linux-3.0/sound/usb/debug.h -7c30c007ebcf3a8988d06b7fb696cf49 linux-3.0/sound/usb/clock.h -1e50575a0573d6d5ddd1d531768e1a51 linux-3.0/sound/usb/clock.c -32182a29a7b5bd2d4d1290f015896e76 linux-3.0/sound/usb/card.h -8970be9d950272c44de5f7d2e250c82e linux-3.0/sound/usb/card.c -d7e2d4d04b59260f7345af3d78b38831 linux-3.0/sound/usb/caiaq/midi.h -5c6ab8f48ff68c484ff3bf1ff738eb47 linux-3.0/sound/usb/caiaq/midi.c -737d2335ee636199a673768c3b38561e linux-3.0/sound/usb/caiaq/input.h -f989f57accb68826d468f42dbb29e722 linux-3.0/sound/usb/caiaq/input.c -49ca099c283ce65c275798c49f9f9af2 linux-3.0/sound/usb/caiaq/device.h -23de2aabbbacd2b4b19576c6f1e77aa8 linux-3.0/sound/usb/caiaq/device.c -6388fec88dd65e51e3203af0dd945dad linux-3.0/sound/usb/caiaq/control.h -4de3f2c0ecf9720451dee19ec82522e0 linux-3.0/sound/usb/caiaq/control.c -a46352ebaea75b90541e1e3cb58663b3 linux-3.0/sound/usb/caiaq/audio.h -c6d230ac02003f347cecdc772aca6c01 linux-3.0/sound/usb/caiaq/audio.c -100b4b2301f6081daefd0784e3a3fa16 linux-3.0/sound/usb/caiaq/Makefile -c06316d3cd7ea3173dc66755e038746b linux-3.0/sound/usb/Makefile -d218fc33bfbc493e80ef28a9f9d0a74b linux-3.0/sound/usb/Kconfig -049a54881438e49d087f8d03fad199da linux-3.0/sound/usb/6fire/pcm.h -c0df608d089594ae0361cbd6e6775802 linux-3.0/sound/usb/6fire/pcm.c -73576ff017218bdf6b80a0dfcca280a0 linux-3.0/sound/usb/6fire/midi.h -74ed60b18280f5d6fe45c8a416e7462b linux-3.0/sound/usb/6fire/midi.c -9c78ebf51165c74edf08e5791bc7fb9c linux-3.0/sound/usb/6fire/firmware.h -eda8c202e8579b4cfc10bef75f19b994 linux-3.0/sound/usb/6fire/firmware.c -b8fcc8548df605e694ecbac71a994d8d linux-3.0/sound/usb/6fire/control.h -ccec83b1802add4bb6c82513f5620532 linux-3.0/sound/usb/6fire/control.c -04d06f72f260922d8ca593206018b986 linux-3.0/sound/usb/6fire/common.h -7e6bf829906b5906987dfbe28e70670a linux-3.0/sound/usb/6fire/comm.h -3503662d76989cb325d3486a613dcbbf linux-3.0/sound/usb/6fire/comm.c -9e4c3e4853c9b80f1142b1f58eda6dc6 linux-3.0/sound/usb/6fire/chip.h -5b5124692fe3688f1b5a4648e382913d linux-3.0/sound/usb/6fire/chip.c -71e72ec0f50342023c1cf6d870a3d50a linux-3.0/sound/usb/6fire/Makefile -c4ec639f36f653d26d6a005a96327196 linux-3.0/sound/synth/util_mem.c -72bafbb71cd70bdf41ee43efe77b8240 linux-3.0/sound/synth/emux/soundfont.c -54a7cf4b5a92ce473cbe9eaf47ec6606 linux-3.0/sound/synth/emux/emux_voice.h -89176744ebc679999fbfe29b1fd09876 linux-3.0/sound/synth/emux/emux_synth.c -0b35dec1007d98b43a0d9eb6be8d8391 linux-3.0/sound/synth/emux/emux_seq.c -a52028a135ae20101b63299b3690116a linux-3.0/sound/synth/emux/emux_proc.c -c107707560bcd87ce20389ad4c35c6f4 linux-3.0/sound/synth/emux/emux_oss.c -e0cb274e99af2295570667baf9bc4799 linux-3.0/sound/synth/emux/emux_nrpn.c -e71cc7a527b15fa01b08a8e95917c567 linux-3.0/sound/synth/emux/emux_hwdep.c -9db8487dc3981cd7a528b0aeadb83bd0 linux-3.0/sound/synth/emux/emux_effect.c -815cf03bccdac0009ba9ef1a0f57e5b5 linux-3.0/sound/synth/emux/emux.c -6ca5c6817efad521dcb8e21359a4bb1a linux-3.0/sound/synth/emux/Makefile -0521b52ac764d8d0c98ad720c59e5ae0 linux-3.0/sound/synth/Makefile -a3cb59cce1d7a47f5771c2f11706f5f4 linux-3.0/sound/spi/at73c213.h -6bfbd68287170e618b133d58804591db linux-3.0/sound/spi/at73c213.c -81b963606e39d26aba2c97d553109780 linux-3.0/sound/spi/Makefile -92f6027a13f300bc7637f123c5ef54f1 linux-3.0/sound/spi/Kconfig -649ea4d7ab406c76f9d9ee6db61ce7c2 linux-3.0/sound/sparc/dbri.c -5033b20a760e403bee67011b570de181 linux-3.0/sound/sparc/cs4231.c -6b710da3384470646a817bceff52cbb0 linux-3.0/sound/sparc/amd7930.c -d7fc1e82e4dee3e12916d352b905969b linux-3.0/sound/sparc/Makefile -734d1c7369f9364947737f0e68207824 linux-3.0/sound/sparc/Kconfig -f817c5d398488344b14afe6a12b3dd6b linux-3.0/sound/sound_firmware.c -b39a7ac99ded21a1bf5bf92bf8da3650 linux-3.0/sound/sound_core.c -ef81d1f0d189737155023d0cd3778ab8 linux-3.0/sound/soc/txx9/txx9aclc.h -1292957473dcbf06601036c21d6e0523 linux-3.0/sound/soc/txx9/txx9aclc.c -b47dc636a225b84ee6037bd11dd72a28 linux-3.0/sound/soc/txx9/txx9aclc-generic.c -1b1b2e857401013ce70dfe0c9111336c linux-3.0/sound/soc/txx9/txx9aclc-ac97.c -6e9356a04e631fd557c7f53d7001f33c linux-3.0/sound/soc/txx9/Makefile -5e4433721cd5eb73591c180d0f1e85fc linux-3.0/sound/soc/txx9/Kconfig -6ddbac39284ed646318f15da12294bed linux-3.0/sound/soc/tegra/trimslice.c -6fe70940131cac002f8cec8d09f3a676 linux-3.0/sound/soc/tegra/tegra_wm8903.c -8cab9e6ff04e786c03a5132aff350eb4 linux-3.0/sound/soc/tegra/tegra_pcm.h -e1a511cbb99c0f8873213a7d32637fed linux-3.0/sound/soc/tegra/tegra_pcm.c -b71bbcb64a922d1e7bc71f9edf04a688 linux-3.0/sound/soc/tegra/tegra_i2s.h -5263e2686c0329618bbfc585e7e9ec3b linux-3.0/sound/soc/tegra/tegra_i2s.c -ea58d0b5b0a6252ed49237e9341facf5 linux-3.0/sound/soc/tegra/tegra_das.h -d6224850bcf007f589647e2f02fd45b2 linux-3.0/sound/soc/tegra/tegra_das.c -6b12d9809409a882f74640b686e4a2bf linux-3.0/sound/soc/tegra/tegra_asoc_utils.h -972bfd0ccc188cbb4fe1b8241baedfee linux-3.0/sound/soc/tegra/tegra_asoc_utils.c -ed62a352e765169e88482741ee23b97b linux-3.0/sound/soc/tegra/Makefile -619d90a69ff3be34c895d22c2a76156a linux-3.0/sound/soc/tegra/Kconfig -597d8025d17f846c9bdd58bd956adc5a linux-3.0/sound/soc/soc-utils.c -4a51e6b715c0a7be2aedbf85128be5ab linux-3.0/sound/soc/soc-jack.c -541038cea8bc3eb36d0c2d27b660a46a linux-3.0/sound/soc/soc-dapm.c -12548ac517019b542f0a5b1b681749b8 linux-3.0/sound/soc/soc-core.c -04437e5b6380703764c607b778e6438d linux-3.0/sound/soc/soc-cache.c -0ae012a4139d887af7db7deb3312dead linux-3.0/sound/soc/sh/ssi.c -1de11a4592df561a1002edf631a5745b linux-3.0/sound/soc/sh/siu_pcm.c -6d1b30b96c850d1b5d31240cdd07446f linux-3.0/sound/soc/sh/siu_dai.c -5a19537d39a3cc59a769a81633a26844 linux-3.0/sound/soc/sh/siu.h -801cc12002929d70426ecd1eb9dea514 linux-3.0/sound/soc/sh/sh7760-ac97.c -ee0491934d2710204291fd8114358888 linux-3.0/sound/soc/sh/migor.c -2b2d108f35faa04a36b01d24d9ddf27f linux-3.0/sound/soc/sh/hac.c -69e28a2792c9e450c80f10a5d96fdfaa linux-3.0/sound/soc/sh/fsi.c -8f9afee5dcb8c4cc2d3a36be95c06982 linux-3.0/sound/soc/sh/fsi-hdmi.c -52cb0623384e8368b2ecc05ed82e2106 linux-3.0/sound/soc/sh/fsi-da7210.c -ac1ee3366626d33395d02e634c26c67c linux-3.0/sound/soc/sh/fsi-ak4642.c -b9459c7774374b8d6528b8eb33963ff3 linux-3.0/sound/soc/sh/dma-sh7760.c -ee325a9e7a9d9ed448f4c51fee7b0ab9 linux-3.0/sound/soc/sh/Makefile -98f88fbb8c2f924c8ef20a5753bc8dbb linux-3.0/sound/soc/sh/Kconfig -50a1044686733db37db36cebeb0a3d17 linux-3.0/sound/soc/samsung/speyside.c -18d588e382d8a81ea31ab3bed3c82fea linux-3.0/sound/soc/samsung/spdif.h -414285e492187173197dd0aadec984f3 linux-3.0/sound/soc/samsung/spdif.c -62267da60a4843c94d23a37e7b27b6b6 linux-3.0/sound/soc/samsung/smdk_wm9713.c -96be8ff1e10627f3bb357b815cbdb7ce linux-3.0/sound/soc/samsung/smdk_wm8994.c -281679b2abb348c2d264b4c5c0fc6ed0 linux-3.0/sound/soc/samsung/smdk_wm8580pcm.c -08947dfc33bf85d1b495f29e9b3d5190 linux-3.0/sound/soc/samsung/smdk_wm8580.c -1143e0eceea27b959616bab0a294bb52 linux-3.0/sound/soc/samsung/smdk_spdif.c -b31544606114a5bd37a774eb782ab62f linux-3.0/sound/soc/samsung/smdk2443_wm9710.c -9d2a2adbe4b89603d60b7dd1ee6b0a23 linux-3.0/sound/soc/samsung/smartq_wm8987.c -a007e8fe435f46d7e9248d944100b8de linux-3.0/sound/soc/samsung/s3c24xx_uda134x.c -34cdcefcc0184bfa82439fcede7f3633 linux-3.0/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c -60a5570c6f175e4ca3ef69eb529120c4 linux-3.0/sound/soc/samsung/s3c24xx_simtec_hermes.c -0126e0f0ef7ef99496649527db8d0296 linux-3.0/sound/soc/samsung/s3c24xx_simtec.h -4d0ac3005b15d21001974d06d4a47a80 linux-3.0/sound/soc/samsung/s3c24xx_simtec.c -54804e1eeea72cf1d4bc13657155b4ce linux-3.0/sound/soc/samsung/s3c24xx-i2s.h -4ffb0594924b1832039e176f5b0bd5a1 linux-3.0/sound/soc/samsung/s3c24xx-i2s.c -211afd814ee26f6f83f40f24667bc621 linux-3.0/sound/soc/samsung/s3c2412-i2s.h -4d5c243a2ad9c8f80e116c80b7b9cdb0 linux-3.0/sound/soc/samsung/s3c2412-i2s.c -4045a8c63b8daa62a280a0423c73d751 linux-3.0/sound/soc/samsung/s3c-i2s-v2.h -d07bad2d05d73209861ddd02c6f4dc29 linux-3.0/sound/soc/samsung/s3c-i2s-v2.c -502d233e7218db0296ef00d3ad672a8e linux-3.0/sound/soc/samsung/rx1950_uda1380.c -16a6870a701c4244b4a554e7b5a612f7 linux-3.0/sound/soc/samsung/regs-i2s-v2.h -ea6897fb5ebffdae2a64996f83f8a88c linux-3.0/sound/soc/samsung/pcm.h -8ac98cc6735893a2781530bfaab9ffde linux-3.0/sound/soc/samsung/pcm.c -8f07706975b04eb51a155a12d7cc37f1 linux-3.0/sound/soc/samsung/neo1973_wm8753.c -ce96a834e709748f1779f7798bef1bc7 linux-3.0/sound/soc/samsung/ln2440sbc_alc650.c -35e5db2c8365c5fa11b41e4986214427 linux-3.0/sound/soc/samsung/jive_wm8750.c -867a5a02802e75593e1f31d4a0d00af6 linux-3.0/sound/soc/samsung/i2s.h -48f7e9d33094c7c3cdfa3eb9c234e5be linux-3.0/sound/soc/samsung/i2s.c -3f148c5ccc8bb82f496df75c0717c6c5 linux-3.0/sound/soc/samsung/h1940_uda1380.c -d2129620bf4afb7e79cd8322d906d065 linux-3.0/sound/soc/samsung/goni_wm8994.c -793b0437551be4b2857bb27369b80767 linux-3.0/sound/soc/samsung/dma.h -32b3e1eb0fe577eed66d578be4126c9a linux-3.0/sound/soc/samsung/dma.c -e453f93a6ab5c3b44973b1d4d75a8245 linux-3.0/sound/soc/samsung/ac97.c -26fac6899b16ec8e2060f53280b957