Compare commits

...

388 Commits
v2.2 ... master

Author SHA1 Message Date
Jaromil
0d06c994cf fix tests to run also when swap present 2024-05-12 22:09:46 +02:00
Jaromil
6c383ffd64 move unfinished portable veracrypt version in extras 2024-05-12 21:47:57 +02:00
nerun
ddb2de6072 Updated copyright year in 'generate_translatable_strings' script. 2024-05-06 15:06:56 +02:00
nerun
3398b7bf89 Updated extras/translations/README. 2024-05-06 15:06:56 +02:00
nerun
abe5704658 Updated all language files to Tomb v2.10 (.po/.pot). 2024-05-06 15:06:56 +02:00
nerun
d0b84d78a0 Fixed tomb listing failure message: localization friendly. 2024-05-06 15:06:56 +02:00
Melroy van den Berg
91adbbe183 Update copyright in translation source 2024-01-26 00:38:37 +01:00
Melroy van den Berg
766cd27c1b Update copyright lines 2024-01-26 00:38:13 +01:00
Narrat
dee2b0f8c4 list_processes: use lsof to list processes
Parsing the output from lsof had possibilities for race conditions.
Either due to short lived processes or issuing "tomb ps" from a terminal which cwd is from inside the tomb.
This would spit out available users on the system.
To avoid this use the lsof output directly.

In the future formatting could be reintroduced via commands like
"lsof +D "$tombmount" -F Lc" or "lsof +D "$tombmount" -F Lc0".

This fixes #503
2024-01-26 00:37:46 +01:00
nerun
13eeef7c6c manpage: replaced 'mlocate' to 'mlocate/plocate', because of commit 59d7331. 2024-01-26 00:30:58 +01:00
nerun
194d60fe9a manpage: better formatting of .EX/.EE macros 2024-01-26 00:30:58 +01:00
nerun
7f91cc917d manpage: fixed typo in "bind-hooks" 2024-01-26 00:30:58 +01:00
nerun
09d981f0fc manpage: 80 columns line break in PASSWORD INPUT. 2024-01-26 00:30:58 +01:00
vladislav doster
e1afecb832 fix: remove duplicate help in subcommands_opts 2024-01-26 00:28:50 +01:00
Melroy van den Berg
e97c088a26 Update copyright in README 2024-01-24 08:42:08 +01:00
Melroy van den Berg
9385ddee43 Update download link 2024-01-24 08:42:08 +01:00
27792f4421 Fix umount issue when path have spaces in directory or file name 2024-01-12 15:09:47 +01:00
8be3163022 Fix spaces in bind-hooks paths 2024-01-12 15:09:47 +01:00
0rtz
21da75adab Use cat instead of _cat
_cat is not defined/autoloaded anywhere, so use plain unix cat utility
to avoid getting "command not found" error
2023-12-16 15:52:34 +01:00
bittin1ddc447d824349b2
a0b633fb77 Translated using Weblate (Swedish)
Currently translated at 100.0% (290 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/sv/
2023-12-07 02:33:37 +01:00
Sven Geuer
f9ec17d126 Fix some table mimicking lines to honor the recent line width 2023-09-30 22:55:28 +02:00
Sven Geuer
dcdf4cb3bb Fix operators to compare numerically, not lexicographically
The operator < compares two strings lexicographically resulting in that a 100MB tomb is considered smaller than 47MB or 18MB.

Closes #489
2023-09-30 22:54:35 +02:00
Jaromil
0c8d4cf477 short README, info moved to homepage 2023-09-20 16:52:12 +02:00
Jaromil
51452e4e6a documentation update for release
long due, was ready in november 2022 and basically left unchanged at
that stage, except the deprecation of veracrypt in the experimental
portable branch, which is not included in the stable release.
2023-09-18 16:04:19 +02:00
nerun
58035cf14a Added README to extras/translations. 2023-08-24 21:21:18 +02:00
nerun
06ea734878 Added Brazilian Portuguese (pt_BR) translation (for Tomb 2.10). Also
created tomb.pot template for version 2.10.
2023-08-24 21:21:18 +02:00
nerun
347462cb0c Updated Brazilian Portuguese translation (pt_BR). Also updated Makefile
to include this translation.
2023-08-24 21:21:18 +02:00
nerun
cd9fa46964 Translations: updated 'es', 'fr', 'it' and 'ru.po' to line up help message
(PR #481 and Jaromil request in PR #483).
2023-08-24 21:21:18 +02:00
nerun
d8989e1955 extras/kdf-keys: fixed hexencode warning caused by wrong type. 2023-08-24 21:11:42 +02:00
nerun
59d7331cb0 Fixed issue with mlocate. Now tomb accepts also plocate for search/index. 2023-06-27 22:47:10 +02:00
Dyne.org foundation
4d72a2180a Added translation using Weblate (Portuguese (Brazil)) 2023-06-27 16:53:44 +02:00
nerun
7249afe98e tomb help: "engrave" lined up (added +5 spaces). 2023-06-27 07:30:31 +02:00
nerun
0eed3cf321 doc: updated tomb_manpage.pdf with new version. 2023-06-27 07:30:31 +02:00
nerun
e316ef22cc manpage: fixed wrong information about --kdfmem. 2023-06-26 03:09:54 +02:00
nerun
82e5342334 extras/gtomb: inserted a "wait" statement in the dig, forge, lock and open functions;
spelling standardization (canceled to cancelled);
simplified the call to MONMORT icon inside zenity functions;
zenity warning function changes;
fixed missing dots.
2023-06-25 19:37:27 +02:00
nerun
4e3221e937 extras/gtomb: replaced soft with hard tabs to maintain code standard. 2023-06-25 19:37:27 +02:00
Narrat
364a457fbc Remove unused check on python2 2023-06-24 04:35:48 +02:00
Alex
571cdeafbf Translated using Weblate (French)
Currently translated at 98.6% (286 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2023-03-21 16:59:45 +01:00
Maxime Leroy
b9cf58054a Translated using Weblate (French)
Currently translated at 96.2% (279 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2023-03-21 16:59:45 +01:00
Selene ToyKeeper
646d2c33fd Use $SUDO_ASKPASS if defined. Allows non-terminal sudo password prompts.
The sudo program checks this env var and uses it, but only if --askpass
was given at the command line, or if it thinks there is no terminal.
But the terminal detection is unreliable, so give it --askpass if there
is an askpass program defined in the environment.

To try it, simply "export SUDO_ASKPASS=/usr/bin/ssh-askpass" before
running tomb.

For me personally, this makes it possible to have a hotkey to run
"pass" and "pass tomb" related commands.  Without this patch, invoking
via hotkey causes my window manager to lock up while waiting for a
password on the VT where Xorg was started... and since it's locked up,
I can't change to the VT to enter the data it's waiting for.  So I have
to log in via ssh from another host to recover it.

So, instead of locking up... now it can use a GUI askpass program.
2023-03-21 16:58:18 +01:00
nerun
713cfd3071 extras/gtomb: SWAPOFF defaults to false. 2023-03-21 16:54:57 +01:00
nerun
5eaa92e614 extras/gtomb again: sudo no longer stored in variable. 2023-03-21 16:54:57 +01:00
nerun
d65a3f1586 extras/gtomb severe update. 2023-03-21 16:54:57 +01:00
Jaromil
b5eae55c8b portable: remove strtosize and use MiB 2022-12-21 10:08:33 +01:00
Jaromil
0130fcf86d update docs 2022-11-21 09:44:21 +01:00
Jaromil
30d2e92a77 sponsor buttons 2022-11-16 15:25:42 +01:00
Jaromil
0d004f312d improve build status badges on readme 2022-11-16 15:25:42 +01:00
Jaromil
b14be5022e fix readme build status links 2022-11-16 15:25:42 +01:00
Jaromil
ed841b9f32 update CI scripts 2022-11-16 15:25:42 +01:00
Jaromil
6e8ef0a29a documentation update for a new release 2022-11-16 15:25:42 +01:00
Jaromil
5772d39e19 updated portable readme with links to milestones and more info 2022-11-14 10:40:15 +01:00
Jaromil
1ae5845c61 portable tomb tests in ci 2022-11-14 10:40:15 +01:00
Jaromil
fce89023a5 try to use native ubuntu matrix on github ci
remove doas tests

removed comparison from bind-hook test
2022-11-14 10:40:15 +01:00
Jaromil
be911b1e16 first basic implementation of portable tomb
this new "flavor" of tomb uses veracrypt for mounted volumes and
POSIX sh only for its scripting, is a work in progress and still
lacks full functionality, but provides a proof-of-concept to be
developed further if needs arise.
2022-11-13 22:00:41 +01:00
Chris Vogel
12684e6740 cmd lock: extend parameters to --filesystem
The --filesystem option can be used  to  specify
an  alternative  filesystem used to format the tomb, in place of the default "ext4".

Beside "btrfs" now the following parameters to --filesystem are supported:

"ext3"    using operating system defaults
"ext4"    using operating system defaults
"btrfs"   for tombs >= 47MB using operating system defaults
"btrfsmixedmode"    for tombs >=18MB btrfs mixed mode (see mkfs.btrfs(8))
"ext3maxinodes"     ext3 with a maximum of inodes (for many small files)
"ext4maxinodes"     ext4 with a maximum of inodes (for many small files)

These changes help use scenarios in which there is a great number of small files
and/or directories in a small filesystem, like e.g. the pass-tomb extension to pass.
2022-10-28 16:19:28 +02:00
Jaromil
42390b78c0 add a notice and link to tomb3 in readme 2022-10-24 15:28:05 +02:00
Valentin Heidelberger
3639b2e0ec fix typo Succesfully -> Successfully 2022-10-20 12:25:40 +02:00
Chris Vogel
8502bdc722 Adjusted messages returned about zramswap
As explained to me here https://github.com/dyne/Tomb/pull/447#discussion_r999777746
it would be bad to change existing messages. Reverted to the already existing messages
and added new messages about zramswap.
2022-10-20 12:22:23 +02:00
Chris Vogel
e463a6e600 fixed patch error
Something went wrong when I tried to move my changes from my installation
into the git. Now the tests complete successfully.
2022-10-20 12:22:23 +02:00
Chris Vogel
6af298e15f recognize zram as swap
Check if unencrypted swap is zram. If it is zram check whether a writeback to
disk is configured.

Unencrypted zramswap not written to disk is accepted.

ToDo (as for other unencrypted swap): check if the writeback happens on an
already encrypted disk/partition.
2022-10-20 12:22:23 +02:00
Jaromil
1655fd5a99 add tomb/.cleanexit lock to check on clean umount 2022-10-11 08:26:37 +02:00
Jens Rischbieth
8782dab9ed Update Dockerfile
Building the image fails as pinentry doesn't exist in the repository, only pinentry-curses. Image building works w/ only pinentry-curses.
2022-07-20 22:43:01 +02:00
AHOHNMYC
3b44a6be3a Translated using Weblate (Russian)
Currently translated at 99.3% (286 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/ru/
2022-05-26 01:27:24 +02:00
Francisco Serrador
7a1b699cea Translated using Weblate (Spanish)
Currently translated at 100.0% (288 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/es/
2022-05-26 01:27:24 +02:00
luzhen
85af9459aa Translated using Weblate (French)
Currently translated at 74.6% (215 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2022-05-26 01:27:24 +02:00
Jaromil
f4e2ae2f97 add win11 compatibility notice to readme 2022-04-15 11:07:40 +02:00
Jaromil
62806769af disable broken gpg recipient tests 2022-04-13 22:53:36 +02:00
Jaromil
8be3e7d2a0 gpg remove check for secret key 2022-04-13 22:53:36 +02:00
Jaromil
97343cf590 fix detection of gpg recipient id validity 2022-04-13 22:53:36 +02:00
Jaromil
60034b0b55 update test docker to devuan chimaera
install pinentry curses and gpg

update github action checkout v3
2022-04-13 22:53:36 +02:00
Narrat
b7822afaf0
lo_mount: check for loop support needs privilege escalation (#437)
If there is no free loop device, the call of loopsetup -f will create one and return it. For this it needs privilege escalation.
It doesn't need those, if there is already an used device, but that cannot be guaranteed.

Closes #436
2022-04-07 15:20:38 +02:00
Jaromil
6955719f04 fix shellcheck linter 2022-03-03 10:27:35 +01:00
Jaromil
8ceeca8769
KDF support for argon2 memory intensive algorithm (#432)
* KDF support for argon2 memory intensive algorithm

following many requests, here is support for argon2 KDF to be switched
on using --kdftype argon2 (--kdf iterations --kdfmem memory)

effective memory required is 2^memory KiB, defaults to 18 (262 MiB)
number of iterations are still specified as --kdf argument

requires the argon2 reference C implementation from P-H-C
also requires tomb-kdb-pbkdf2-gensalt in extras/kdf-keys

example usage:
tomb forge -k argon.key --kdf 10 --kdftype argon2

* manual updates for argon2
2022-02-20 22:05:01 +01:00
Jaromil
03c93ef976
Sudo loopback improve (#435)
* small improvements to loopback setup and --sudo

* support reading hostname from file

also tolerate not finding the hostname (fill localhost)

address #428

* cleanup and support sup,sud,pkexec
2022-02-20 21:57:05 +01:00
Jaromil
5a5eb6ddcf
wrap all references to $tombmount string into quotes (#434)
this may fix whitespace issues referred by #433 and previously related
to bind mounts as of #222
2022-02-20 21:10:47 +01:00
Jaromil
9323c1caf8 reduce noise of superuser password requests
messages downgraded to verbose mode (fix #431)

also updated dates and version
2022-01-31 04:44:10 +01:00
Weblate (bot)
f7046c5941
Translations update from Weblate (#401)
* Translated using Weblate (French)

Currently translated at 74.6% (215 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Added translation using Weblate (Chinese (Simplified))

* Translated using Weblate (French)

Currently translated at 76.0% (219 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Translated using Weblate (French)

Currently translated at 76.3% (220 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Translated using Weblate (French)

Currently translated at 78.4% (226 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

* Translated using Weblate (French)

Currently translated at 95.4% (275 of 288 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/

Co-authored-by: luzhen <luzhen@uniontech.com>
Co-authored-by: Dyne.org foundation <translate@dyne.org>
Co-authored-by: Maxime Leroy <lisacintosh@gmail.com>
2022-01-30 16:21:18 +01:00
Artur Malimonov
b6ffe1a2f1
GitHub Actions CI (#430)
Add Github Actions CI config
2022-01-08 01:26:51 +01:00
Jaromil
84ef4bef4b remove pkexec autodetection 2021-10-21 11:49:54 +02:00
Jaromil
e0ba8c5f4d wider support for privilege escalation tools
now supporting also pkexec (polkit daemon), suckless' sup and
sud.dyne.org

pkexec is autodetected when polkit is running

manpage documents the --sudo flag which overrides any autodetection
2021-10-20 16:27:27 +02:00
Jaromil
90eec3d830 adopt external utility cat instead of zsh builtin
fix #426
2021-10-13 15:46:34 +02:00
Damien Ready
930b414889 Correct some typos 2021-10-01 18:32:44 +02:00
Jaromil
61a9d1a420 updates to dockerfile 2021-08-10 10:57:43 +02:00
timvisee
d2d35bc8db Quiet cryptsetup when opening a Tomb with -q provided 2021-07-21 09:06:22 +02:00
timvisee
585af6a61b Quiet fsck when opening a Tomb with -q provided 2021-07-21 09:06:22 +02:00
Jaromil
2a744fe89d quick install instructions for qt tray
fix #413
2021-06-24 07:42:29 +02:00
Jaromil
b235f16ce4 correct shell sequence to open without tomb script
fix #372 thanks to @grcancelliere
2021-06-24 07:36:50 +02:00
heat-wave
ce521ed2e2 Validate user-supplied sudo alternative (in name only) 2021-04-15 12:27:26 +02:00
heat-wave
087ecd25a2 Restrict access to doas.conf 2021-04-15 12:27:26 +02:00
heat-wave
24a89b680d Disable sphinx tests to test loop devices limit hypothesis 2021-04-15 12:27:26 +02:00
heat-wave
3860487a0b Fix typos in doas config and --sudo opt definition 2021-04-15 12:27:26 +02:00
heat-wave
61386ca646 Support for sudo alternatives such as doas 2021-04-15 12:27:26 +02:00
Denis Roio
ae21619d04
Merge pull request #408 from heat-wave/fix/sphinx-test-configs
Fix configs and dockerfile to enable sphinx in tests
2021-01-31 00:10:22 +01:00
Jaromil
7f2e22c517 fix read-only opening of tombs using -o ro
skip touch, chown and some minor operations when read-only
2021-01-25 18:26:49 +01:00
Jaromil
fb3ffcec03 manpage mention of fallocate(1) for faster dig 2021-01-25 15:14:31 +01:00
heat-wave
815b8f4218 Fix configs and dockerfile to enable sphinx in tests 2021-01-23 19:16:29 +00:00
Jaromil
f35ad11e3f updated documentation for release 2021-01-04 22:00:29 +01:00
Denis Roio
c0d1a7584d
Merge pull request #406 from mcrapet/dig_sudo
dig/forge unecessary sudo
2021-01-04 10:51:40 +01:00
Matthieu Crapet
c5701793fb minor typo/formatting fixes
Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:22:06 +01:00
Matthieu Crapet
02812f4c06 tomb forge: useless sudo and chown
Depending script invokation, behavior is not exactly similar.
Assuming that if SUDO_USER is set, the _sudo invokation can be dropped (EUID=0).
In the other case, user has created file, owner is already good, don't call chown.

Preparation:
$ tomb dig foo.tomb -s 10

Method 1:
$ sudo tomb forge foo.tomb.key -v

Method 2:
$ tomb forge foo.tomb.key -v
... ask user password to gain superuser privileges
...
Sorry, user <username> is not allowed to execute '/bin/chown <uid>:<gid> foo.tomb.key' as root on <hostname>.

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:20:26 +01:00
Matthieu Crapet
99f10bf215 tomb dig: useless sudo and chown
Depending script invokation, behavior is not exactly similar.
Assuming that if SUDO_USER is set, the _sudo invokation can be dropped (EUID=0).
In the other case, user has created file, owner is already good, don't call chown.

Method 1:
$ sudo tomb dig foo.tomb -s 10 -v

Method 2:
$ tomb dig foo.tomb -s 10 -v
... ask user password to gain superuser privileges
...
Sorry, user <username> is not allowed to execute '/bin/chown <uid>:<gid> foo.tomb' as root on <hostname>.

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:12:55 +01:00
Matthieu Crapet
859a5c7783 TMPPREFIX is not supposed to be a directory
http://zsh.sourceforge.net/Doc/Release/Files.html
TMPPREFIX defaults to /tmp/zsh (for zsh shell)

Note: --tmp command line switch is not documented?

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:09:51 +01:00
Matthieu Crapet
312915b4b3 fix potential wrong _USER value
"id -u" gives then uid not a the name.
https://man7.org/linux/man-pages/man1/id.1.html

Signed-off-by: Matthieu Crapet <mcrapet@gmail.com>
2021-01-02 10:09:51 +01:00
Jaromil
d227695778 add support for tombs formatted with the btrfs filesystem
basic functionality working for open, close and resize

still needs test coverage and some minor checks
2020-12-29 13:50:04 +01:00
Jaromil
8d5a85658f explicit return codes for all operations 2020-12-29 11:52:23 +01:00
Jaromil
0ac5a34c20 close luks mapper and end with an error on lock format failures 2020-12-29 10:56:20 +01:00
Jaromil
d8360688b3 fix wrong comparison of $pass_asked left by last commits
fix #404
2020-12-29 10:52:16 +01:00
Jaromil
7a81ad032d add zsh to version output 2020-12-29 08:58:14 +01:00
Denis Roio
9f30f7da89
Merge pull request #403 from catleeball/cflags
Read CFLAGS in kdf-keys makefile
2020-12-29 08:07:09 +01:00
🎶🎷🐛 Lee Ball
6d87b7e355 Read CFLAGS in kdf-keys makefile
Added $(CFLAGS) in the kdf-keys makefile to allow users to specify
additional build flags.
2020-12-23 22:03:49 -08:00
Jaromil
b6fff10c2a add file among dependencies in the INSTALL guide
fix #396
2020-12-16 12:52:17 +01:00
Jaromil
b0de6e07b2 adopt everywhere -z test to check when variables are empty
check works both for empty ("") and non-existing vars and is a fix
for regression #398 to work on older Zsh versions. It is normalized
through all tomb's code.
2020-12-15 18:22:38 +01:00
Jaromil
5199bef4a6 documentation for release 2020-11-27 18:40:17 +01:00
Jaromil
45b144d213 print messages by default on stderr 2020-11-25 14:47:54 +01:00
Jaromil
c13f38266e fix tomb list output 2020-11-25 14:47:43 +01:00
Jaromil
285f3c3a07 docker tomb wrapper included in extras 2020-11-24 19:48:07 +01:00
Jaromil
0a968b80b7 fix losetup permission
leftover bug in #391
2020-11-23 21:56:25 +01:00
Denis Roio
694390bd4a
Merge pull request #394 from Narrat/fix/393
Fix typo calling pinentry_assuan_getpass
2020-11-23 11:14:15 +01:00
Narrat
da590fb50e Fix typo calling pinentry_assuan_getpass
Affected the use of Tomb without DISPLAY and pinentry-curses.
Fixes #393
2020-11-21 22:33:30 +01:00
Jaromil
c9f3b07cd8 documentation updates for release 2020-11-17 13:58:43 +01:00
Denis Roio
f9d9d4bc8b
Merge pull request #391 from dyne/loopmount_refactor
refactor of state tracking for loop mounting
2020-11-17 10:21:43 +01:00
Jaromil
c3a354cc0f fixes and cleanups 2020-11-17 08:19:36 +01:00
Jaromil
3fb248bde8 refactor of state tracking for loop mounting
simplified function calls for tracking of loop mount by using global
variables whose scope is limited to execution, most computation is now
included in the `is_valid_tomb` function.
2020-11-16 23:35:03 +01:00
Jaromil
940563d02c resize now checks for correct password before operating
fixes bug mentioned in issue #333 that made tomb append space to a
tomb file before checking for correct password, leading to file
corruption in case the wrong password is inserted 3 times.
2020-11-16 13:28:37 +01:00
Denis Roio
91debdbf58
Merge pull request #390 from dyne/pinentry_display_updates
updated pinentry invokation to include wayland
2020-11-16 13:27:04 +01:00
Denis Roio
c7b1f00370
Merge pull request #389 from dyne/check_in_use
improve the check if a tomb file is in use
2020-11-16 13:26:42 +01:00
Jaromil
bc94559ac4 updated pinentry invokation to include wayland
also changes to priority order of invokation and some code cleanups and
indentations. Invokation order is now:

- WAYLAND? pinentry-gnome3
- X11?
	1. pinentry-x11 (distro specific wrapper)
	2. pinentry-gtk2 (legacy, removable)
	3. pinentry-gnome3
	4. pinentry-qt5
	5. pinentry-qt4
- NO DISPLAY? pinentry-curses
2020-11-16 10:24:37 +01:00
Denis Roio
b20ef50563
Merge pull request #382 from weblate/weblate-tomb-tomb
Translations update from Weblate
2020-11-16 10:00:52 +01:00
Jaromil
72da5b481e add .loop extension to /dev/mapper volume to identify its nature 2020-11-16 09:56:38 +01:00
Jaromil
9be5dff823 use realpath to always use absolute paths of tomb files in maphash 2020-11-16 09:54:37 +01:00
Jaromil
59d3810665 improve the check if a tomb file is in use
Change the mapper path using a hash of the tomb file path,
making it unique and reproducible to check if tomb is in use.
Check happens inside the new render_mapper() function which is
executed right after the key file opening.
2020-11-16 09:54:37 +01:00
mv87
d135730386 Translated using Weblate (German)
Currently translated at 100.0% (290 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/de/
2020-11-14 11:28:54 +01:00
Jeannette L
010674f30b Translated using Weblate (French)
Currently translated at 73.7% (214 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/fr/
2020-11-14 11:28:54 +01:00
Milo Ivir
193cd42bbd Translated using Weblate (German)
Currently translated at 95.5% (277 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/de/
2020-11-14 11:28:54 +01:00
Mattias Münster
a1f2364eb8 Translated using Weblate (Swedish)
Currently translated at 95.2% (276 of 290 strings)

Translation: Tomb/tomb
Translate-URL: https://hosted.weblate.org/projects/tomb/tomb/sv/
2020-11-14 11:28:54 +01:00
Jaromil
763dbdb356 document known bug for password with DISPLAY and pinentry-curses
detailed in issue #385
2020-11-13 23:54:41 +01:00
Jaromil
5b80abd96d deactivate test for libsphinx
seems to break in libsphinx build when calling `pkgconf`
2020-11-13 23:36:29 +01:00
Jaromil
136ba6e053 honor custom settings for GNUPGHOME
fix #371
2020-11-13 23:16:36 +01:00
Denis Roio
9ddfdc426f
Merge pull request #386 from aaronjanse/security-pinentry-curses
Use _verbose for pinentry-curses in ask_password
2020-11-11 14:41:09 +01:00
Denis Roio
298eedfedf
Merge pull request #384 from giacomoferretti/patch-1
Fix table syntax Markdown
2020-11-11 14:40:53 +01:00
Aaron Janse
15c894dfb4 use _verbose for pinentry-curses in ask_password 2020-11-09 16:43:06 -08:00
Giacomo Ferretti
d3995b82db
Fix table syntax 2020-10-06 16:26:34 +02:00
Jaromil
fb154bbb2f fix test docker for beowulf stable 2020-06-22 10:48:49 +02:00
Denis Roio
f14ba758ee
Merge pull request #378 from quickcougar/pinentry-override
Allow pinentry to fail, so long as the password comes through.
2020-06-22 10:05:31 +02:00
Denis Roio
85f02f746e
Merge pull request #376 from quickcougar/debug-messages
Write all debug messages to stderr to avoid polluting stdout.
2020-06-22 10:01:10 +02:00
Mark Mykkanen
2f29e6709c Allow pinentry to fail, so long as the password comes through. 2020-03-23 17:07:46 -05:00
Mark Mykkanen
7cead041ac Write all debug messages to stderr to avoid polluting stdout. 2020-03-22 14:02:12 -05:00
Jaromil
4ec85ea3ca
Merge pull request #373 from dcommisso/fix_manpage
Fix manpage
2020-02-05 19:56:14 +01:00
Domenico Commisso
7da034c146 Fix Firefox example in linux manpage to avoid Firefox error at startup 2020-02-04 15:27:25 +01:00
Domenico Commisso
ac13adaa28 Update exec-hooks name in linux manpage 2020-02-04 15:07:08 +01:00
Jaromil
e9fd1a19e1 documentation fix for default xts cipher string 2019-11-18 10:17:38 +01:00
Jaromil
377e335e93 remove warning on open by fixing order of chown
fix #369 (completes #324)
2019-11-18 10:15:12 +01:00
Jaromil
bec53aeb72 documentation update for new release 2019-10-11 22:06:55 +02:00
Jaromil
2d8a72ed95
Merge pull request #367 from edoffagne/doc
Fixed documentation
2019-10-03 09:37:36 +02:00
Erik Doffagne
7e82beaca6 Fixed documentation 2019-10-03 06:31:45 +02:00
Jaromil
15d279605b fix getent parsing when fields are emtpy
fix #365
2019-09-14 12:06:13 +02:00
Jaromil
ca708b4d46 Documentation to build gtk-tray
fix #351
2019-08-22 17:43:36 +02:00
Jaromil
d2cd41e1e7
Merge pull request #362 from dyne/getent-everywhere
use getent with wrappers every /etc/passwd query
2019-08-22 17:20:11 +02:00
Jaromil
382070481f use getent with wrappers every /etc/passwd query 2019-08-22 17:07:12 +02:00
Jaromil
01f5412fc5
Merge pull request #361 from dyne/normalize-conditionals
rewrite some conditionals for less ambiguity
2019-08-22 15:38:54 +02:00
Jaromil
1574723502 rewrite some conditionals for less ambiguity
this restyles some code introduced by the sphinx feature and tries to fix parse
errors reported in issue #357
2019-08-22 14:37:47 +02:00
Jaromil
71a7241f9c remove unneeded umount on forge_key failure 2019-08-22 11:49:43 +02:00
Jaromil
94d8ad497f Fix inconsistent cleanup on forge_key failure
Address concern raised in #360
2019-08-22 11:47:58 +02:00
Jaromil
187a627022 Fix tomb-kdb-pbkdf2 helper for multiplatform build
Avoid EOF being missed when reading in the password. Replace undefined
char-to-int comparison with a int-to-int one. Fix #360
2019-08-22 11:47:58 +02:00
Jaromil
6f2ce59d29
Merge pull request #356 from dyne/gettext_optional
make gettext optional in _sudo()
2019-07-08 07:19:36 +02:00
Jaromil
0b9080e0ca make gettext optional in _sudo()
address #355
2019-07-05 08:10:00 +02:00
Jaromil
d83e39f1ec fix spelling errors in manpage 2019-07-05 08:04:06 +02:00
Jaromil
1a93481480
Merge pull request #353 from roddhjav/master
Ensure the gpg key is trusted.
2019-06-28 09:12:28 +02:00
Alexandre Pujol
6b4bd69dbc
Ensure GPG key is trusted. See #340 2019-06-27 19:58:58 +01:00
Alexandre Pujol
0e9fe51b50
Add untrusted key in the test keyring and convert to v2 keyring. 2019-06-27 19:54:46 +01:00
Jaromil
06039a9e47 fix warnings on first tomb open
fix issue #324
2019-06-26 08:56:05 +02:00
Jaromil
e45c005c88 documentation updates for release
known bugs, changelog and manual for sphinx
2019-05-30 18:48:30 +02:00
Jaromil
0af46fe3c1 move dockerfile for tests into extras/tests 2019-05-22 10:59:36 +02:00
Jaromil
bd3e3c7056
Merge pull request #350 from dyne/urandom-switch
switch default random source to /dev/urandom
2019-05-22 10:13:07 +02:00
Jaromil
ace80c87db switch default random source to /dev/urandom
there is no need to default to a blocking source of random,
since /dev/random doesn't improves the quality of randomness in Linux
2019-05-22 09:55:02 +02:00
Jaromil
2b8eec6ba5 improve tomb umount checks avoiding grep call
consolidate use of `list_tomb_mounts` also inside umount_tomb
this should fix #315
2019-05-22 01:03:00 +02:00
Jaromil
7b06be5d43 before opening check if mountpoint is already used by a tomb
a simple check introduced on `tomb open` to list all tombs mounted and
control if any mountpoint is already in use, in case refuse opening.

fix #326
2019-05-22 00:30:48 +02:00
Jaromil
95f2f68654 Revert temp file mechanism for sphinx
for some reason to be investigated further using tomb's temp
file mechanism breaks the test suite of sphinx support

This reverts commit a6d252c949.
2019-05-18 09:51:55 +02:00
Jaromil
b34584b61c added new contributor to credits 2019-05-18 09:51:12 +02:00
Jaromil
91e607efb5 improve gpg key listing command 2019-05-18 09:50:57 +02:00
Jaromil
ec31d2d280 improved gpg recipient output warning
dropped the dependency from grep, head, cut, sed (only using awk)
added human readeable GPG ID besides fingerprint on recipient check
2019-05-05 11:29:25 +02:00
Jaromil
eec64f4f65
Merge pull request #348 from AlexisDanizan/master
Bug fix #328
2019-05-05 10:39:16 +02:00
Jaromil
a6d252c949 correct use of tomb's internal temp file mechanism for sphinx 2019-05-05 10:21:38 +02:00
Jaromil
5ce9960207
Merge pull request #344 from heat-wave/feature/pitchforked-sphinx-integration
Pitchforked sphinx integration
2019-05-05 09:58:39 +02:00
alexisdanizan
f1f23c417b Bug fix #328 2019-05-01 22:43:04 +02:00
heat-wave
b054a83ee5 Pitchforked sphinx integration for remote password storage 2019-04-30 16:05:03 +01:00
Jaromil
2dfb744a34
Merge pull request #347 from stevesbrain/master
Update suggested command with missing flag
2019-04-18 16:01:46 +02:00
Steve Divskinsy
522ad3abad
Update suggested command with missing flag
`tomb lock` suggested command was missing "-k" flag
2019-04-18 20:54:30 +09:30
Jaromil
a0c74985ca documentation updates on the new cloakify feature
also updated the list of code contributors
2019-02-22 09:50:04 +01:00
Jaromil
59408b373f
Merge pull request #342 from heat-wave/feature/cloakify-integration
Cloakify integration
2019-02-22 09:40:46 +01:00
heat-wave
f2eb1fd242 Expanded steganographic functionality with cloakify integration 2019-02-21 09:28:21 +00:00
Jaromil
9299f3eecf mention need of using trusted gpg keys in manpage
documentation to address issue #340
2019-02-20 20:37:28 +01:00
Jaromil
12767764fb
Merge pull request #336 from jcrd/usage
Improve usage message
2018-12-06 10:45:39 +01:00
James Reed
46286047f6
Align usage message lines (fix #335)
Print newlines only when necessary

Correct coma to comma in usage message
2018-11-26 10:48:19 -07:00
Jaromil
1b535f920b
Merge pull request #331 from jcrd/msg
Overhaul message printing
2018-11-25 15:12:36 +08:00
James Reed
477ab20443
Overhaul message printing 2018-11-24 15:48:25 -07:00
Jaromil
a14a39ae12
Merge pull request #332 from jcrd/kdf-header
Check for KDF header in is_valid_key
2018-11-04 16:01:02 +01:00
James Reed
1ba3c55241
Check for KDF header in is_valid_key 2018-11-03 15:15:48 -06:00
Jaromil
629841c0e7
Merge pull request #320 from roddhjav/stat
Track access/modification time of sensitive files
2018-09-24 18:09:10 +02:00
Alexandre Pujol
2f3826d88d
Fix modification time restoration. 2018-09-24 12:58:51 +01:00
Alexandre Pujol
aaa4637ed0
Track access/modification time of sensitive files
Collects the stats of tomb keys and tomb files then restore them when
Tomb exits. Can be extended to any file opened by Tomb. See #266
2018-09-23 22:21:05 +01:00
Jaromil
cdd3c5804a updated documentation on KDF whitespace bug
fix #307
2018-09-23 12:05:02 +02:00
Jaromil
e8919af867
Merge pull request #308 from AitorATuin/kdf_input
Fixes a problem reading passwords when using the kdf wrapper
2018-09-23 11:04:10 +01:00
Jaromil
27fd026cd5 documentation updates
also fix #319
2018-09-23 11:41:08 +02:00
Jaromil
a27f15aada
Merge pull request #318 from roddhjav/master
Add test coverage for GPG subkeys support.
2018-08-20 13:03:50 +02:00
Alexandre Pujol
c293aa7261
Add regression test for tomb 2.4 2018-08-19 23:08:48 +01:00
Alexandre Pujol
24fee7a076
Add test coverage for GPG subkeys support. See #317 2018-08-19 22:53:21 +01:00
Jaromil
7c8067fef3
Merge pull request #317 from bjonnh/patch-1
Add support for multiple GPG subkeys
2018-08-19 23:25:03 +02:00
bjonnh
18b7541a98
subkey_id for display must be out of _fingerprint
I did a typo, the ($gpg_id) should have been out of the _fingerprint call.
2018-08-18 17:41:37 -05:00
bjonnh
b49a36a07b
Add support for multiple keys especially subkeys
Correcting issue #316
2018-08-17 14:03:33 -05:00
Jaromil
8d698ad46a
Merge pull request #309 from Ganondolf/gtk-tray-path
Fix mounted volume default path
2018-02-18 11:10:22 +01:00
Ganondolf
f290904f71
Fix mounted volume default path 2018-02-17 16:23:26 +01:00
Jaromil
68a9589925 set ownership after dig and forge 2018-02-11 21:41:58 +01:00
ATuinDev
3cb8ebefd3
Don't impose any limitation in password size
Remove the hardcoded buffer with dynamic memory to get password input
(uses now calloc / realloc)
2018-02-04 20:53:35 +01:00
ATuinDev
35cf8572bf
Rename variable j to something more descriptive
Remove `+ 1` in conditions, easier to understand using the comparators
2018-02-03 23:24:20 +01:00
ATuinDev
a66224d549
Run extras/kdf-keys tests in travis 2018-02-03 21:23:07 +01:00
ATuinDev
510c8f6430
Add check for password lens 2018-02-03 21:05:56 +01:00
ATuinDev
5c419b3117
Add explicit comment before reading the password 2018-02-03 19:20:01 +01:00
ATuinDev
257e5ee99a
whitespaces are now part of the password
NULL character can also be part of the password. There was a failing
test that was checking exactly that (now all the tests pass).
2018-02-03 19:16:20 +01:00
ATuinDev
bce58cae3e
Add test testing that whitespaces are used by kdf wrapper
Adds a new test that assures that spaces are part of the passwords
2018-02-03 19:12:05 +01:00
Jaromil
3440a32839
Merge pull request #305 from dyne/assuan_deduplication
put assuan commands to pinentry into a single function
2018-02-02 16:47:28 +01:00
Jaromil
f5ceddc0b7 put assuan commands to pinentry into a single function 2018-01-29 09:47:42 +01:00
Jaromil
f4913e0744
Merge pull request #303 from sargo-devel/master
Added pinentry-qt5 support
2018-01-29 09:38:47 +01:00
SargoDevel
66ade86441 Added pinentry-qt5 support 2018-01-28 21:49:21 +01:00
Jaromil
d0805084a2 added doc to avoid logging of invokations in syslog
see issue #302 and thanks to @stevesbrain
2018-01-23 09:38:52 +01:00
Jaromil
f42a4c2a0b
Merge pull request #301 from Narrat/small_things
Small things
2018-01-09 10:42:40 +01:00
Narrat
08ca0a8eef list_gnupg_ciphers: Remove check on gpg
This function is called after _ensure_dependencies(), which would bail out if gpg is not found
2018-01-07 21:56:19 +01:00
Narrat
5e3b0dec84 list_gnupg_ciphers: be language agnostic
The old awk implementation always worked on lines beginning with 'Ciphers:' until it found 'Hash:'.
This fails for locales where a respective gnupg2.mo entry exists (Example: Ciphers in german is translated as Verschlü.:).
This is replaced by pointing awk on a specific line, which is for gpg1 and gpg2 the same. Work is done until awk stumbles up on a line which marks a new section (marked by keyword and :)

This closes #299
2018-01-07 21:40:32 +01:00
Narrat
e15c58dfd7 list_gnupg_ciphers: Pipe everything into /dev/null
Firstly the printed binary path is in the wrong place. Reading the text, one assumes Ciphers coming next.
Secondly it doesn't make sense to check there for a missing gnupg installation. Before calling list_gnupg_ciphers(), there is a direct call for gpg --version. If that fails the whole text is scrambled and no error reported

Dropping the output from which allows to remove the space from printing the ciphers. The text is correctly aligned now
2018-01-07 21:03:21 +01:00
Narrat
61fdab85be Show only version of pinentry
pinentry --version invocation includes License information.
As the same applies for gpg, and the information is not displayed there, we should the same with pinentry.
And tomb doesn't deal with the gpg sourcecode in any way.

This closes #300
2018-01-07 20:03:04 +01:00
Jaromil
74689ea484 tagging the release 2018-01-03 20:34:18 +01:00
Jaromil
7951645db5 switch code indentation to hard tabs
also update code guidelines.
2018-01-03 20:27:14 +01:00
Jaromil
70abf31bab documentation updates for a new release 2018-01-03 19:53:35 +01:00
Jaromil
ae78659efb
Merge pull request #297 from dyne/restore-chown
restored change of ownership on tomb's contents
2018-01-03 19:42:52 +01:00
Jaromil
ab3044c6f5 restored change of ownership on tomb's contents
this reverts commit 843b7fdfc4
and refers to various issues, among them #268

on the long term its easy to realise how this is a usability feature for most
users, so we just provide a new '-p' flag to preserve ownership on open.
2018-01-03 18:34:50 +01:00
Jaromil
214ec8ecbd
Merge pull request #298 from roddhjav/fix-version-check
Fixes: version check & tests
2018-01-03 16:36:16 +01:00
Alexandre Pujol
cec0c01b3e
Test: remove useless cleanup 2018-01-03 14:50:00 +00:00
Alexandre Pujol
c7dc379c0d
Tests: support non-standard zsh location. See #283 2018-01-03 14:49:08 +00:00
Alexandre Pujol
b20daeea6f
Fix: use is-at-least function to check program version 2018-01-03 14:46:23 +00:00
Jaromil
bc963cd1ae fix for correct execution of exec-hooks
this was broken in the latest release and fixes #271
2018-01-03 14:37:15 +01:00
Jaromil
3b1759b2b3 fix for hidden files without an extension as tombs
fix #147 introducing an extra check on TOMBNAME that, if returned
empty by the first transormation that removes the last .extension,
then is filled with the full TOMBFILE name without any transformation
2018-01-03 13:43:05 +01:00
Jaromil
c8616787f7 added a comment in manpage about resize fail and restore
documentation according to #269
2018-01-03 11:14:21 +01:00
Jaromil
31a78de23f removed known_bugs entry according to #276
This was confusing. Later found time to verify facts. We use 512 bytes
long keys (4096 bits) so AES-256 was always used in XTS mode.
2018-01-03 11:09:33 +01:00
Jaromil
60b72ad91f documentation and version updates
findmnt version shown
2018-01-03 10:44:27 +01:00
Jaromil
cf93551efa
Merge pull request #296 from dyne/use-findmnt
Replace 'mount -l' with 'findmnt'
2018-01-03 10:38:48 +01:00
Jaromil
9b1d1891cc
Merge pull request #290 from parazyd/use-shred-instead-wipe-patch
use shred instead of wipe
2018-01-03 10:03:50 +01:00
Jaromil
b72d67618b restore square parens detection in list_tomb_mounts() 2018-01-02 15:35:32 +01:00
Jaromil
3f107f9d31 small fix for usage of findmnt --raw flag 2018-01-02 14:04:53 +01:00
Jaromil
3593721967 drop zsh/regex module in favor of =~
As debated in issue #282 Zsh introduced a bug in v5.3.1 which briefly affected
our mechanism for closing tombs. The bug is fixed, but while investigating the
issue @aude realised there can be a better way to apply this regex for the
detection of mounted volumes on distro dependent /run/media/$USER paths.

This change drops usage of the regex optional module in Zsh to use the built-in
=~ comparison and improves the match using round parenthesis. It may fix the
close command on some distributions.
2018-01-02 14:04:53 +01:00
Victor Calvert
09ff889c1d Replace mount with findmnt
this fixes a mount-related functionality (finding the volume label) in new
versions of util-linux, that since v2.30 does not list anymore volume labels
with its mount -l command. Since findmnt needs sudo to list labels, this also
introduces the need for sudo in more commands: is_valid_tomb(), list, index and
search. The issue was examined in PR #283 and this is a rebase of it.
2018-01-02 13:39:35 +01:00
Jaromil
742b097192
Merge pull request #294 from dyne/regex-match
drop zsh/regex module in favor of =~
2018-01-02 13:23:23 +01:00
Jaromil
a7190eb2e2 drop zsh/regex module in favor of =~
As debated in issue #282 Zsh introduced a bug in v5.3.1 which briefly affected
our mechanism for closing tombs. The bug is fixed, but while investigating the
issue @aude realised there can be a better way to apply this regex for the
detection of mounted volumes on distro dependent /run/media/$USER paths.

This change drops usage of the regex optional module in Zsh to use the built-in
=~ comparison and improves the match using round parenthesis. It may fix the
close command on some distributions.
2018-01-02 12:35:11 +01:00
Jaromil
e083ce7a23
Merge pull request #291 from dyne/slam-refactor
Slam refactor
2017-12-14 17:48:47 +01:00
Jaromil
c537676e92 wrap the cats 2017-12-12 11:29:53 +01:00
Jaromil
f6457090af documentation of the new 'ps' command 2017-12-11 17:55:02 +01:00
Jaromil
121a64bc80 improvements to the new ps and slam implementations 2017-12-11 17:55:02 +01:00
Jaromil
8e33be0472 new slam and list_processes 2017-12-11 17:55:02 +01:00
Jaromil
85ac179cc8 cleanup of umount_tomb and removed slam from inside 2017-12-11 17:55:02 +01:00
Jaromil
21347f3657 square parens detection in list_mounted_tombs 2017-12-11 17:53:38 +01:00
Jaromil
6182a56fb6 fix bug in message rendering: stop overriding $i if existing
this small bug caused rewriting the $i variable (often used as
iterator in loops) whenever a log message was produced. Fix also
includes an acceleration of log printing by substituting seq with
native zsh notation.
2017-12-11 17:53:17 +01:00
parazyd
c8be1c22dd
use shred instead of wipe 2017-12-07 11:36:59 +01:00
Jaromil
6696f0c758 Merge pull request #285 from avindra/patch-2
changelog: fix minor typo
2017-10-19 09:59:04 +02:00
Avindra Goolcharan
fa1049e8f0 changelog: fix minor typo
just missing a space here
2017-10-18 02:02:37 -04:00
Jaromil
aba4f4c3b8 Merge pull request #275 from gador/master
Double check after umounting bind directories
2017-08-08 09:55:09 +02:00
Florian Brandes
8d2cb2d852
Double check after umounting bind directories
Checking if a tomb is still present in the
output of mount after umounting binded directories.
Addresses issue #265
2017-07-23 10:28:06 +02:00
Jaromil
64daf70229 remove 'lint' target from travis CI builds
the shellcheck developer unilaterally decided removal of zsh support
https://github.com/koalaman/shellcheck/issues/298 so far ignoring all
requests to add support, so we won't run our shellcheck tests on
travis. using Devuan Jessie's 0.3.5 with our ignores still works, so
will keep testing it locally.
2017-07-21 11:45:40 +02:00
Ivan J
6926583d79 Merge pull request #270 from mesbahamin/grammatical-corrections
Fix 3 grammatical errors.
2017-06-24 21:43:26 +02:00
Amin Mesbah
04ec0189a6 Fix 3 grammatical errors. 2017-06-24 12:24:50 -07:00
Jaromil
fb5eee002b Merge pull request #267 from dyne/exec-hooks
Exec hooks
2017-06-13 09:30:14 +02:00
Jaromil
127a8ed7ee exit code check on close
also removed pre-open and post-close as they don't really make sense
since all hooks are contained inside the Tomb. The post-close may be
implemented using a temp file, if a use case turns up for it.
2017-06-06 16:30:48 +02:00
Jaromil
056d0174f4 refactoring of exec-hooks
Renamed file from "post-hooks" to more appropriate "exec-hooks".
Implemented and documented a more consistent call system made of 4
different stages: pre-open, post-open, pre-close, post-close.
Addresses issue #265
2017-06-06 12:45:29 +02:00
Jaromil
5f71b486df Merge pull request #261 from Narrat/missing_lsof_stuff_2
Missing lsof stuff
2017-05-14 09:47:28 +02:00
Narrat
67004392fa Add testcase for slam operation
Thanks to @roddhjav for the intel and the test itself.

Closes #258
2017-05-12 20:24:49 +02:00
Narrat
c658d33187 --version includes lsof as optional dep 2017-05-10 21:50:48 +02:00
Jaromil
9fa55d1165 Merge pull request #260 from roddhjav/sharness
Replace the test suite by the sharness test suite
2017-05-10 14:58:49 +02:00
Alexandre Pujol
99855746b3
Travis: install KDF dependencies & use apt addons 2017-05-04 20:57:14 +01:00
Alexandre Pujol
593747d619
Add bind hooks tests and minors test setup changes 2017-05-03 19:51:36 +01:00
Alexandre Pujol
4688820b68
Tests: support for /media and /usr/media 2017-05-01 23:26:17 +01:00
Alexandre Pujol
92a670efd8
Add integrity tests 2017-05-01 23:02:49 +01:00
Alexandre Pujol
045c55436f
Regression tests: add old tomb version, support for zsh 5.3 2017-05-01 22:48:28 +01:00
Alexandre Pujol
8aaa8e1725
Add setkey tests 2017-05-01 21:22:36 +01:00
Alexandre Pujol
408977a351
Add steganography tests 2017-05-01 21:22:20 +01:00
Alexandre Pujol
a609b855c7
Add tests for tomb resize, passwd and engrave 2017-05-01 21:21:25 +01:00
Alexandre Pujol
36f5ed8729
Add regression tests 2017-05-01 21:19:48 +01:00
Alexandre Pujol
eb57899162
Add KDF related tests 2017-05-01 21:19:19 +01:00
Alexandre Pujol
166bbdc18c
Add main tests for GPG key support 2017-05-01 21:18:59 +01:00
Alexandre Pujol
e99a0350fb
Add base tests 2017-05-01 21:11:28 +01:00
Alexandre Pujol
bdebb6af32
Add tests setup:
- Tomb & auxiliary programs config
- GPG config
- Temporary test files config
- Helper function definitions
2017-05-01 21:08:08 +01:00
Alexandre Pujol
e558ad9f13
Update files for Sharness support
- extras/test/Makefile
- extras/test/.gitignore

Add test aggregate script
2017-05-01 21:06:02 +01:00
Alexandre Pujol
1a2fd5901f
Add sharness v1.0.0 - https://github.com/chriscool/sharness 2017-05-01 21:01:16 +01:00
Jaromil
c858def9c4 minor typo in authors 2017-04-16 17:25:34 +02:00
Jaromil
18067a1c2e version bump 2017-04-16 17:18:16 +02:00
Jaromil
7241eb0fd1 updated README with recent help screen 2017-04-16 13:06:33 +02:00
Jaromil
f9dc9ed5a7 documentation updates 2017-04-16 12:49:04 +02:00
Jaromil
5de9cb32b9 manual updated with actual gpg id usage information 2017-04-16 12:15:21 +02:00
Jaromil
66aa7fdac7 minor corrections to new gpg related message formatting 2017-04-16 12:07:41 +02:00
Jaromil
3e0dd1e111 add debug message of loop device in umount_tomb 2017-04-16 12:00:47 +02:00
Jaromil
3ffcc74e5b Merge pull request #256 from roddhjav/gpg-options
Options for GnuPG Key - 2
2017-04-16 11:55:26 +02:00
Alexandre Pujol
1050d43c59
Support for gpg encrypted tomb key loaded from stdin. See #255 2017-04-05 14:52:47 +01:00
Alexandre Pujol
c303513be4
Fix issues with GPG default key.
- Remove --no-options gpg option when using GPG key.
- Improve gpg default key tests

To use the default key, ~/.gnupg/gpg.conf needs:
  default-key <keyid>
  default-recipient-self
Or
  default-recipient <keyid>

Otherwise the first key in the keyring is used.
2017-04-03 13:24:25 +01:00
Alexandre Pujol
d720e4b2ce
Remove --shared flag when sharing a tomb key. See #252 2017-03-24 20:08:33 +00:00
Jaromil
6c2077fee1 make documentation less opinionated about the u/random issue
fix #253
2017-03-22 16:40:21 +01:00
Jaromil
ba9c0481cc Merge pull request #250 from roddhjav/gpg-support
Options for GnuPG Key
2017-03-20 21:03:03 +01:00
Alexandre Pujol
bb77de0815
Fix compatibility with GnuPG 2.2.19
GnuPG 2.2.19 added a warning when no command was given. Some invocations
do not specify a command, added --decrypt in this these cases.
2017-03-20 19:14:47 +00:00
Alexandre Pujol
29a177aa05
Fix issue #251 2017-03-20 19:07:04 +00:00
Jaromil
69f52bee25 information about compatible software 2017-03-18 15:24:01 +01:00
Alexandre Pujol
c793e0b132
Add support for non hidden and hidden recipient
Use -r to provide non-hidden recipient,
Use -R to provide hidden recipient.
2017-03-03 21:19:04 +00:00
Alexandre Pujol
6352a1d417
Add GPG default key support for key encryption
If the option -r is not set, use the gpg default key to encrypt
a tomb key
2017-03-03 20:36:50 +00:00
Jaromil
0644ebe951 updated manual to reflect final stage of gpg asymmetric encryption 2017-02-21 15:39:54 +01:00
Jaromil
b0815b514b small improvement to ISO compliancy documentation 2017-02-20 22:13:43 +01:00
Jaromil
3f06bce8eb failed bind-hooks (missing target) no more abort the mount operation
a warning is printed and the mounting goes forward without the hooks
2017-02-20 22:13:43 +01:00
Jaromil
e37982d114 Merge pull request #244 from roddhjav/gnupg-key-support
GnuPG Key Support
2017-02-20 22:09:48 +01:00
Jaromil
bea7fe3f7c Merge branch 'master' into gnupg-key-support 2017-02-20 20:47:06 +01:00
Jaromil
1f022d10f1 Merge pull request #248 from mesbahamin/open_read_only
Open non-writable Tomb files with "read only " mount option
2017-02-18 20:30:33 +01:00
Jaromil
2bc7e43198 Merge pull request #245 from Arusekk/master
Totally fixed spaces handling in tomb and tomber
2017-02-14 10:58:35 +01:00
Amin Mesbah
70334f58fb Skip writable check when mounting with "ro" option.
When opening a tomb file with "ro" passed through the -o option, the
writability check in is-valid-tomb() is skipped. This allows tomb files
to be opened without write permission.

test-open-read-only() now succeeds.
2017-02-12 17:44:55 -08:00
Amin Mesbah
f4f8c4e024 Add failing test for opening read-only tomb.
Adds a test function called test-open-read-only(). The test prepares a
tomb file, removes the "write" permissions from it, and then attempts to
open it with "read-only" mount options (`-o ro,noatime,nodev`).

The test currently fails as expected.
2017-02-12 16:39:39 -08:00
Arusekk
f4cdc1a0c5 Fixed spaces handling in Tomb 2017-02-10 21:05:04 +01:00
Alexandre Pujol
4a7019715f Use --hidden-recipient by default instead of --recipient.
Due to the hidden-recipient, GPG will try all the available keys. User
can speed up this process providing the recipent using the -r
option. Therefore, 'tomb open' optionaly support the -r option.
2017-02-09 20:59:10 +00:00
Alexandre Pujol
c63fcf2730 Fix is_valid_recipients private key detection 2017-02-09 19:18:02 +00:00
Alexandre Pujol
528140738a Add -g/--gpgkey option to tell tomb to use GPG key to encrypt a tomb key
Option -r is now only used to provide the recipient
Option -R removed, the new recipient can be given by the -r option.
2017-02-09 18:57:34 +00:00
Jaromil
8832471170 Merge pull request #246 from Narrat/bug/slam
lsof should be correctly detected now
2017-02-09 02:23:10 +01:00
Narrat
e69795fe71 lsof should be correctly detected now
LSOF would be set everytime otherwise
2017-02-07 03:30:25 +01:00
Jaromil
5b7f875f3d Merge pull request #243 from Narrat/bug/slam
Use of lsof to fix slam for specific mountpoint
2017-02-06 11:52:08 +01:00
Narrat
b2ee2114cf Make lsof an optional dep
tomb doesn't need lsof for anything else, and can work regulary without it.
So make it an optional feature, which allows to slam a tomb if lsof is installed

Updates additionally the man page and generates a new pdf from it
2017-02-05 20:03:29 +01:00
Alexandre Pujol
bfe5bb9707 Update the man page with GPG key support 2017-02-03 23:57:52 +00:00
Alexandre Pujol
f27130053d Add new options description in tomb -h 2017-02-03 23:57:52 +00:00
Alexandre Pujol
6cfffef137 Update function comments & description with GPG recipient support. 2017-02-03 23:57:52 +00:00
Alexandre Pujol
dfc593f9d6 Add support for GPG key in the tomb outputs. 2017-02-03 23:57:52 +00:00
Alexandre Pujol
e8384ec7ac Allow opening a tomb without giving a valid recipient.
The -r option always requires an arguments. However GPG does not need
any recipient when decrypting a key. In order to be able to open a tomb
without writing (the long) recipient, the user can use the -f option to
short-cut the valid recipient checking. A dummy recipient is still required.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
6f89dbd2fe Add '--shared' in order to activate sharing support.
Sharing feature is a very sensitive action, the user needs to trust the
GPG public key it is going to share its tomb. This is why this feature
needs to be explicitly activated using in more the flag --shared
on the key encryption commands.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
15164f5578 Add sharing support for tomb key.
A tomb key can be encrypted with more than one recipient. Therefore, a
tomb can be shared between different user. The multiple recipients are
given using the -r (or/and -R) option and must be separated by ','.

Multiple recipients can be given for the commands: forge, setket and passwd
2017-02-03 23:57:52 +00:00
Alexandre Pujol
53b7460274 Add tomb setkey support for GPG key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
a200448de2 Add tomb resize support for GPG key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
47ddeebbc4 Add support to change the GPG key used to encrypt a tomb key. (tomb passwd) 2017-02-03 23:57:52 +00:00
Alexandre Pujol
5a35ab9668 Improve key encryption/decryption using GPG key.
Decryption/Encryption works without these improvment, however, there
are needed in order to have clean key (without empty line).

Moreover, tests showed not doing cause troubles when changing the GPG key
used to encrypt a tomb key.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
8f8dc0a0d4 Improve exhumation of key when opening a tomb 2017-02-03 23:57:52 +00:00
Alexandre Pujol
b23e9aa028 Add --tomb-pwd support for GPG key on steganography functions 2017-02-03 23:57:52 +00:00
Alexandre Pujol
e2fe8e508e Add unit tests for steganography feature using GPG key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
d1b016b3c1 Add GPG recipient support for steganography function (bury and exhume)
The tomb policy is to use the same password to encrypt
the key and to bury it. However, steganography cannot be
done with GPG key. Therefore, we check the user can
decrypt the tomb with its GPG key and we ask for a
steganography password. Having different method is a
technical requirement and should enhance security.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
2d516cbaed Check if the GPG key given is a valid.
Add the function 'is_valid_recipients'
A key is valid if both public and private keys are present
in the GPG database
2017-02-03 23:57:52 +00:00
Alexandre Pujol
902860fd9f Add GPG recipient support when generating a new tomb key 2017-02-03 23:57:52 +00:00
Alexandre Pujol
db7109da4a Add tests for GPG recipient support in tomb 2017-02-03 23:57:52 +00:00
Alexandre Pujol
f72534790a Fix test suite error in the return code: GLOBAL_RESULT were always true. 2017-02-03 23:57:52 +00:00
Alexandre Pujol
e78af47c56 Add a GPG database in 'extras/test/gnupg' for test suite purpose
The GPG Key are unencrypted. Do not use them for an other purpose
than a test suite.
2017-02-03 23:57:52 +00:00
Alexandre Pujol
6c0d89cab1 Use -r option to shortcut interactive tomb password popup 2017-02-03 23:57:52 +00:00
Alexandre Pujol
9ee0a1550e Add -r option to enable GPG key integration.
The flag -r (for recipient like in GPG itself) takes a mandatory
argument, the GPG key ID.
2017-02-03 23:57:52 +00:00
Narrat
537bb6aaeb Use of lsof to fix slam for specific mountpoint
Apparantly fuser didn't report back, if the tomb was mounted in a subdir of /run (whereas /run itself is often a tmpfs mount).
With no list of process ids those couldn't be killed, so slamming the tomb failed.
lsof is capable to report back the sought information.

Fixes #220

Additionally fixing the debug output, where a hardcoded mountpoint was used
2017-02-03 17:46:16 +01:00
Jaromil
1f852908ae improved readme, section on compliancy 2017-02-01 09:19:09 +01:00
Jaromil
9110ccd9d1 really use key-size 512 on luksFormat 2017-01-29 21:54:46 +01:00
Jaromil
7a98ee8ba6 change forged key lenght to 512 bits
Addresses issue #238: as 512 bit key length triggers use of AES256.
Apparently so far tombs used AES128 due to key length 256.
Change passes all tests and has no regression implications.
2017-01-21 23:50:57 +01:00
Jaromil
4439a6a327 minor fixes to regression tests 2017-01-21 23:50:57 +01:00
Daniel Rodriguez
e7e21243db Automatically remove conflicting quotes on pot generation 2017-01-21 18:48:09 +01:00
Daniel Rodriguez
26e549292f Remove extra char quotes in translation files 2017-01-21 18:38:07 +01:00
Jaromil
5e8db49701 Merge pull request #239 from reiven/master
Update tomber in extras for v2.2+
2017-01-11 14:36:40 +01:00
Federico Reiven
a808d4aef8 Update tomber in extras for v2.2+ 2017-01-10 12:11:32 -03:00
Daniel Rodriguez
42ae73d727 Sync translations with POEditor 2017-01-03 12:00:29 +01:00
Jaromil
ed37b4e1fa integrated latest changes in changelog 2017-01-02 12:54:55 +01:00
Jaromil
fa145074f8 documentation updates and reorganisation
Added two new sections to the manpage: deniability and password.
Small actualisation of the install instructions.
2017-01-02 12:02:23 +01:00
Jaromil
843b7fdfc4 remove change of ownership when mounting tombs
The chmod/chown launched on the mounted volume is not really effective
for security, plus the UID is not correctly guessed when tomb is
launched using sudo. It is now up to the user to correctly set
ownership and permission on mounted volumes. There is also one less
check on the ownership of the tomb file which was failing with a
warning in the same case.
2017-01-02 11:04:08 +01:00
Jaromil
5996beab0e small fixes to run clean tests 2017-01-02 07:03:54 +01:00
Jaromil
cb699189e7 small linting fixes 2017-01-02 06:13:52 +01:00
Jaromil
0fa4a07f8c make lint check on travis using shellcheck 2017-01-02 06:04:58 +01:00
Jaromil
18743c82a5 code linting
small cleanup using shellcheck, also available as 'make lint'
2017-01-02 06:03:29 +01:00
Jaromil
6f4cfd626c prefer ascii single-quotes to utf8 2017-01-02 06:02:50 +01:00
Jaromil
d41347fe22 documentation updates for release 2016-12-29 19:20:48 +01:00
Jaromil
88f5a926f0 updated extras/gtomb to latest by parazyd 2016-12-29 17:29:15 +01:00
Jaromil
7b72f07f96 switch shebang to use /usr/bin/env
this is a more generic approach to shebang which supports interpreters
when installed anywhere in the current path.
2016-12-29 13:49:03 +01:00
Jaromil
14cba81f6e fix is_valid_tomb check for already mounted tombs
also added some more verbosity on debug
2016-12-26 20:40:23 +01:00
Jaromil
db976a5210 improve wrapping of key generation
gen_key now avoids adding a final newline to file (addressing #226)
and provides more debugging information from the gpg  process.
2016-12-26 20:19:01 +01:00
Jaromil
e59518befa included regression tests against old Tomb versions 2016-12-26 20:03:14 +01:00
Jaromil
f5375c61fe improvement over previous gpg_decrypt fix
now also avoiding the use of `read` shell built-in
2016-12-26 19:04:54 +01:00
Jaromil
df75c39a58 new parsing for gpg_decrypt function
this new parser works with all ZSh versions and brings overall
improvement by eliminating the invocation of exernal binary `grep`
over the secret data.
2016-12-26 12:12:34 +01:00
Jaromil
4b1afb4fab documentation on direct use of images as keys
as mentioned in #225 now the manual mentions using jpeg images
directly as arguments to -k on open commands.
2016-12-22 20:46:40 +01:00
Jaromil
b9f555b5fe temporary advice for zsh 5.3 users 2016-12-19 09:40:10 +01:00
Jaromil
844a886da1 fix sudo execution (patch by robertmx in #223)
tested also in #228, this stops overwriting the $USERNAME
variable which is not really useful (it was used in the previous
privilege escalation model)
2016-11-18 19:00:47 +01:00
Jaromil
7e88c5d07b travis fix to force apt to overwrite new conf files 2016-11-18 14:27:04 +01:00
Jaromil
99bb7fd067 fix travis for non-interactive apt 2016-11-18 14:21:57 +01:00
Jaromil
101b89f0be use head directly without cat in post-hooks
less is more...
2016-11-18 13:56:44 +01:00
Jaromil
fa44f46eba better documentation for kdf
also correctly use _failure on fatal error using --kdf
2016-11-18 13:56:44 +01:00
D.J.R
c502ef3d92 Merge pull request #230 from mandeep/change_swap_success_message
Changed message when encrypted swap found to something more informative
2016-10-17 22:00:46 +02:00
mandeepbhutani
50719fb06f Changed message when encrypted swap found to something more informative
Changed message to detail all swap partitions
2016-10-16 11:40:26 -05:00
Jaromil
c80ebd6d6e travis CI: removed kdf tests
kdf computation seems to not work on travis, it times out
2016-06-13 10:34:31 +02:00
Jaromil
6855127c15 added extras to CI tests (kdf etc.) 2016-06-12 17:28:09 +02:00
Jaromil
feb35205c8 setup continuous integration tests on Travis 2016-06-12 17:18:21 +02:00
Jaromil
ff57aa3214 experimental port to android - works with limited functionality 2016-01-24 23:07:19 +01:00
Jaromil
8f0b2943ce documentation improvements for kdf and dm-crypt cipher choice 2016-01-09 10:16:42 +01:00
Jaromil
a08cb6e0de forgot to bump the version into the script 2015-12-31 13:33:36 +01:00
128 changed files with 25481 additions and 6945 deletions

4
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
github: dyne
patreon: dyneorg
open_collective: dyne

45
.github/workflows/linux.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name:
💀 Linux Tomb
on:
push:
paths-ignore:
- 'doc/**'
- 'portable/**'
- '*.md'
branches:
- master
pull_request:
paths-ignore:
- 'doc/**'
- 'portable/**'
- '*.md'
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
build-matrix:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-20.04]
runs-on: ${{ matrix.os }}
steps:
- name: Install tomb dependencies
run: |
sudo apt-get update -y -q
sudo apt-get install -y -q zsh cryptsetup gpg gawk libgcrypt20-dev steghide qrencode python2 python3-pip python3-dev libssl-dev make gcc sudo gettext bsdmainutils file pinentry-curses xxd libsodium23 libsodium-dev
- uses: actions/checkout@v3
- name: Build the KDF extras
run: |
make --directory=extras/kdf-keys
sudo make --directory=extras/kdf-keys install
- name: Disable swap
run: sudo swapoff -a
- name: Run main tests
run: sudo make test
- name: Run KDF tests
run: sudo make -C extras/kdf-keys test

97
.github/workflows/portable.yml vendored Normal file
View File

@ -0,0 +1,97 @@
name: ☠️ Portable tomb
on:
push:
paths:
- 'portable/**'
- '.github/workflows/portable.yml'
branches:
- master
pull_request:
paths:
- 'portable/**'
- '.github/workflows/portable.yml'
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
jobs:
ubuntu-20:
name: 🐧 test Ubuntu-20
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Install portable tomb dependencies
run: |
sudo apt-get install -q -y make openssl wget fuse3 util-linux
sudo make -C portable download-deps-ubuntu20
- name: Run portable tomb test
run: sudo make -C portable create-open-close
- uses: actions/upload-artifact@v3
with:
name: ubuntu-test.tomb
path: portable/test.tomb*
retention-days: 1
freebsd:
runs-on: macos-12
name: 😈 test FreeBSD-13
needs: [ubuntu-20]
steps:
- uses: actions/checkout@v2
- name: Download portable tomb volume built on Ubuntu
uses: actions/download-artifact@v3
with:
name: ubuntu-test.tomb
path: portable
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0
with:
usesh: true
prepare: |
pkg install -y sudo gmake bash fusefs-libs3 fusefs-lkl e2fsprogs wget
wget https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64
mv veracrypt-freebsd13-amd64 /usr/bin/veracrypt
wget https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64
mv zenroom-freebsd13-amd64 /usr/bin/zenroom
chmod a+x /usr/bin/veracrypt /usr/bin/zenroom
kldload fusefs
run: |
sudo gmake -C portable
sudo gmake -C portable check-random-data
- uses: actions/upload-artifact@v3
with:
name: freebsd-test.tomb
path: portable/test.tomb*
retention-days: 1
ubuntu-last-cleanup:
name: 🎯 final Ubuntu tests and cleanup
runs-on: ubuntu-20.04
needs: [freebsd]
steps:
- uses: actions/checkout@v3
- name: Install compiler and dependencies
run: |
sudo apt-get install -q -y make openssl wget fuse3 util-linux
sudo make -C portable download-deps-ubuntu20
- name: Download tomb built on FreeBSD
uses: actions/download-artifact@v3
with:
name: freebsd-test.tomb
path: portable
- name: Check integrity FreeBSD->Ubuntu
run: sudo make -C portable check-random-data
- name: delete FreeBSD tomb artifacts
uses: geekyeggo/delete-artifact@v2
with:
name: freebsd-test.tomb
- name: delete Ubuntu tomb artifacts
uses: geekyeggo/delete-artifact@v2
with:
name: ubuntu-test.tomb

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
sudo: required
language: c
services:
- docker
before_script:
- cp -v extras/test/Dockerfile .
- docker build -t dyne/tomb .
- docker run -it --privileged dyne/tomb /bin/bash -c "oracle & make test"
script:
- docker run -it --privileged dyne/tomb /bin/bash -c "make -C extras/kdf-keys test"

View File

@ -1,32 +1,43 @@
Cryptsetup is written by Christophe Saout and Clemens Fruhwirth.
Cryptsetup was originally written in 2004 by Jana Saout
Tomb is written and directed by Denis Roio <jaromil@dyne.org>
LUKS extensions are written in 2006 by Clemens Fruhwirth
Tomb is written and maintained since 2007 by [Denis "Jaromil" Roio](https://jaromil.dyne.org)
Tomb includes code and advices by Anathema, Boyska, Hellekin O. Wolf,
GDrooid, Parazyd.
Daniel Rodriguez, Parazyd, Alexandre Pujol, AitorATuin, Narrat, Artur
Malimonov and Chris Vogel.
The minimal Zenity GUI is being written by Parazyd
The 'gtomb' GUI based on Zenity is written by Parazyd and Daniel Dias Rodrigues.
The Qt5 desktop tray GUI is written by Gianluca Montecchi.
Python Tomb wrappers are written by Reiven and Boyska.
Python Tomb wrappers are contributed by Reiven and Boyska.
The Docker Tomb wrapper is contributed by Greg Tczap and Jens Rischbieth.
Artwork is contributed by Jordi aka Mon Mort and Logan VanCuren.
Gettext internationalization and Spanish translation is contributed by
GDrooid, French translation by Hellekin, Russian translation by fsLeg,
German translation by x3nu, Italian translation by Massimiliano
Augello and Swedish translation by PLJ / Kosovoper.
Daniel Rodriguez and Francisco Serrador. French translation by
Hellekin and Roy Lockhart, Russian translation by fsLeg and AHOHNMYC,
German translation by Jerry Polfer, Italian translation by
Massimiliano Augello and Swedish translation by PLJ / Kosovoper,
general fixes contributed by Daniel Dias Rodrigues.
Testing, reviews and documentation contributed by Dreamer, Vlax,
Shining the Translucent, Mancausoft, Asbesto Molesto, Nignux, TheJH,
The Grugq, Reiven, GDrooid, Alphazo, Brian May, fsLeg, JoelMon,
Narrat, x3nu, Jim Turner, Maxime Arthaud and the Linux Action Show!
The Grugq, Reiven, GDrooid, Alphazo, Brian May, fsLeg, Narrat, Jerry
Polfer, Jim Turner, Maxime Arthaud, RobertMX, mhogomchungu Mandeep
Bhutani, Emil Lundberg, Joel Montes de Oca, Armin Mesbah, Arusekk,
Stephan Schindel, Asbjørn Apeland, Victor Calvert, bjonnh, SargoDevel,
AitorATuin, Alexis Danizan, Sven Geuer, Greg Tczap, Aaron Janse, Mark
Mykkanen, Alexis Danizan, Steve Litt, James R, Matthieu Crapet, Selene
ToyKeeper, Valentin Heidelberg and... the Linux Action Show!
Tomb includes an implementation of the "Password-Based Key Derivation
Function v2" based on GCrypt and written by Anthony Thyssen.
Function v2" based on GCrypt and written by Anthony Thyssen, with
fixes contributed by AitorATuin.
Tomb developers can be contacted via GitHub issues on
https://www.github.com/dyne/Tomb or over IRC https://irc.dyne.org
channel **#dyne** (or directly over port 9999 with SSL)
Some of the Tomb developers can be contacted via [GitHub discussions](https://github.com/dyne/Tomb/discussions)
or over Telegram via the [Dyne.org Chat Channel](https://t.me/dyne_chat).

View File

@ -1,5 +1,127 @@
# Tomb ChangeLog
## 2.10
### Sep 2023
This release adds optional support for Argon2 KDF brute-force
protection and introduces support for doas as an alternative to sudo
for privilege escalation. It also improves support for BTRFS formatted
Tombs, adds zram detection as swap memory, updates documentation and
translations and cleans up the script code.
## 2.9
### Jan 2021
This release fixes all bugs introduced by the unfortunate 2.8 release
series in 2020 as well introduces support for BTRFS formatted
Tombs. The fixes are for password insertion to work on all desktops,
as well the fix to a regression when using old Zsh versions. The new
feature is activated by the '--filesystem' flag on 'lock' commands.
It only supports BTRFS as internal filesystem of a Tomb instead of the
default EXT4; resizing works as well to create and send or receive
subvolumes and snapshots inside a Tomb. There are also some cleanups,
small error handling improvements and no more need for suid actions by
'forge' and 'dig' commands.
## 2.8.1
### Nov 2020
This is a minor bugfix release. It fixes two bugs introduced by the
previous release: the release of loopback devices and a typo affecting
password insertion in text-only mode. It also provides a cosmetic fix
for the output of 'tomb list' that now displays correct sizes. At
last, the docker wrapper has been included in extras/ to be shipped in
Tomb. The span of CVE-2020-28638 has been assessed with more precision
and KNOWN_BUGS updated accordingly.
## 2.8
### Nov 2020
This new release updates the documentation, improves usability and
fixes two bugs. A bug has been found (CVE-2020-28638) to corrupt
passwords entered using pinentry-curses on desktops using a X11
DISPLAY, the documentation in KNOWN_BUGS outlines how to fix
regressions. Another bug has been fixed to prevent mounting tombs that
are already opened, a situation leading to potential data loss.
Changes mentioned lead to a small internal refactoring and cleanup,
leading to a change in the way volumes appear in /dev/mapper. Along
the usability improvements are the support of GNUPGHOME environment
variable to support non-standard GnuPG home locations as well updated
translations and the fact that debug messages are now written to
stderr, making it easier to parse stdout.
## 2.7
### Oct 2019
Fixed getent parsing of passwd and notation of conditionals
normalised. A few other minor fixes and documentation improvements.
## 2.6
### May 2019
This release adds new features and provides an important fix for usage
of Tomb with cryptsetup 2.1 and future versions; it also fixes a
whitespace bug in KDF passwords, all fixes are documented in
KNOWN_BUGS. A notable new feature is the libsphinx integration for
password-authenticated key agreement (PAKE). Another feature is the
integration of cloakify to support new cloak/uncloak commands that
hide keys inside long text files. Also support for gpg sub-keys has
been added and overall gpg asymmetric key protection is improved.
## 2.5
### January 2018
This is mostly a bugfix release, including two internal
refactorings. An important change is the re-introduction (since v2.3)
of ownership change of all files inside tombs, to facilitate single
user usage, which is now default and can be prevented using the '-p'
flag on 'open' commands. The first refactoring concerns the test
units, now using the 'sharness' framework. The other refactoring
concerns 'post-hooks' now renamed to 'exec-hooks' and launched on
'open' and 'close' commands with a defined set of arguments. Another
internal change concerns the use of 'findmnt' instead of parsing the
output of 'mount -l', which grants compatibility with more recent
versions of util-linux. A fix was made to the 'slam' command for a
better process detection and the introduction of a new 'ps' command to
just list processes using tombs. Another fix was made to support tomb
hidden filenames (starting with a dot) without any extension. Some
more minor fixes were made to messaging and translations, plus all the
documentation is updated.
## 2.4
### April 2017
This release introduces a major new feature with support for
asymmetric encryption of Tomb keys using public/private GPG key
pairs. It is now possible to protect a Tomb key using a GPG key (which
can also be password-less for automations) as well encrypt a Tomb key
for multiple recipients (list of GPG ids). Other improvements include:
a fix to the 'slam' command with better detection of running programs
using 'lsof' (new optional dependency); a fix to 'forge' key creation
to really use 512 bits long keys to really trigger usage of AES256;
correct support for opening tombs in read-only mode; update of the
Tomber python wrapper in extras. Documentation has been updated.
## 2.3
### January 2017
Fix to bug occurring when using ZSh version 5.3 or higher. Fix to
inclusion of final newline in keys generated with 2.2, only affecting
third-party software. Removed chmod/chown of tombs when open. Enhanced
continuous integration script with regression tests with usage of old
stable versions of Tomb and shellcheck linting. Improved parser and
post-hooks to avoid usage of external binaries (grep and cat) also
improving security when decrypting keys. Fix for clean execution via
sudo nopasswd. Updated extras/gtomb to latest stable version. Various
documentation updates about kdf, using images as keys, deniability and
gpg-agent usage. New experimental port to Android platforms in extras.
## 2.2
### December 2015

View File

@ -5,6 +5,7 @@
Tomb needs a few programs to be installed on a system in order to work:
* zsh
* file
* sudo
* gnupg
* cryptsetup
@ -20,12 +21,12 @@ To install Tomb simply download the source distribution (the tar.gz file)
from https://files.dyne.org/tomb and decompress it. From a terminal:
cd Downloads
tar xvfz Tomb-2.0.1.tar.gz (correct with actual file name)
tar xvfz Tomb-2.4.tar.gz (correct with actual file name)
Then enter its directory and run 'make install' as root, this will install
Tomb into /usr/local:
cd Tomb-2.0.1 (correct with actual directory name)
cd Tomb-2.4 (correct with actual directory name)
sudo make install
After installation one can read the commandline help or read the manual:
@ -50,7 +51,7 @@ The key can also be hidden in an image, to be used as key later
tomb bury -k secrets.tomb.key nosferatu.jpg (hide the key in a jpeg image)
tomb open -k nosferatu.jpg secrets.tomb (use the jpeg image to open the tomb)
Or backupped to a QRCode that can be printed on paper and hidden in
Or backed up to a QRCode that can be printed on paper and hidden in
books. QRCodes can be scanned with any mobile application, resulting
into a block of text that can be used with `-k` just as a normal key.
@ -60,41 +61,13 @@ There are some more things that tomb can do for you, make sure you
have a look at the manpage and at the commandline help to find out
more.
## Basic usage notes
Here we collect notes on common issues users may or may not experience
and the commonly working solutions found.
### Pinentry issues
If pinentry has problems dealing with the password because of language
or tty settings on your system, try running `gpg-agent` by launching it
from the session initialization (~/.xsession or ~/.xinitrc) with this
command:
```
eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info")
```
### Deleting history
To improve deniability one has to avoid that tomb commands are
recorded in the shell history. In order to do so the
`HISTIGNORESPACE=1` environment setting of Zsh comes handy. Anywhere
in the `.zshrc` put:
```
export HISTIGNORESPACE=1
alias tomb=' tomb'
```
# Advanced usage
## Install optional tools
# Optional tools
Tomb can use some optional tools to extend its functionalities:
executable | function
---------- | ---------------------------------------------------
lsof | slam a tomb (close even if open programs)
dcfldd | show progress while digging tombs and keys
steghide | bury and exhume keys inside images
resizefs | extend the size of existing tomb volumes
@ -110,12 +83,12 @@ the packages provided by each distribution.
Once any of the above is installed Tomb will find the tool automatically.
## Install Tomb Extras
# Extras
Tomb comes with a bunch of extra tools that contribute to enhance its
functionality or integrate it into particular system environments.
### extras/gtk-tray
## extras/gtk-tray
The Gtk tray adds a nifty tomb skull into the desktop toolbar: one can
use it to close, slam and explore the open tomb represented by it.
@ -130,15 +103,15 @@ To have it change directory `extras/gtk-tray` then
3. run `sudo make install` (default PREFIX is `/usr/local`)
4. start `tomb-gtk-tray tombname` after the tomb is open
Of cource one can include the launch of tomb-gtk-tray scripts.
Of course, one can include the launch of tomb-gtk-tray scripts.
### extras/qt-tray
## extras/qt-tray
The QT tray adds a tomb tray in a QT desktop toolbar. It requires at
least QT libraries of version 5.4 or above.
Build with 'qmake' and then 'make'.
### extras/kdf-keys
## extras/kdf-keys
The KDF wrapper programs allows one to use KDF rounds on passwords in
order to obstruct dictionary based and similar brute-forcing attacks.
@ -165,7 +138,7 @@ Please note that it doesn't makes much sense to use KDF keys and
steganography, since the latter will invalidate the brute-forcing
protection. For details on the issue see [KNOWN_BUGS.md](KNOWN_BUGS).
### extras/translations/
## extras/translations/
There are translations available for Tomb and they are installed by
default. If you wish to update them manually navigate to extras/po
@ -174,7 +147,7 @@ and run 'make install' as root:
cd extras/translations
sudo make install
### extras/gtomb/
## extras/gtomb/
This is a minimalistic graphical user interface scripted in ZSh
depending from Zenity to display dialog boxes. It covers all basic
@ -191,7 +164,7 @@ Sure as Hell it can! Licensing issues aside ([GNU GPLv3+](COPYING)
terms) Tomb provides machine-readable output and interaction via some
flags:
flag | function
flag | function
--------------- | ------------------------------------------------
--no-color | avoids coloring output to allow parsing
--unsafe | allows passwords options and cleartext key from stdin
@ -204,6 +177,14 @@ other people logged on the same system can easily log your passwords
while such commands are executing.
We only recommend using the pinentry to input your passwords.
At the time of writing another free software graphical application
supports opening and closing Tombs via a plugin installed by
default: [zuluCrypt](https://mhogomchungu.github.io/zuluCrypt/). One
needs to activate the Tomb plugin included in the zuluCrypt source to
be able to create, open and close tombs. Beware zuluCrypt may miss
advanced Tomb functionalities that are only available from the
command-line.
## Python
![](extras/images/python_for_tomb.png)
@ -212,14 +193,6 @@ A Python wrapper is under development and already usable, but it
introduces some vulnerabilities mentioned above. Find it in
`extras/tomber`. For more information see [PYTHON](extras/PYTHON.md).
## Graphical applications
So far the only graphical application supporting Tomb volumes is
[ZuluCrypt](https://github.com/mhogomchungu/zuluCrypt). One needs to
activate the Tomb plugin included in its source and will be able to
create, open and close tombs. It might still miss advanced Tomb
functionalities that are only available from the command-line.
## Let us know!
If you plan to develop any kind of wrapper for Tomb you are welcome to

View File

@ -1,3 +1,42 @@
# Password bug in X11 when using pinentry-curses
## Issue with Tomb version 2.6 and 2.7
This bug affects systems with a running X11 DISPLAY, but where only
pinentry-ncurses is installed. It wrongly reads the input password: no
matter what string is chosen, the password becomes:
tomb [W] Detected DISPLAY, but only pinentry-curses is found.
Following the fix in Tomb 2.8 affected users will need to use the line
above as password to open their tomb and should change their key with
a new password using 'tomb passwd'.
# Cryptsetup change of default to luks2
## Issue opening tombs with cryptsetup >2.0
Tomb uses the cryptsetup LUKS volume header default to type luks1
which has been for long the default in cryptsetup. But starting from
cryptsetup v2.1 a new default has been introduced (luks2) and the
--type option added to specify the old luks1.
Using Tomb version 2.6 (and future releases) the problem opening tombs
using recent GNU/Linux distributions is fixed.
# Whitespace in KDF passwords
## Issue affecting passwords used with PBKDF2 keys (<2.6)
Up until and including Tomb's version 2.5 the PBKDF2 wrapper for keys
in Tomb has a bug affecting passwords that contain whitespace. Since
the passwords are trimmed at the first whitespace, this makes them
weaker, while fortunately the KDF transformation still applies.
This issue is fixed in Tomb version 2.6: all users adopting KDF keys
that have passwords containing whitespace should change them,
knowing that their "old password" is trimmed until the whitespace.
Users adopting GPG keys or plain (without KDF wrapper) can ignore
this bug.
# Vulnerability to password bruteforcing
## Issue affecting keys used in steganography
@ -23,6 +62,18 @@
a good practice to change it using the `setkey` command on a secure
machine, possibly while off-line or in single user mode.
# Ending newline in tomb keys
## 2.2
When used to forge new keys, Tomb version 2.2 incorrectly added a new
line ('\n', 0x0A) character at the end of each key's secret sequence
before encoding it with GnuPG. This does not affect Tomb regression
and compatibility with other Tomb versions as this final newline is
ignored in any case, but third party software may have
problems. Those writing a software that supports opening Tomb files
should always ignore the final newline when present in the secret
material obtained after decoding the key with the password.
# Versioning and stdin key
## 1.5

View File

@ -2,6 +2,12 @@ PROG = tomb
PREFIX ?= /usr/local
MANDIR ?= ${PREFIX}/share/man
deps:
@[ -r /etc/debian_version ] && { \
apt-get install -qy zsh cryptsetup file gnupg pinentry-curses; }
@[ -r /etc/fedora-release ] ^^ { \
yum install -y zsh cryptsetup file gnupg pinentry-curses; }
all:
@echo
@echo "Tomb is a script and does not need compilation, it can be simply executed."
@ -9,7 +15,7 @@ all:
@echo "To install it in /usr/local together with its manpage use 'make install'."
@echo
@echo "To run Tomb one needs to have some tools installed on the system:"
@echo "Sudo, cryptsetup, pinentry and gnupg. Also wipe is recommended."
@echo "Sudo, cryptsetup, pinentry and gnupg."
@echo
install:
@ -24,3 +30,6 @@ install:
test:
make -C extras/test
lint:
shellcheck -s bash -e SC1058,SC1073,SC1072,SC1009 tomb

218
README.md
View File

@ -1,42 +1,14 @@
..... ..
.H8888888h. ~-. . uW8"
888888888888x `> u. .. . : `t888
X~ `?888888hx~ ...ue888b .888: x888 x888. 8888 .
' x8.^"*88*" 888R Y888r ~`8888~'888X`?888f` 9888.z88N
`-:- X8888x 888R I888> X888 888X '888> 9888 888E
488888> 888R I888> X888 888X '888> 9888 888E
.. `"88* 888R I888> X888 888X '888> 9888 888E
x88888nX" . u8888cJ888 X888 888X '888> 9888 888E
!"*8888888n.. : "*888*P" "*88%""*88" '888!` .8888 888"
' "*88888888* 'Y" `~ " `"` `%888*%"
^"***"` "`
# Tomb: The Linux Crypto Undertaker
*A minimalistic commandline tool to manage encrypted volumes* aka **The Crypto Undertaker**
[![Build Status](https://github.com/dyne/tomb/actions/workflows/linux.yml/badge.svg)](https://github.com/dyne/Tomb/actions)
<!-- [![Build Status](https://github.com/dyne/tomb/actions/workflows/portable.yml/badge.svg)](https://github.com/dyne/Tomb/actions) -->
[![software by Dyne.org](https://www.dyne.org/wp-content/uploads/2015/12/software_by_dyne.png)](http://www.dyne.org)
Minimalistic command line tool based on Linux dm-crypt and LUKS, trusted by hackers since 2007.
Updates on website: https://www.dyne.org/software/tomb
Get the stable .tar.gz signed release for production use!
Download it from https://files.dyne.org/tomb
You can keep your volumes secure and easily manageable with simple commands.
![tomb's logo](https://github.com/dyne/Tomb/blob/master/extras/images/monmort.png)
# What is Tomb, the crypto undertaker?
Tomb aims to be a free and open source system for easy encryption and
backup of personal files, written in code that is easy to review and
links shared GNU/Linux components.
At present, Tomb consists of a simple shell script (Zsh) using
standard filesystem tools (GNU) and the cryptographic API of the Linux
kernel (cryptsetup and LUKS). Tomb can also produce machine parsable
output to facilitate its use inside graphical applications.
# How does it work?
To create a Tomb, do:
```
$ tomb dig -s 100 secret.tomb
$ tomb forge secret.tomb.key
@ -46,187 +18,41 @@ To open it, do
```
$ tomb open secret.tomb -k secret.tomb.key
```
and after you are done
And after you are done
```
$ tomb close
```
or if you are in a hurry
Or, if you are in a hurry, kill all processes with open files inside your tomb and close it.
```
$ tomb slam all
$ tomb slam
```
## [Get started on dyne.org/software/tomb](https://dyne.org/software/tomb)
For the instructions on how to get started using Tomb, see [INSTALL](INSTALL.md).
<a href="https://dyne.org/software/tomb"><img src="https://files.dyne.org/software_by_dyne.png" width="30%"></a>
```
Syntax: tomb [options] command [arguments]
All information is found on our website.
Commands:
Use only stable and signed releases in production!
// Creation:
dig create a new empty TOMB file of size -s in MB
forge create a new KEY file and set its password
lock installs a lock on a TOMB to use it with KEY
### 💾 [Download from files.dyne.org/tomb](https://files.dyne.org/tomb/)
// Operations on tombs:
open open an existing TOMB (-k specify KEY file)
index update the search indexes of tombs
search looks for filenames matching text patterns
list list of open TOMBs and information on them
close close a specific TOMB (or 'all')
slam slam a TOMB killing all programs using it
resize resize a TOMB to a new size -s (can only grow)
Tomb's development is community-based!
// Operations on keys:
passwd change the password of a KEY (needs old pass)
setkey change the KEY locking a TOMB (needs old key and pass)
## How can you help
// Backup on paper:
engrave makes a QR code of a KEY to be saved on paper
Donations are very welcome on [dyne.org/donate](https://www.dyne.org/donate)
// Steganography:
bury hide a KEY inside a JPEG image (for use with -k)
exhume extract a KEY from a JPEG image (prints to stout)
Translations are also welcome: see our simple [translation guide](https://github.com/dyne/Tomb/blob/master/extras/translations/README.md)
Options:
Tomb's code is short and readable: don't be afraid to inspect it! If you plan to submit a PR, please remember that this is a minimalist tool, and the code should be short and readable. Also, first, read our small intro to [Tomb's coding style](doc/HACKING.txt).
-s size of the tomb file when creating/resizing one (in MB)
-k path to the key to be used ('-k -' to read from stdin)
-n don't process the hooks found in tomb
-o mount options used to open (default: rw,noatime,nodev)
-f force operation (i.e. even if swap is active)
--kdf generate passwords armored against dictionary attacks
We have a [space for issues](https://github.com/dyne/Tomb/issues) open for detailed bug reports. Always include the Tomb version being used when filing a case, please.
-h print this help
-v print version, license and list of available ciphers
-q run quietly without printing informations
-D print debugging information at runtime
```
# What is this for, exactly?
This tool can be used to dig .tomb files (LUKS volumes), forge keys
protected by a password (GnuPG symmetric encryption) and use the keys
to lock the tombs. Tombs are like single files whose contents are
inaccessible in the absence of the key they were locked with and its
password.
Once open, the tombs are just like normal folders and can contain
different files, plus they offer advanced functionalities like bind
and execution hooks and fast search, or they can be slammed close even
if busy. Keys can be stored on separate media like USB sticks, NFC, or
bluetooth devices to make the transport of data safer: one always
needs both the tomb and the key, plus its password, to access it.
The tomb script takes care of several details to improve user's
behaviour and the security of tombs in everyday usage: secures the
typing of passwords from keyloggers, facilitates hiding keys inside
images, indexes and search a tomb's contents, lists open tombs and
selectively closes them, warns the user about free space and last time
usage, etc.
# How secure is this?
Death is the only sure thing in life. That said, Tomb is a pretty
secure tool especially because it is kept minimal, its source is
always open to review (even when installed) and its code is easy to
read with a bit of shell script knowledge.
All encryption tools being used in Tomb are included as default in
many GNU/Linux operating systems and therefore are regularly peer
reviewed: we don't add anything else to them really, just a layer of
usability.
The file [KNOWN_BUGS.md](KNOWN_BUGS.md) contains some notes on known
vulnerabilities and threat model analysis.
In absence or malfunction of the Tomb script it is always possible to
access the contents of a Tomb only using a dm-crypt enabled Linux
kernel, cryptsetup, GnuPG and any shell interpreter issuing the
following commands as root:
```
lo=$(losetup -f)
losetup -f secret.tomb
pass="$(gpg -d secret.key)"
echo -n -e "$pass" | cryptsetup --key-file - luksOpen $lo secret
mount /dev/mapper/secret /mnt
```
One can change the last argument `/mnt` to where the Tomb has to be
mounted and made accessible. To close the tomb then use:
```
umount /mnt
cryptsetup luksClose /dev/mapper/secret
```
# Stage of development
Tomb is an evolution of the 'mknest' tool developed for the
[dyne:bolic](http://www.dynebolic.org) 100% Free GNU/Linux
distribution in 2001: its 'nesting' mechanism allowed the liveCD users
to encrypt and make persistent home directories. Since then the same
shell routines kept being maintained and used for dyne:bolic until
2007, when they were ported to work on more GNU/Linux distributions.
As of today, Tomb is a very stable tool also used in mission critical
situations by a number of activists in dangerous zones. It has been
reviewed by forensics analysts and it can be considered to be safe for
military grade use where the integrity of information stored depends
on the user's behaviour and the strength of a standard AES-256 (XTS
plain) encryption algorithm.
# Use stable releases in production!
Anyone planning to use Tomb to store and access secrets should not use
the latest development version in Git, but use instead the .tar.gz
release on https://files.dyne.org/tomb . The stable version will
always ensure backward compatibility with older tombs: we make sure it
creates sane tombs and keys by running various tests before releasing
it. The development version in Git might introduce sudden bugs and is
not guaranteed to produce backward- or forward-compatible tombs and keys.
The development version in Git should be used to report bugs, test new
features and develop patches.
So be warned: do not use the latest Git version in production
environments, but use a stable release versioned and packed as
tarball on https://files.dyne.org/tomb
# How can you help
Donations are very welcome, please go to https://www.dyne.org/donate
Translations are also needed: they can be contributed via this website
https://poeditor.com/join/project/b276xMGAmB
or simply sending the .po file. Start from `extras/po/tomb.pot`.
The code is pretty short and readable: start looking around and the
materials found in `doc/` which are good pointers at security measures
to be further implemented.
For the bleeding edge visit https://github.com/dyne/Tomb
If you plan to commit code into Tomb, please keep in mind this is a
minimalist tool and its code should be readable. Guidelines on the
coding style are illustrated in [doc/HACKING.txt](doc/HACKING.txt).
Tomb's developers can be contacted using the issues on GitHub or over
IRC on https://irc.dyne.org channel **#dyne** (or direct port 9999 SSL)
There is also a [space for discussion](https://github.com/dyne/Tomb/discussions) of new features, desiderata and whatnot on github.
# Licensing
Tomb is Copyright (C) 2007-2016 by the Dyne.org Foundation
Tomb is Copyright (C) 2007-2024 by the Dyne.org Foundation and maintained by [Jaromil](https://github.com/jaromil). The [AUTHORS](AUTHORS.md) file contains more information on all the developers involved. The license is GNU Public License v3.
More information on all the developers involved is found in the
[AUTHORS](AUTHORS.md) file.
## [More info on dyne.org/software/tomb](https://dyne.org/software/tomb)
This source code is free software; you can redistribute it and/or
modify it under the terms of the GNU Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
to the GNU Public License for more details.
You should have received a copy of the GNU Public License along with
this source code; if not, write to: Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA.

View File

@ -4,11 +4,7 @@ Style guidelines
Indentation
-----------
Code must be indented using four spaces.
In vim, this can be accomplished using
set shiftwidth=4
set expandtab
Code must be indented using hard tabs.
Naming
------

View File

@ -1,13 +1,11 @@
Overview
=========
What's a key?
It basicly is a gpg simmetrically encrypted, ascii-armored file.
It's encryption key is a function (see below, on KDF section) of your tomb
What is a key?
It is a gpg symmetrically encrypted, ascii-armored file.
The encryption key is a function (see below, on KDF section) of your tomb
passphrase.
Layout
======

View File

@ -31,11 +31,11 @@ Linux hard disk encryption settings
and LRW for standardisation. EME along with it's cousin CMC seems to
provide the best security level, but imposes additional encryption
steps. Plumb-IV is discussed only for reference, because it has the
same performance penalty as CMC, but in constrast suffers from
same performance penalty as CMC, but in contrast suffers from
weaknesses of CBC encryption.
As convention, this document will use the term "blocks", when it
referes to a single block of plain or cipher text (usually 16 byte),
refers to a single block of plain or cipher text (usually 16 byte),
and will use the term "sectors", when it refers to a 512-byte wide hard
disk block.
@ -171,8 +171,8 @@ Content leaks
cipher blocks. But how does this number grow in n? Obviously
exponentially. Plotting a few a decimal powers shows that the chance
for finding at least on identical cipher pair flips to 1 around n =
10^20 (n = 10^40 for a 256-bit cipher). This inflexion point is reached
for a 146 million TB storage (or a hundered thousand trillion trillions
10^20 (n = 10^40 for a 256-bit cipher). This inflection point is reached
for a 146 million TB storage (or a hundred thousand trillion trillions
TB storage for a 256-bit cipher).
^1The blocks with available preceding cipher blocks is 62/1KB for all

View File

@ -9,8 +9,14 @@ Roadmap notes:
* Release 3.0
*** [#A] integrate the zenroom for custom crypto functions
https://decodeproject.github.io/lua-zenroom
*** [#A] study cryptsetup 2.0 and integrate it
In particular kernel keystore functionalities
*** [#A] support BtrFS and snapshots
*** [#A] system to split passwords in parts (ssss)
*** [#B] modular encryption system support
to go beyond dm-crypt/cryptsetup
@ -21,13 +27,21 @@ Roadmap notes:
*** [#B] udev rules to avoid usb automount of keyplug in gnome
*** [#B] sign and verify tomb script integrity
*** [#B] make a graphical tomb undertaker (gnome-druid in glade?)
*** [#B] analyse and show tomb entropy using libdisorder
*** [#B] use inotify on tomb
inotify can also count when was the last time tomb was used and
unmount it automatically after a timeout, see how much free space
is left and warn when the space is almost finished
*** DONE [#A] system to split passwords in parts
CLOSED: [2018-01-03 Wed 19:48]
solved with secrets.dyne.org
*** DONE [#B] make a graphical tomb undertaker (gnome-druid in glade?)
CLOSED: [2018-01-03 Wed 19:49]
solved by gtomb and qtomb
** Notes from #CybRes

View File

@ -1,4 +1,4 @@
.TH tomb 1 "November 26, 2014" "tomb"
.TH tomb 1 "Jun 25, 2023" "tomb"
.SH NAME
Tomb \- the Crypto Undertaker
@ -30,23 +30,27 @@ harddisk and its key file on a USB stick.
.IP "dig"
Generates a file that can be used as a tomb and will occupy as much
space as its desired initial size, the unlocked \fI.tomb\fR file can
then be locked using a \fIkey\fR. It takes a mandatory \fI-s\fR option which is
the size in megabytes (MiB). Tombs are digged using
low-quality random data (/dev/urandom).
then be locked using a \fIkey\fR. It takes a mandatory \fI-s\fR option
which is the size in megabytes (MiB). Tombs are digged using random
data gathered from a non-blocking source (/dev/urandom). For very
large tombs this may take up too much time and entropy, then it is
possible to use \fIfallocate(1)\fR being aware it does not pre-fill
with random data, decreasing the tomb's security.
.B
.IP "forge"
Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to
protect its usage. This operation requires high quality random data
(/dev/random) which can take quite some time to be gathered on a
server: it works better on a desktop where the mouse can be moved
around for entropy. The default cipher to protect the key is AES256, a
custom one can be specified using the \fI-o\fR option, for a list of
supported ciphers use \fI-v\fR. For additional protection against
dictionary attacks on keys, the (experimental) \fI--kdf\fR option can
be used when forging a key, making sure that the \fItomb-kdb-pbkdf2\fR
binaries in \fIextras/kdf\fR were compiled and installed on the
system.
Creates a new \fIkey\fR and prompts the user for a \fIpassword\fR to protect
its usage using symmetric encryption. This operation uses random data from a
non-blocking source (/dev/urandom) and it may take long only in some cases; to
switch using a blocking source the \fI--use-random\fR flag can be used. The
\fI-g\fR option switches on the use of a GPG key instead of a password
(asymmetric encryption), then the \fI-r\fR option indicates the recipient key;
more recipient GPG ids can be indicated (comma separated). The default cipher
to protect the key is AES256, a custom one can be specified using the \fI-o\fR
option, for a list of supported ciphers use \fI-v\fR. For additional protection
against dictionary attacks on keys, the \fI--kdf\fR option can be used when
forging a key, making sure that the binaries in \fIextras/kdf\fR were compiled
and installed on the system.
.B
.IP "lock"
@ -54,20 +58,48 @@ Initializes and locks an empty tomb (made with \fIdig\fR) using a key
(made with \fIforge\fR), making it ready for usage. After this
operation, the tomb can only be opened in possession of the key and
knowing its password. As in any other command requiring a key, the
option \fI-k\fR should be used to specify a key file. The \fI-o\fR
option \fI-k\fR should be used to specify a key file; in case of
encryption to GPG recipients the \fI-g\fR flag should be used followed
by \fI-r\fR and the recipient's secret GPG key id. The \fI-o\fR
option can be used to specify the cipher specification: default is
"aes-xts-plain64:sha256", old versions of Tomb used "aes-cbc-essiv:sha256".
This operation requires root privileges to loopback mount, format the tomb (using
LUKS and Ext4), then set the key in its first LUKS slot.
"aes-xts-plain64", old versions of Tomb used "aes-cbc-essiv:sha256".
If you are looking for something exotic, also try
"serpent-xts-plain64". More options may be found in cryptsetup(8) and
Linux documentation. The \fI--filesystem\fR option can be used to
specify an alternative filesystem used to format the tomb,
in place of the default "ext4". This operation requires root
privileges to loopback mount, format the tomb (using LUKS and mkfs),
then set the key in its first LUKS slot.
.RS
Supported filesystems for \fI--filesystem\fR:
.PD 0
.IP "ext3" 15
using operating system defaults
.IP "ext4"
using operating system defaults
.IP "btrfs"
for tombs >= 47MB using operating system defaults
.IP "btrfsmixedmode"
for tombs >=18MB btrfs mixed mode (see mkfs.btrfs(8))
.IP "ext3maxinodes"
ext3 with a maximum of inodes (for many small files)
.IP "ext4maxinodes"
ext4 with a maximum of inodes (for many small files)
.PD
.RE
.B
.IP "open"
Opens an existing \fI.tomb\fR (first argument) using a key (\fI-k\fR),
if a second argument is given it will indicate the \fImountpoint\fR
where the tomb should be made accessible, else the tomb is mounted in
a directory inside /media (if not available it uses /run/media/$USER).
The option \fI-o\fR can be used to pass mount(8) options
(default: rw,noatime,nodev).
Opens an existing \fItomb file\fR (first argument) using a key
(\fI-k\fR) which can also be hidden inside a \fIjpeg image\fR (see
\fIbury\fR/\fIexhume\fR) or a long text file
(see\fIcloak\fR/\fIuncloak\fR). If a second argument is given it will
indicate the \fImountpoint\fR where the tomb should be made
accessible, else the tomb is mounted in a directory inside /media (if
not available it uses /run/media/$USER). The option \fI-o\fR can be
used to pass mount(8) options (default: rw,noatime,nodev). The
\fI-g\fR option is needed when using GPG encryption to recipients.
.B
.IP "list"
@ -78,12 +110,20 @@ returns an error if it's not found. If the option
\fI--get-mountpoint\fR is used then print a simple list of currently
open tomb mountpoint paths.
.B
.IP "ps"
List all the processes found running inside the tombs that are open,
printing out their PIDs and owners. This is useful to have an overview
of programs that are keeping the tombs busy and would eventually be
killed by the \fIslam\fR command. The lsof(8) utility is used
internally to enumerate processes running in one or all tombs.
.B
.IP "index"
Creates or updates the search indexes of all tombs currently open:
enables use of the \fIsearch\fR command using simple word patterns on
file names. Indexes are created using mlocate's updatedb(8) and
swish-e(1) if they are found on the system. Indexes allow to search
file names. Indexes are created using mlocate/plocate's updatedb(8) and
swish-e(1) if they are found on the system. Indexes allow one to search
very fast for filenames and contents inside a tomb, they are stored
inside it and are not accessible if the Tomb is closed. To avoid
indexing a specific tomb simply touch a \fI.noindex\fR file in it.
@ -92,7 +132,7 @@ indexing a specific tomb simply touch a \fI.noindex\fR file in it.
.IP "search"
Takes any string as argument and searches for them through all tombs
currently open and previously indexed using the \fIindex\fR command.
The search matches filenames if mlocate is installed and then also
The search matches filenames if mlocate/plocate is installed and then also
file contents if swish++ is present on the system, results are listed
on the console.
@ -108,36 +148,43 @@ the tomb is in use by running processes (to force close, see
.IP "slam"
Closes a tomb like the command \fIclose\fR does, but it doesn't fail
even if the tomb is in use by other application processes: it looks
for and violently kills \-9 each of them. This command may
for and closes each of them (in order: TERM, HUP, KILL). This command may
provoke unsaved data loss, but assists users to face surprise
situations.
situations. It requires \fIlsof\fR else it falls back to \fIclose\fR.
.B
.IP "passwd"
Changes the password protecting a key file specified using
\fI-k\fR. The user will need to know the key's current password, then
its content will be decoded and reencoded using the new one. This
action can't be forced if the current password is not known. If the
key file is broken (missing headers) this function also attempts its
recovery.
\fI-k\fR. With keys encrypted for GPG recipients use \fI-g\fR followed
by \fI-r\fR to indicate the new recipient key, or a comma separated
list.. The user will need to know the key's current password, or
possess at least one of the current recipients GPG secret keys,
because the key contents will be decoded and reencoded using the new
passwords or keys. If the key file is broken (missing headers) this
function also attempts its recovery.
.B
.IP "setkey"
Changes the key file that locks a tomb, substituting the old one with
a new one. Both the old and the new key files are needed for this
operation and their passwords must be known. The new key must be
specified using the \fI-k\fR option, the first argument should be the old
key and the second and last argument the tomb file.
operation and their passwords or GPG recipient(s) secret keys must be
available. The new key must be specified using the \fI-k\fR option,
the first argument should be the old key and the second and last
argument the tomb file. Use the \fI-g\fR option to unlock the tomb
with a GPG key, the \fI-r\fR to indicate the recipient or a comma
separated list for more than one recipient.
.B
.IP "resize"
Increase the size of a tomb file to the amount specified by the
\fI-s\fR option, which is the new size in megabytes (MiB). Full access to the tomb using
a key (\fI-k\fR) and its password is required. Tombs can only grow and
can never be made smaller. This command makes use of the cryptsetup
resize feature and the resize2fs command: its much more practical than
creating a new tomb and moving everything into it.
\fI-s\fR option, which is the new size in megabytes (MiB). Full access
to the tomb using a key (\fI-k\fR) and its password is required. Tombs
can only grow and can never be made smaller. This command makes use of
the cryptsetup(8) resize feature and the resize2fs command: its much
more practical than creating a new tomb and moving everything into
it. There is no data-loss if a failure occurs during resize: the
command can be re-launched and the resize operation will complete.
.B
.IP "engrave"
@ -155,7 +202,9 @@ Hides a tomb key (\fI-k\fR) inside a \fIjpeg image\fR (first argument)
using \fIsteganography\fR: the image will change in a way that cannot
be noticed by human eye and hardly detected by data analysis. This
option is useful to backup tomb keys in unsuspected places; it depends
from the availability of \fIsteghide\fR.
from the availability of \fIsteghide\fR. Use the \fI-g\fR flag and
\fI-r\fR option followed by recipient id to use GPG asymmetric
encryption.
.B
.IP "exhume"
@ -166,27 +215,55 @@ containing a key. If the right key password is given, the key will be
exhumed. If the password is not known, it is very hard to verify if a
key is buried in any image or not.
.B
.IP "cloak"
Hides a tomb key (\fI-k\fR) inside a \fIlong plain-text file\fR (first
argument) using \fIsteganography\fR: the text will change in a way
that can hardly be noticed by human eye and hardly detected by data
analysis. This option is useful to backup tomb keys in unsuspected
places; it depends from the availability of \fIcloakify\fR and
consequently \fIpython2\fR. This function does not support asymmetric
encryption using the \fI-g\fR flag.
.B
.IP "uncloak"
This command recovers from long plain-text files the keys that were
previously hidden into them using \fIcloak\fR. Cloak requires a key
filename (\fI-k\fR) and a \fIplain-text\fR file (first argument) known
to be containing a key. If the right key password is given, the key
will be exhumed. If the password is not known, it is quite hard to
verify if a key is buried in a text or not.
.SH OPTIONS
.B
.B
.IP "-k \fI<keyfile>\fR"
For all operations requiring a key, this option specifies the location
of the key file to use. Arguments can also be \fIjpeg image\fR files
where keys have been hidden using the \fIbury\fR command, or text
files retrieved from \fIengraved\fR QR codes. If the \fIkeyfile\fR
argument is "-" (dash), Tomb will read the key from stdin (blocking).
where keys have been hidden using the \fIbury\fR or \fIcloak\fR
commands, or text files retrieved from \fIengraved\fR QR codes. If the
\fIkeyfile\fR argument is "-" (dash), Tomb will read the key from
stdin (blocking).
.B
.IP "-n"
Skip processing of post-hooks and bind-hooks if found inside the tomb.
Skip processing of exec-hooks and bind-hooks if found inside the tomb.
See the \fIHOOKS\fR section in this manual for more information.
.B
.IP "-p"
When opening a tomb, preserves the ownership of all files and
directories contained in it. Normally the \fIopen\fR command changes
the ownership of a tomb's contents to the UID and GID of the user who
has successfully opened it: it is a usability feature in case a tomb is
used by a single user across different systems. This flag deactivates
this behaviour.
.B
.IP "-o"
Manually specify mount options to be used when opening a tomb instead
of the default \fIrw,noatime,nodev\fR, i.e. to mount a tomb read-only
(ro) to prevent any modification of its data. Can also be used to
change the symmetric encryption algorithm for keys during \fIforge\fR
operations (default \fIAES256\fR) or the LUKS encryption method during
\fIlock\fR operations (default \fIaes-xts-plain64:sha256\fR).
\fIlock\fR operations (default \fIaes-xts-plain64\fR).
.B
.IP "-f"
Force flag, currently used to override swap checks, might be
@ -197,12 +274,51 @@ what you are doing if you force an operation.
When digging or resizing a tomb, this option must be used to specify
the \fIsize\fR of the new file to be created. Units are megabytes (MiB).
.B
.IP "-g"
Tell tomb to use a asymmetric GnuPG key encryption instead of a
symmetric passphrase to protect a tomb key. This option can be
followed by \fI-r\fR when the command needs to specify recipient(s).
.B
.IP "-r \fI<gpg_id>[,<gpg_id2>]\fR"
Provide a new set of recipient(s) to encrypt a tomb key. \fIgpg_ids\fR
can be one or more GPG key ID, comma separated. All GPG keys must be
trusted keys in GPG.
.B
.IP "--kdf \fI<itertime>\fR"
Activate the KDF feature against dictionary attacks when creating a
key: forces a delay of \fI<itertime>\fR seconds every time this key is used.
You should keep in mind that the actual iteration count is calculated based on
the performance of the computer where you forge the key.
The argument must be an integer, so you cannot say \fI--kdf 0.3\fR for 300ms.
Activate the KDF feature against dictionary attacks when creating a key: forces
a delay of \fI<itertime>\fR times every time this key is used. The actual time
to wait depends on the CPU speed (default) or the RAM size (argon2) of the
computer where the key is used. Using 5 or 10 is a sane amount for modern
computers, the value is multiplied by 1 million.
.B
.IP "--kdftype \fIargon2 | pbkdf2\fR"
Adopt the \fIargon2\fR algorithm for KDF, stressing the RAM capacity rather
than the CPU speed of the computer decrypting the tomb. Requires the
\fIargon2\fR binary by P-H-C to be installed, as packaged by most distros.
Default is \fIpbkdf2\fR.
.B
.IP "--kdfmem \fI<memory>\fR"
In case of \fIargon2\fR KDF algorithm, this value specifies the size of RAM
used: it consists of a number which is the elevated power of two in kilobytes.
Default is 18 which is 250 MiB (2^18 = 262,144 kilobytes).
.B
.IP "--sudo \fI<executable>\fR"
Select a different tool than sudo for privilege escalation.
Alternatives supported so far are: pkexec, doas, sup, sud. For any
alternative to work the executable must be included in the current
PATH.
.B
.IP "--sphx-user \fI<username>\fR"
Activate the SPHINX feature for password-authenticated key agreement.
This option indicates the \fI<username>\fR used to retrieve the
password from a sphinx oracle key reachable via TCP/IP.
.B
.IP "--sphx-host \fI<domain>\fR"
Activate the SPHINX feature for password-authenticated key agreement.
This option indicates the \fI<domain>\fR used to retrieve the password
from a sphinx oracle daemon reachable via TCP/IP. This is not the
network address of the daemon, which is configured in /etc/sphinx
.B
.IP "-h"
Display a help text and quit.
@ -227,9 +343,10 @@ Enable using dev-mode arguments, i.e. to pass passwords from
commandline options. This is mostly used needed for execution by
wrappers and testing suite.
.B
.IP "--use-urandom"
Use an inferior quality random source to improve the speed of key
generation at the cost of security (needed for the testing suite).
.IP "--use-random"
Use a blocking random source. Tomb uses by default /dev/urandom since
the non-blocking source of Linux kernel doesn't degrades the quality
of random.
.B
.IP "--tomb-pwd <string>"
Use string as password when needed on tomb.
@ -251,40 +368,43 @@ Switch to this TTY terminal when dropping privileges.
Hooks are special files that can be placed inside the tomb and trigger
actions when it is opened and closed; there are two kinds of such
files: \fIbind-hooks\fR and \fIpost-hooks\fR can be placed in the
files: \fIbind-hooks\fR and \fIexec-hooks\fR can be placed in the
base root of the tomb.
.B
.IP "bind-hooks"
This hook file consists of a simple two column list of files or
directories inside the tomb to be made directly accessible inside the
current user's home directory. Tomb will use the "mount \-o bind"
command to bind locations inside the tomb to locations found in $HOME
so in the first column are indicated paths relative to the tomb and in
the second column are indicated paths relative to $HOME contents, for
This hook file consists of a simple text file named \fIbind-hooks\fR
containing a two column list of paths to files or directories inside
the tomb. The files and directories will be made directly
accessible by the tomb \fIopen\fR command inside the current user's
home directory. Tomb uses internally the "mount \-o bind" command to
bind locations inside the tomb to locations found in $HOME. In the
first column are indicated paths relative to the tomb and in the
second column are indicated paths relative to $HOME contents, for
example:
.EX
mail mail
.gnupg .gnupg
.fmrc .fetchmailrc
.mozilla .mozilla
mail mail
.gnupg .gnupg
.fmrc .fetchmailrc
.mozilla .mozilla
.EE
.B
.IP "post-hooks"
This hook file gets executed as user by tomb right after opening it;
it should be a regular shell script, starting with a shebang. Tomb
executes this hook as user (dropping root privileges) and giving it
two arguments: "$1" is "open" or "close" depending from the tomb
command given, "$2" is the full path to the mountpoint where the tomb
is open.
.IP "exec-hooks"
This hook file gets executed as user by tomb with the first argument
determining the step of execution (\fIopen\fR or \fIclose\fR) and the second
being the full path to the mountpoint. The \fIexec-hooks\fR file should be
executable (ELF or shell script) and present inside the Tomb. Tomb
executes this hook as user and adds the name, loopback device and
dev-mapper device paths as additional arguments for the \fIclose\fR
command.
.SH PRIVILEGE ESCALATION
The tomb commandline tool needs to acquire super user rights to
execute most of its operations: to do so it uses sudo(8), while
pinentry(1) is adopted to collect passwords from the user. Tomb
executes as super user only when required.
execute most of its operations: so it uses sudo(8) or other configured
tools, while pinentry(1) is adopted to collect passwords from the
user. Tomb executes as super user only when required.
To be made available on multi user systems, the superuser execution of
the tomb script can be authorized for users without jeopardizing the
@ -294,6 +414,15 @@ whole system's security: just add such a line to \fI/etc/sudoers\fR:
username ALL=NOPASSWD: /usr/local/bin/tomb
.EE
To avoid that tomb execution is logged by \fIsyslog\fR also add:
.EX
Cmnd_Alias TOMB = /usr/local/bin/tomb
Defaults!TOMB !syslog
.EE
.SH PASSWORD INPUT
Password input is handled by the pinentry program: it can be text
based or graphical and is usually configured with a symlink. When
using Tomb in X11 it is better to use a graphical pinentry-gtk2 or
@ -324,6 +453,71 @@ If you don't need swap, execute \fI swapoff -a\fR. If you really need
it, you could make an encrypted swap partition. Tomb doesn't detect if
your swap is encrypted, and will complain anyway.
.SH DENIABILITY
The possibility to have an encrypted volume which is invisible and
cannot be detected is called "deniability". The cryptographic layer of
the device mapper in Linux (dm-crypt) does not implement
deniability. Tomb is just a wrapper on top of that and it doesn't add
cryptographic deniability. However a certain way of using tomb can
facilitate a weak sort of deniability outside of the scenario of
seized devices and forensic analysis of files and blocks on disc.
For instance to eliminate any trace of tomb usage from the shell
history ZSh users can activate the "HISTIGNORESPACE" feature and
prefix all invocations of tomb with a blank space, including two lines
in ".zshrc":
.EX
export HISTIGNORESPACE=1
alias tomb=' tomb'
.EE
.SH PASSWORD INPUT
Tomb uses the external program "pinentry" to let users type the key password
into a terminal or a graphical window. This program works in conjunction with
"gpg-agent", a daemon running in background to facilitate secret key
management with gpg. It is recommended one runs "gpg-agent" launching it from
the X session initialization ("~/.xsession" or "~/.xinitrc" files) with this
command:
.EX
eval $(gpg-agent --daemon --write-env-file "${HOME}/.gpg-agent-info")
.EE
In the future it may become mandatory to run gpg-agent when using tomb.
.SH SHARE A TOMB
A tomb key can be encrypted with more than one recipient. Therefore, a
tomb can be shared between different users. The recipients are given
using the \fI-r\fR (or/and \fI-R\fR) option and if multiple each GPG
key ID must be separated by a comma (\fI,\fR). Sharing a tomb is a
very sensitive action and the user needs to trust that all the GPG
public keys used are kept safe. If one of them its stolen or lost, it
will be always possible to use it to access the tomb key unless all
its copies are destroyed. The \fI-r\fR option can be used in the tomb
commands: \fIopen\fR, \fIforge\fR \fIsetkey\fR, \fIpasswd\fR,
\fIbury\fR, \fIexhume\fR and \fIresize\fR.
.SH SPHINX (PAKE)
Using the package libsphinx
.UR https://github.com/stef/libsphinx
.UE
and its python client/daemon implementation pwdsphinx
.UR https://github.com/stef/pwdsphinx
.UE
is possible to store and retrieve safely the password that locks the
tomb. Using this feature will make it impossible to retrieve the
password without the oracle sphinx server running and reachable. Each
key entry needs a username and a domain specified on creation and
a password that locks it.
SPHINX makes it impossible to maliciously retrieve the password
locking the tomb key without an attacker accessing both the
server, the sphinx password and the tomb key file.
.SH EXAMPLES
.IP \(bu
@ -373,13 +567,14 @@ keeping all its profile data inside it:
.EX
tomb open FOX.tomb -k FOX.tomb.key
cat <<EOF > /media/FOX.tomb/post-hooks
cat <<EOF > /media/FOX.tomb/exec-hooks
#!/bin/sh
if [ "$1" = "open" ]; then
firefox -no-remote -profile "$2"/firefox-pro &
firefox -no-remote -profile "$2"/firefox-pro &
fi
EOF
chmod +x /media/FOX.tomb/post-hooks
chmod +x /media/FOX.tomb/exec-hooks
mkdir /media/FOX.tomb/firefox-pro
.EE
.IP \(bu
@ -390,16 +585,16 @@ Script a tomb to archive Pictures using Shotwell, launching it on open:
cat <<EOF > /media/Pictures.tomb/bind-hooks
Pictures Pictures
EOF
cat <<EOF > /media/Pictures.tomb/post-hooks
cat <<EOF > /media/Pictures.tomb/exec-hooks
#!/bin/sh
if [ "$1" = "open" ]; then
which shotwell > /dev/null
if [ "$?" = "0" ]; then
shotwell -d "$2"/Pictures/.shotwell &
fi
which shotwell > /dev/null
if [ "$?" = "0" ]; then
shotwell -d "$2"/Pictures/.shotwell &
fi
fi
EOF
chmod +x /media/Pictures.tomb/post-hooks
chmod +x /media/Pictures.tomb/exec-hooks
.EE
.SH BUGS
@ -407,30 +602,12 @@ Please report bugs on the Github issue tracker at
.UR https://github.com/dyne/Tomb/issues
.UE
One can also try to get in touch with developers via the #dyne chat channel on \fIhttps://irc.dyne.org\fR.
.SH AUTHORS
Tomb is designed, written and maintained by Denis Roio aka Jaromil.
Tomb includes code by Anathema, Boyska, Hellekin O. Wolf and GDrooid.
Tomb's artwork is contributed by Jordi aka Mon Mort and Logan VanCuren.
Gettext internationalization and Spanish translation is contributed by
GDrooid, French translation by Hellekin, Russian translation by fsLeg,
German translation by x3nu.
Testing, reviews and documentation are contributed by Dreamer, Shining
the Translucent, Mancausoft, Asbesto Molesto, Nignux, Vlax, The Grugq,
Reiven, GDrooid, Alphazo, Brian May, TheJH, fsLeg, JoelMon and the
Linux Action Show!
Cryptsetup was developed by Christophe Saout and Clemens Fruhwirth.
One can also try to get in touch with developers via the #dyne chat
channel on \fIhttps://irc.dyne.org\fR.
.SH COPYING
This manual is Copyright (c) 2011-2015 by Denis Roio <\fIjaromil@dyne.org\fR>
This manual is Copyright (c) 2011-2021 by Denis Roio <\fIjaromil@dyne.org\fR>
This manual includes contributions by Boyska and Hellekin O. Wolf.
@ -451,15 +628,13 @@ documentation is available for download from its website on
.B
.IP cryptsetup(8)
.B
.IP pinentry(1)
.B
.IP gpg-agent(1)
GnuPG website:
.br
https://www.gnupg.org
GnuPG website: https://www.gnupg.org
DM-Crypt website:
.br
https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
DM-Crypt website: https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt
LUKS website:
.br
https://gitlab.com/cryptsetup/cryptsetup/wikis/home
LUKS website: https://gitlab.com/cryptsetup/cryptsetup/wikis/home

Binary file not shown.

18
extras/android/README.txt Normal file
View File

@ -0,0 +1,18 @@
This is an experimental port of Tomb to Android platform
REQUIRES ROOT - see on-line docs on how to root Android.
This includes a static binary of cryptsetup to interact with the
dm-crypt Linux API for filesystem cryptography (ELF for ARMv6)
Tombs can be created and opened correctly
Only aes-cbc-essiv:sha254 crypto algo is supported.
At the moment tombs cannot be closed.
One has to reboot the device for that.
Other functions have not been tested.
More than ever, this is provided WITHOUT ANY WARRANTY

2760
extras/android/tomb Executable file

File diff suppressed because it is too large Load Diff

33
extras/docker/Dockerfile Normal file
View File

@ -0,0 +1,33 @@
##
# gregtzar/tomb
#
# This creates an Ubuntu derived base image and installs the tomb library
# along with it's dependencies.
FROM dyne/devuan:chimaera
ARG DEBIAN_FRONTEND=noninteractive
ARG TOMB_VERSION=2.9
# Install dependencies
RUN apt-get update -y && \
apt-get install -y -q --no-install-recommends\
make \
sudo \
curl \
rsync \
zsh \
gnupg \
cryptsetup \
pinentry-curses \
file xxd \
steghide \
mlocate \
swish-e
# Build and install Tomb from remote repo
RUN curl https://files.dyne.org/tomb/releases/Tomb-$TOMB_VERSION.tar.gz -o /tmp/Tomb-$TOMB_VERSION.tar.gz && \
cd /tmp && \
tar -zxvf /tmp/Tomb-$TOMB_VERSION.tar.gz && \
cd /tmp/Tomb-$TOMB_VERSION && \
make install

21
extras/docker/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Greg Tzar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

166
extras/docker/README.md Normal file
View File

@ -0,0 +1,166 @@
# docker-tomb
A [docker](https://www.docker.com) wrapper for the the Linux-based [Tomb encryption library](https://www.dyne.org/software/tomb) aimed at enabling functional cross platform support for the [Tomb library](https://github.com/dyne/Tomb) and container format.
**This is a work in progress and while functional it is not yet fully stable or secure.**
# Requirements
This strategy should work on MacOS, Windows, Linux, and any host OS where Docker is available. The only requirement is that you must have Docker running on your machine. My recommendation for first time users is to install [Docker Desktop](https://www.docker.com/products/docker-desktop).
# Container Setup
You can build the container by running the following command from the root directory of this repo:
```bash
docker build -t tomb .
```
Although building it yourself is preferred, you could also pull the container from my [dockerhub repo](https://hub.docker.com/r/gregtzar/tomb).
You can confirm the setup by first opening an interactive bash prompt in the new container and from there confirm the tomb version and exit the container.
```bash
docker run -it --rm tomb /bin/bash
tomb -v
exit
```
### Security Notes
You will see by examining the `Dockerfile` that I am using Docker's official Ubuntu Bionic container as the base, and that the latest Tomb library is downloaded directly from the [official tomb filestore](https://files.dyne.org/tomb). The `TOMB_VERSION` arg near the top of the `Dockerfile` will allow you to configure which Tomb version you want.
Be aware that because of the way the filestore is currently organized, the curl path will break unless the version is set to the most current. I am filing a request with them to address this.
Optionally if you wanted to store the tomb code in the repo, for example inside of a `src` folder, then you could install it alternatively like this:
```
ADD src/Tomb-$TOMB_VERSION.tar.gz /tmp/
RUN cd /tmp/Tomb-$TOMB_VERSION && \
make install
```
# Container Usage
The thing which allows this container strategy to be really functional is the use of Docker's [bind mounts](https://docs.docker.com/storage/bind-mounts/) to give the dockerized tomb instance the ability to read an encrypted/closed tomb volume **from** the host and to then mount the decrypted/open tomb volume **back** to the host where it can be accessed as normal.
## Usage Examples
Once you understand the concept of docker bind mounts you will be able to run tomb commands from the host in any way you need. Here are a few examples to get you started.
### Create a Tomb Volume
First launch an interactive bash prompt in a temporary instance of the container, and bind-mount a working directory from the host machine where we can output the new tomb volume.
This will mount the `/tmp/tomb-gen` directy on the host to the location of `/tomb-gen` inside the docker container instance. **Note**: The `/tmp/tomb-gen` directory must already exist on the host.
```bash
docker run -it --rm --privileged \
--mount type=bind,source=/tmp/tomb-gen,target=/tomb-gen \
tomb /bin/bash
```
Now from the interative prompt inside the docker continer instance creating a new tomb volume is textbook. **Note**: If you get an error about swap partitions during the `forge` command, this is a host issue and not a container issue. You need to disable swapping on the host if you want to fix it, or use `-f` to forge ahead anyways.
```bash
cd /tomb-gen
tomb dig -s 100 secret.tomb
tomb forge secret.tomb.key
tomb lock secret.tomb -k secret.tomb.key
```
Now the host `/tmp/tomb-gen` directory contains a new `secret.tomb` volume and `secret.tomb.key` key. Use `exit` to close the tomb container and the new files will remain on the host.
### Direct Mount a Tomb Volume
This example will use the volume and key we created in the previous example but can be modified to suite your needs. First, launch another interactive bash prompt in a temporary instance of the container.
This will mount the host `/tmp/tomb-gen` directory as well as a new `/tmp/tomb-mount` directory where the container can mount the open tomb volume back to so the host can access it.
```bash
docker run -it --rm --privileged \
--mount type=bind,source=/tmp/tomb-gen,target=/tomb-gen \
--mount type=bind,source=/tmp/tomb-mount,target=/tomb-mount,bind-propagation=shared \
tomb /bin/bash
```
Now from the interative prompt inside the docker continer instance we can open the tomb volume as usual, referencing the tomb volume, key, and mount destinations bind-mounted to the host:
```bash
tomb open /tomb-gen/secret.tomb /tomb-mount -k /tomb-gen/secret.tomb.key
```
The contents of the tomb volume are now accessible via `/tmp/tomb-mount` on the host. Be sure to successfully close the tomb volume before you exit the docker container:
```bash
tomb close
```
### Unpack and Repack a Tomb Volume
This workflow was created as a workaround for the MacOS lack of support for bind propagation which prevents us from directly mounting a tomb volume back to the host OS. The workaround is to use an additional bash script layer to copy and synchronize the tomb volume contents between a bind mounted host directory and the mounted tomb volume accesible only from inside the docker container.
The `tomb.sh` script is desinged to run from the host and requires the following ENV VARS to be available to it:
* `TOMB_DOCKER_IMAGE`: The name of the docker image on the host. In the examples we named it `tomb`.
* `TOMB_VOLUME`: The full path of the tomb volume file on the host. In the example it is `/tmp/tomb-gen/secret.tomb`.
* `TOMB_VOLUME_KEY`: The full path of the tomb volume key file on the host. In the examples it is `/tmp/tomb-gen/secret.tomb.key`.
* `TOMB_OUTPUT_DIR`: The full path of the directory where we will unpack the tomb volume contents to on the host. In the examples it is `/tmp/tomb-out`.
#### tomb.sh unpack
This command will launch a new docker container, open the tomb volume internally, and copy it's contents to the host `TOMB_OUTPUT_DIR` directory which it expects to be empty. It will then close the tomb volume and exit the docker container.
```bash
export TOMB_DOCKER_IMAGE=tomb
export TOMB_VOLUME=/tmp/tomb-gen/secret.tomb
export TOMB_VOLUME_KEY=/tmp/tomb-gen/secret.tomb.key
export TOMB_OUTPUT_DIR=/tmp/tomb-out
./tomb.sh unpack
```
If needed, the `-f` flag can be forwarded through to tomb by passing it to the `unpack` command.
```bash
./tomb.sh unpack -f
```
#### tomb.sh repack
This command will launch a new docker container, open the tomb volume internally, and copy the current contents of the host `TOMB_OUTPUT_DIR` directory back into the open tomb volume. Copying uses `rsync` with `--delete` any files which were removed from `TOMB_OUTPUT_DIR` will also be removed from the open tomb volume contents. It will then close the tomb volume, delete the contents of `TOMB_OUTPUT_DIR`, and exit the docker container. This way all changes are persisted back to the tomb volume. Each step is performed sequentially and an error will cause the entire sequence to bail, so for example if we are unable to successfully close the tomb volume the the contents of `TOMB_OUTPUT_DIR` will not be deleted.
```bash
export TOMB_DOCKER_IMAGE=tomb
export TOMB_VOLUME=/tmp/tomb-gen/secret.tomb
export TOMB_VOLUME_KEY=/tmp/tomb-gen/secret.tomb.key
export TOMB_OUTPUT_DIR=/tmp/tomb-out
./tomb.sh repack
```
If needed, the `-f` flag can be forwarded through to tomb by passing it to the `repack` command.
```bash
./tomb.sh repack -f
```
# Known Issues and Workarounds
## Swap partitions
The user may get a security error from Tomb regarding the swap partitions and is prompted to either disable them or force an override. While the swap error is reported by tomb, it actually pertains to the state of the host OS and not the container OS. So the recommended `swapoff -a` command will obviously work on a Linux host but won't work on a MacOS or Windows host. In these later cases it is up to the user to disable swapping (either temporarily or permanently) on their host os should they choose to, or use the `-f` flag with tomb.
## Privileged access containers
The docker container must be launched in privileged mode, otherwise the tomb library cannot mount loopback devices which it depends. Launching a docker container in privileged mode removes some of the container security sandboxing and grants anything in the container access to parts of your host system. Until docker can support loopback mounting for unprivileged users this may be the only option.
## Bind propagation in MacOS
MacOS does not supported bind propagation for bind mounts, which means we cannot set the `bind-propagation=shared` option on the `--mount` flag for the docker instance. So even though we can mount the tomb volume back to the bind-mounted host directory, the mount will not recurse and the directory will appear empty from the hosts point of view. I have attempted to overcome this a variety of ways including FUSE `bindfs` and symlinks and cannot find a way to expose the mounted tomb volume directory back to the host of MacOS. As a workaround I created the `tomb unpack` and `tomb repack` commands which use `rsync` to recursively copy the contents between the tomb volume mounted inside the docker container and the bind-mounted directory from the host.
## Loop device mount ghost
If the docker container instance is shut down before the tomb volume is successfully closed then the the mounted volume and it's contents may remain attached and available from the host. Obviously any changes made to the contents at this point will not be persisted back to the tomb volume. The ghosted device mount and it's contents will disappear the host system is restarted or the node is manually cleaned up (which I don't know how to do properly), and you will be able to mount the real tomb volume again.
## Filesystem permissions
Since the docker container needs to run as root, anything it creates will have root permissions and may not be accessible from the host user without additional privileges.

121
extras/docker/tomb.sh Executable file
View File

@ -0,0 +1,121 @@
#!/bin/bash
set -e
function log_info(){
local msg=$1
printf "INFO: ${msg}\n"
}
function log_error(){
local msg=$1
printf "ERROR: ${msg}\n"
}
# Check & Validate Required ENV VARS
##
# TOMB_DOCKER_IMAGE
if [ -z "$TOMB_DOCKER_IMAGE" ]; then
log_error "ENV VAR: TOMB_DOCKER_IMAGE is required!"
exit 1
fi
if [ -z "$(docker images -q $TOMB_DOCKER_IMAGE)" ]; then
log_error "TOMB_DOCKER_IMAGE not found: $TOMB_DOCKER_IMAGE"
exit 1
fi
##
# TOMB_VOLUME
if [ -z "$TOMB_VOLUME" ]; then
log_error "ENV VAR: TOMB_VOLUME is required!"
exit 1
fi
if [ ! -f "$TOMB_VOLUME" ]; then
log_error "TOMB_VOLUME not found: $TOMB_VOLUME"
exit 1
fi
##
# TOMB_VOLUME_KEY
if [ -z "$TOMB_VOLUME_KEY" ]; then
log_error "ENV VAR: TOMB_VOLUME_KEY is required!"
exit 1
fi
if [ ! -f "$TOMB_VOLUME_KEY" ]; then
log_error "TOMB_VOLUME_KEY not found: $TOMB_VOLUME_KEY"
exit 1
fi
##
# TOMB_OUTPUT_DIR
if [ -z "$TOMB_OUTPUT_DIR" ]; then
log_error "ENV VAR: TOMB_OUTPUT_DIR is required!"
exit 1
fi
if [ ! -d "$TOMB_OUTPUT_DIR" ]; then
log_error "TOMB_OUTPUT_DIR not found: $TOMB_OUTPUT_DIR"
exit 1
fi
# Internal Vars
I_TOMB_VOLUME=/tomb/volume
I_TOMB_VOLUME_KEY=/tomb/volume_key
I_TOMB_OUTPUT_DIR=/tomb/output_dir # Important: This must NOT end in a slash!
I_TOMB_MOUNT_DIR=/tomb/mount_dir # Important: This must NOT end in a slash!
# Parse command
cmd=$1
if [ -z "$cmd" ]; then
log_error "A valid command is required! (eg: unpack, repack)"
exit 1
fi
# Parse flags (right now there is only one possible flag so I'm being lazy about parsing it)
flags=$2
force_opt=""
if [ "$flags" = "-f" ]; then
force_opt=$flags
fi
if [ $cmd = unpack ]; then
if [ ! -z "$(ls -A $TOMB_OUTPUT_DIR)" ]; then
log_error "Cannot unpack: TOMB_OUTPUT_DIR is not empty! $TOMB_OUTPUT_DIR"
exit 1
fi
docker run -it --rm --privileged \
--mount type=bind,source=${TOMB_VOLUME},target=${I_TOMB_VOLUME} \
--mount type=bind,source=${TOMB_VOLUME_KEY},target=${I_TOMB_VOLUME_KEY} \
--mount type=bind,source=${TOMB_OUTPUT_DIR},target=${I_TOMB_OUTPUT_DIR} \
tomb /bin/bash -c "set -e; tomb open ${I_TOMB_VOLUME} ${I_TOMB_MOUNT_DIR} -k ${I_TOMB_VOLUME_KEY} ${force_opt}; rsync -azh --delete ${I_TOMB_MOUNT_DIR}/ ${I_TOMB_OUTPUT_DIR}; tomb close"
elif [ $cmd = repack ]; then
if [ -z "$(ls -A $TOMB_OUTPUT_DIR)" ]; then
log_error "Cannot repack: TOMB_OUTPUT_DIR is empty! $TOMB_OUTPUT_DIR"
exit 1
fi
docker run -it --rm --privileged \
--mount type=bind,source=${TOMB_VOLUME},target=${I_TOMB_VOLUME} \
--mount type=bind,source=${TOMB_VOLUME_KEY},target=${I_TOMB_VOLUME_KEY} \
--mount type=bind,source=${TOMB_OUTPUT_DIR},target=${I_TOMB_OUTPUT_DIR} \
tomb /bin/bash -c "set -e; tomb open ${I_TOMB_VOLUME} ${I_TOMB_MOUNT_DIR} -k ${I_TOMB_VOLUME_KEY} ${force_opt}; rsync -azh --delete ${I_TOMB_OUTPUT_DIR}/ ${I_TOMB_MOUNT_DIR}; tomb close; rm -rf ${I_TOMB_OUTPUT_DIR}/..?* ${I_TOMB_OUTPUT_DIR}/* ${I_TOMB_OUTPUT_DIR}/.[!.]*"
elif [ $cmd = drop ]; then
if [ -z "$(ls -A $TOMB_OUTPUT_DIR)" ]; then
log_error "Cannot drop: TOMB_OUTPUT_DIR is empty! $TOMB_OUTPUT_DIR"
exit 1
fi
docker run -it --rm --privileged \
--mount type=bind,source=${TOMB_OUTPUT_DIR},target=${I_TOMB_OUTPUT_DIR} \
tomb /bin/bash -c "set -e; rm -rf ${I_TOMB_OUTPUT_DIR}/..?* ${I_TOMB_OUTPUT_DIR}/* ${I_TOMB_OUTPUT_DIR}/.[!.]*"
else
log_error "Invalid command: $cmd"
fi

View File

@ -4,6 +4,23 @@
If you like to see our nifty little skull on the upper right corner of
your desktop, then compile and install this little auxiliary program.
## Build
Make sure that Gcc, GNU Make, pkg-config, Gtk 2.0 and libnotify
development packages are installed: in Debian and Ubuntu the packages
are:
- gcc
- make
- pkg-config
- libgtk2.0-dev
- libnotify-dev
Then launch the `make` command which will build the `tomb-gtk-tray`
executable one can copy to the path (for instance `~/bin`).
## Usage
Use by launching `tomb-gtk-tray` followed by the name of your tomb as
reported by `tomb list`. For instance if your tomb is `secrets.tomb`:

View File

@ -77,7 +77,7 @@ int main(int argc, char **argv) {
}
snprintf(filename,255, "%s", argv[1]);
snprintf(mountpoint,255, "/media/%s.tomb", argv[1]);
snprintf(mountpoint,255, "/media/%s", argv[1]);
// libnotify
notify_init("Tomb");

View File

@ -1,11 +1,12 @@
gtomb - A GUI wrapper for Tomb, the crypto undertaker
Copyright (C) 2015 Parazyd <parazyd AT dyne DOT org>
Copyright (C) 2015-2016 Parazyd <parazyd@dyne.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

View File

@ -11,7 +11,7 @@ list and the script will run it for you. Easy-peasy.
### Random notes
* If you type in your sudo password once correctly, in the next 5 (or whatever your sudoers timeout is) minutes, you can type in the wrong password as well.
* The function for catching cancellation sometimes fails because of bad ps syntax. No idea why yet.
* The function for catching cancellation sometimes fails because of bad ps syntax. (Possibly fixed, needs more testing)
## Dependencies
* [tomb](https://github.com/dyne/Tomb) (also get tomb's dependencies)
@ -19,8 +19,8 @@ list and the script will run it for you. Easy-peasy.
## TODO
* Complete error checking
* Figure out why ps fails sometimes
* and more stuff
## What you need to do
* Be patient or help with coding :)
* Request features

File diff suppressed because it is too large Load Diff

11
extras/install_cloakify.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -ex
wget https://github.com/TryCatchHCF/Cloakify/archive/v1.0.3.tar.gz -O /tmp/cloakify.tar.gz
mkdir -p extras/cloakify
tar -xvf /tmp/cloakify.tar.gz --strip-components=1 -C $PWD/extras/cloakify
echo "#!/bin/sh
python2 $PWD/extras/cloakify/cloakify.py $@" > /usr/bin/cloakify
echo "#!/bin/sh
python2 $PWD/extras/cloakify/decloakify.py $@" > /usr/bin/decloakify
chmod +x /usr/bin/cloakify
chmod +x /usr/bin/decloakify

15
extras/install_sphinx.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
set -ex
git clone https://github.com/stef/libsphinx
cd libsphinx
git submodule update --init --recursive --remote
cd src
sed -i 's|/usr/local|/usr|' makefile
make && make install && ldconfig
cd ../..
git clone https://github.com/stef/pwdsphinx
cd pwdsphinx
python3 setup.py install
mkdir -p /etc/sphinx && cp ../test/sphinx.cfg /etc/sphinx/config && cd /etc/sphinx
openssl req -new -x509 -nodes -out server.crt -keyout server.key -subj '/CN=localhost'
sphinx init

View File

@ -2,10 +2,14 @@
PREFIX ?= /usr/local
all:
$(CC) -O2 -o tomb-kdb-pbkdf2 pbkdf2.c -lgcrypt
$(CC) -O2 -o tomb-kdb-pbkdf2-getiter benchmark.c -lgcrypt
$(CC) -O2 -o tomb-kdb-pbkdf2-gensalt gen_salt.c -lgcrypt
$(CC) -O2 -o tomb-kdb-hexencode hexencode.c
$(CC) -O2 $(CFLAGS) -o tomb-kdb-pbkdf2 pbkdf2.c -lgcrypt
$(CC) -O2 $(CFLAGS) -o tomb-kdb-pbkdf2-getiter benchmark.c -lgcrypt
$(CC) -O2 $(CFLAGS) -o tomb-kdb-pbkdf2-gensalt gen_salt.c -lgcrypt
$(CC) -O2 $(CFLAGS) -o tomb-kdb-hexencode hexencode.c
test:
@echo "Running Tomb-kdb tests"
./test.sh
clean:
rm -f tomb-kdb-pbkdf2 tomb-kdb-pbkdf2-getiter tomb-kdb-pbkdf2-gensalt tomb-kdb-hexencode

View File

@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
} else {
while( (read_bytes=fread(buf, sizeof(char), 2, stdin)) != 0) {
if(read_bytes == 1) buf[1]='\0';
sscanf(buf, "%x", &c);
sscanf(buf, "%s", &c);
printf("%c", c);
}
return 0;

View File

@ -28,6 +28,7 @@
*************
**
** Anthony Thyssen 4 November 2009 A.Thyssen@griffith.edu.au
** AitorATuin 3 February 2018 (whitespace password fix in Tomb)
**
** Based on a test program "pkcs5.c" found on
** http://www.mail-archive.com/openssl-users@openssl.org
@ -43,6 +44,9 @@
#include <gcrypt.h>
/* block size for password buffer */
#define BLOCK_SIZE 40
/* TODO: move print_hex and hex_to_binary to utils.h, with separate compiling */
void print_hex(unsigned char *buf, int len)
{
@ -73,15 +77,37 @@ int hex_to_binary(unsigned char *buf, char *hex)
return count;
}
void cleanup(char *result, int result_len, char *pass, char *salt, int salt_len) {
int i;
//clear and free everything
if (result) {
for(i=0; i<result_len;i++)
result[i]=0;
free(result);
}
if (pass) {
for(i=0; i<strlen(pass); i++) //blank
pass[i]=0;
free(pass);
}
if (salt) {
for(i=0; i<salt_len; i++) //blank
salt[i]=0;
free(salt);
}
}
int main(int argc, char *argv[])
{
char *pass = NULL;
unsigned char *salt;
unsigned char *salt = NULL;
int salt_len; // salt length in bytes
int ic=0; // iterative count
int result_len;
unsigned char *result; // result (binary - 32+16 chars)
int i;
unsigned char *result = NULL; // result (binary - 32+16 chars)
int i, pw_len = 0;
int buff_len = BLOCK_SIZE;
if ( argc != 4 ) {
fprintf(stderr, "usage: %s salt count len <passwd >binary_key_iv\n", argv[0]);
@ -89,7 +115,8 @@ int main(int argc, char *argv[])
}
//TODO: move to base64decode
salt=calloc(strlen(argv[1])/2+3, sizeof(char));
salt_len = strlen(argv[1])/2+3;
salt = calloc(salt_len, sizeof(char));
salt_len=hex_to_binary(salt, argv[1]);
if( salt_len <= 0 ) {
fprintf(stderr, "Error: %s is not a valid salt (it must be a hexadecimal string)\n", argv[1]);
@ -105,14 +132,41 @@ int main(int argc, char *argv[])
exit(1);
}
fscanf(stdin, "%ms", &pass);
if ( pass[strlen(pass)-1] == '\n' )
pass[strlen(pass)-1] = '\0';
/* Read password char by char.
*
* Doing in this way we make sure that blanks (even null bytes) end up
* in the password.
*
* passwords containing just a bunch of spaces are valid
*/
pass = calloc(buff_len, sizeof(char));
int c = getchar();
while (c != EOF) {
if (pw_len == buff_len) {
buff_len *= 2;
pass = realloc(pass, buff_len);
if (!pass) {
fprintf(stderr, "Error allocating memory\n");
cleanup(result, result_len, pass, salt, salt_len);
exit(3);
}
}
pass[pw_len] = (char)c;
pw_len++;
c = getchar();
}
if (pw_len <= 1) {
fprintf(stderr, "Error: password is empty\n");
cleanup(result, result_len, pass, salt, salt_len);
exit(1);
}
pass[pw_len-1] = '\0';
// PBKDF 2
result = calloc(result_len, sizeof(unsigned char*));
if (!gcry_check_version ("1.5.0")) {
fputs ("libgcrypt version mismatch\n", stderr);
cleanup(result, result_len, pass, salt, salt_len);
exit (2);
}
/* Allocate a pool of 16k secure memory. This make the secure memory
@ -124,19 +178,10 @@ int main(int argc, char *argv[])
/* Tell Libgcrypt that initialization has completed. */
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
gcry_kdf_derive( pass, strlen(pass), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result);
gcry_kdf_derive(pass, pw_len-1, GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result);
print_hex(result, result_len); // Key + IV (as hex string)
//clear and free everything
for(i=0; i<result_len;i++)
result[i]=0;
free(result);
for(i=0; i<strlen(pass); i++) //blank
pass[i]=0;
free(pass);
for(i=0; i<strlen(argv[1])/2+3; i++) //blank
salt[i]=0;
free(salt);
cleanup(result, result_len, pass, salt, salt_len);
return(0);
}

72
extras/kdf-keys/test.sh Normal file → Executable file
View File

@ -1,21 +1,65 @@
#!/usr/bin/env zsh
error=0
while read line; do
pass=`cut -f1 <<<$line`
salt=`cut -f2 <<<$line`
iter=`cut -f3 <<<$line`
keylen=`cut -f4 <<<$line`
expected=`cut -f5 <<<$line`
hexsalt=`cut -f6 <<<$line`
#TODO: check!
derived=`./pbkdf2 $hexsalt $iter $keylen <<<$pass`
if [[ $derived != $expected ]]; then
echo ./pbkdf2 $hexsalt $iter $keylen "<<<$pass"
echo "Expected $expected, got $derived" >&2
check_kdf() {
while read line; do
pass=`cut -f1 <<<$line`
salt=`cut -f2 <<<$line`
iter=`cut -f3 <<<$line`
keylen=`cut -f4 <<<$line`
expected=`cut -f5 <<<$line`
hexsalt=`cut -f6 <<<$line`
#TODO: check!
derived=`./tomb-kdb-pbkdf2 $hexsalt $iter $keylen <<<$pass`
if [[ $derived != $expected ]]; then
echo "Expected $expected, got $derived" >&2
error=$((error + 1))
fi
done < test.txt
}
check_white_spaces() {
hexsalt="73616c74"
iter=4096
keylen=20
typeset -a results
passwords=('one two three' 'one two' 'one')
for pwd in $passwords; do
results+=`./tomb-kdb-pbkdf2 $hexsalt $iter $keylen <<<$pwd`
done
for ((i=1;i<=3;i++)); do
d1=$results[$i]
i1=$passwords[$i]
for ((j=(($i+1));j<=3;j++)); do
d2=$results[$j]
i2=$passwords[$j]
if [[ $d1 == $d2 ]]; then
echo "Inputs \"$i1\" and \"$i2\" produce the same output $d1" >&2
error=$((error + 1))
fi
done
done
}
check_password_len() {
hexsalt="73616c74"
iter=4096
keylen=20
./tomb-kdb-pbkdf2 $hexsalt $iter $keylen 2>/dev/null <<<"" && {
echo "Empty passwords are accepted"
error=$((error + 1))
fi
done < test.txt
}
bigpassword=`perl -e 'print "a"x3000'`
./tomb-kdb-pbkdf2 $hexsalt $iter $keylen &>/dev/null <<<"$bigpassword" || {
echo "Error when using long password"
error=$((error + 1))
}
}
check_kdf
check_white_spaces
check_password_len
if [[ $error == 1 ]]; then
exit $error

View File

@ -0,0 +1,46 @@
.PHONY: test
system := $(shell uname -s)
# if [ ${system} == "FreeBSD" ]; then sudo kldload fusefs; fi
TOMB ?= test.tomb
#########
## BUILD
#########
test: create-open-close check-random-data
########
## TEST
########
create-open-close:
TOMB=${TOMB} ./test/bats/bin/bats ./test/create_open_close.bats
chmod a+r ${TOMB}
# arg: TOMB='path to tomb containing random.data'
# assumes $TOMB.hash is the pre-calculated hash on creation
check-random-data:
TOMB=${TOMB} ./test/bats/bin/bats test/check-random-data.bats
########
## LINT
########
shellcheck:
shellcheck -s sh tomb -e SC2006,SC2059,SC2034
########
## DEPS
########
download-deps-ubuntu22:
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu22-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
download-deps-ubuntu20:
curl https://files.dyne.org/tomb3/third-party/veracrypt-ubuntu20-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
curl https://files.dyne.org/zenroom/nightly/zenroom-linux-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
download-deps-freebsd13:
curl https://files.dyne.org/tomb3/third-party/veracrypt-freebsd13-amd64 -o /usr/local/bin/veracrypt && chmod +x /usr/local/bin/veracrypt
curl https://files.dyne.org/tomb3/third-party/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom
#curl https://files.dyne.org/zenroom/nightly/zenroom-freebsd13-amd64 -o /usr/local/bin/zenroom && chmod +x /usr/local/bin/zenroom

69
extras/portable/README.md Normal file
View File

@ -0,0 +1,69 @@
# Portable Tomb :: the crypto undertaker runs everywhere
[![continuous integration tests badge](https://github.com/dyne/tomb/actions/workflows/portable.yml/badge.svg)](https://github.com/dyne/Tomb/actions) test coverage status for portability
## ⚠️ WORK IN PROGRESS 🛠️
This is the portable version of [Tomb](https://github.com/dyne/tomb)
[![software by Dyne.org](https://files.dyne.org/software_by_dyne.png)](http://www.dyne.org)
# Purpose
Portable tomb achieves direct **interoperable access to tomb volumes** between:
- GNU base Linux (Ubuntu)
- Busybox based Linux (Alpine)
- MS/Windows using WSL2 (Ubuntu)
- FreeBSD (WIP using libluksde)
- ~~Apple/OSX using [MacFUSE](https://osxfuse.github.io/)~~
After some extensive testing during 2022, __adoption of Veracrypt in portable Tomb has been dropped__ because of unreliability and bad performance.
Portable tomb stays as an experimental branch that aims to reduce dependencies and in particular uses only the **POSIX sh interpreter**.
# Status
Portable tomb development is in progress and tracked via issues and the [portable milestone](https://github.com/dyne/Tomb/milestone/9).
## Features
The following features will be implemented where possible:
- mount bind (Linux only)
- ps / slam
- resize (pending investigation)
- index & search ([recoll based](https://github.com/dyne/Tomb/issues/211))
- bury / exhume
## Dependencies
- FreeBSD: `fusefs-libs3 fusefs-lkl e2fsprogs util-linux libluksde`
- Linux: `fuse3 util-linux`
- crossplatform [Veracrypt binaries](https://files.dyne.org/tomb3/third-party) console-only
## Note on Veracrypt
The way upstream developers distribute Veracrypt is far from meeting our minimalist needs, but the console-only binary once installed has a few library dependencies and is all what we need.
I setup [my own build](https://github.com/jaromil/veracrypt) and provide binary builds of Veracrypt v1.25.9 for download on Tomb's file repository for testing purposes.
# Disclaimer
Tomb is Copyright (C) 2007-2023 by the Dyne.org Foundation and
developed by [Jaromil](https://github.com/jaromil).
This source code is free software; you can redistribute it and/or
modify it under the terms of the GNU Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
to the GNU Public License for more details.
You should have received a copy of the GNU Public License along with
this source code; if not, write to: Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA.

View File

@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
if command -v greadlink >/dev/null; then
bats_readlinkf() {
greadlink -f "$1"
}
else
bats_readlinkf() {
readlink -f "$1"
}
fi
fallback_to_readlinkf_posix() {
bats_readlinkf() {
[ "${1:-}" ] || return 1
max_symlinks=40
CDPATH='' # to avoid changing to an unexpected directory
target=$1
[ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
[ -d "${target:-/}" ] && target="$target/"
cd -P . 2>/dev/null || return 1
while [ "$max_symlinks" -ge 0 ] && max_symlinks=$((max_symlinks - 1)); do
if [ ! "$target" = "${target%/*}" ]; then
case $target in
/*) cd -P "${target%/*}/" 2>/dev/null || break ;;
*) cd -P "./${target%/*}" 2>/dev/null || break ;;
esac
target=${target##*/}
fi
if [ ! -L "$target" ]; then
target="${PWD%/}${target:+/}${target}"
printf '%s\n' "${target:-/}"
return 0
fi
# `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
# <file mode>, <number of links>, <owner name>, <group name>,
# <size>, <date and time>, <pathname of link>, <contents of link>
# https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
link=$(ls -dl -- "$target" 2>/dev/null) || break
target=${link#*" $target -> "}
done
return 1
}
}
if ! BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}" 2>/dev/null); then
fallback_to_readlinkf_posix
BATS_PATH=$(bats_readlinkf "${BASH_SOURCE[0]}")
fi
export BATS_ROOT=${BATS_PATH%/*/*}
export -f bats_readlinkf
exec env BATS_ROOT="$BATS_ROOT" "$BATS_ROOT/libexec/bats-core/bats" "$@"

View File

@ -0,0 +1,98 @@
#!/usr/bin/env bash
bats_prefix_lines_for_tap_output() {
while IFS= read -r line; do
printf '# %s\n' "$line" || break # avoid feedback loop when errors are redirected into BATS_OUT (see #353)
done
if [[ -n "$line" ]]; then
printf '# %s\n' "$line"
fi
}
function bats_replace_filename() {
local line
while read -r line; do
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
done
if [[ -n "$line" ]]; then
printf "%s\n" "${line//$BATS_TEST_SOURCE/$BATS_TEST_FILENAME}"
fi
}
bats_quote_code() { # <var> <code>
printf -v "$1" -- "%s%s%s" "$BATS_BEGIN_CODE_QUOTE" "$2" "$BATS_END_CODE_QUOTE"
}
bats_check_valid_version() {
if [[ ! $1 =~ [0-9]+.[0-9]+.[0-9]+ ]]; then
printf "ERROR: version '%s' must be of format <major>.<minor>.<patch>!\n" "$1" >&2
exit 1
fi
}
# compares two versions. Return 0 when version1 < version2
bats_version_lt() { # <version1> <version2>
bats_check_valid_version "$1"
bats_check_valid_version "$2"
local -a version1_parts version2_parts
IFS=. read -ra version1_parts <<< "$1"
IFS=. read -ra version2_parts <<< "$2"
for i in {0..2}; do
if (( version1_parts[i] < version2_parts[i] )); then
return 0
elif (( version1_parts[i] > version2_parts[i] )); then
return 1
fi
done
# if we made it this far, they are equal -> also not less then
return 2 # use other failing return code to distinguish equal from gt
}
# ensure a minimum version of bats is running or exit with failure
bats_require_minimum_version() { # <required version>
local required_minimum_version=$1
if bats_version_lt "$BATS_VERSION" "$required_minimum_version"; then
printf "BATS_VERSION=%s does not meet required minimum %s\n" "$BATS_VERSION" "$required_minimum_version"
exit 1
fi
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$required_minimum_version"; then
BATS_GUARANTEED_MINIMUM_VERSION="$required_minimum_version"
fi
}
bats_binary_search() { # <search-value> <array-name>
if [[ $# -ne 2 ]]; then
printf "ERROR: bats_binary_search requires exactly 2 arguments: <search value> <array name>\n" >&2
return 2
fi
local -r search_value=$1 array_name=$2
# we'd like to test if array is set but we cannot distinguish unset from empty arrays, so we need to skip that
local start=0 mid end mid_value
# start is inclusive, end is exclusive ...
eval "end=\${#${array_name}[@]}"
# so start == end means empty search space
while (( start < end )); do
mid=$(( (start + end) / 2 ))
eval "mid_value=\${${array_name}[$mid]}"
if [[ "$mid_value" == "$search_value" ]]; then
return 0
elif [[ "$mid_value" < "$search_value" ]]; then
# This branch excludes equality -> +1 to skip the mid element.
# This +1 also avoids endless recursion on odd sized search ranges.
start=$((mid + 1))
else
end=$mid
fi
done
# did not find it -> its not there
return 1
}

View File

@ -0,0 +1,116 @@
#!/usr/bin/env bash
# reads (extended) bats tap streams from stdin and calls callback functions for each line
# bats_tap_stream_plan <number of tests> -> when the test plan is encountered
# bats_tap_stream_begin <test index> <test name> -> when a new test is begun WARNING: extended only
# bats_tap_stream_ok [--duration <milliseconds] <test index> <test name> -> when a test was successful
# bats_tap_stream_not_ok [--duration <milliseconds>] <test index> <test name> -> when a test has failed
# bats_tap_stream_skipped <test index> <test name> <skip reason> -> when a test was skipped
# bats_tap_stream_comment <comment text without leading '# '> <scope> -> when a comment line was encountered,
# scope tells the last encountered of plan, begin, ok, not_ok, skipped, suite
# bats_tap_stream_suite <file name> -> when a new file is begun WARNING: extended only
# bats_tap_stream_unknown <full line> <scope> -> when a line is encountered that does not match the previous entries,
# scope @see bats_tap_stream_comment
# forwards all input as is, when there is no TAP test plan header
function bats_parse_internal_extended_tap() {
local header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
if [[ "$header" =~ $header_pattern ]]; then
bats_tap_stream_plan "${header:3}"
else
# If the first line isn't a TAP plan, print it and pass the rest through
printf '%s\n' "$header"
exec cat
fi
ok_line_regexpr="ok ([0-9]+) (.*)"
skip_line_regexpr="ok ([0-9]+) (.*) # skip( (.*))?$"
not_ok_line_regexpr="not ok ([0-9]+) (.*)"
timing_expr="in ([0-9]+)ms$"
local test_name begin_index ok_index not_ok_index index scope
begin_index=0
index=0
scope=plan
while IFS= read -r line; do
case "$line" in
'begin '*) # this might only be called in extended tap output
((++begin_index))
scope=begin
test_name="${line#* "$begin_index" }"
bats_tap_stream_begin "$begin_index" "$test_name"
;;
'ok '*)
((++index))
if [[ "$line" =~ $ok_line_regexpr ]]; then
ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
if [[ "$line" =~ $skip_line_regexpr ]]; then
scope=skipped
test_name="${BASH_REMATCH[2]}" # cut off name before "# skip"
local skip_reason="${BASH_REMATCH[4]}"
bats_tap_stream_skipped "$ok_index" "$test_name" "$skip_reason"
else
scope=ok
if [[ "$line" =~ $timing_expr ]]; then
bats_tap_stream_ok --duration "${BASH_REMATCH[1]}" "$ok_index" "$test_name"
else
bats_tap_stream_ok "$ok_index" "$test_name"
fi
fi
else
printf "ERROR: could not match ok line: %s" "$line" >&2
exit 1
fi
;;
'not ok '*)
((++index))
scope=not_ok
if [[ "$line" =~ $not_ok_line_regexpr ]]; then
not_ok_index="${BASH_REMATCH[1]}"
test_name="${BASH_REMATCH[2]}"
if [[ "$line" =~ $timing_expr ]]; then
bats_tap_stream_not_ok --duration "${BASH_REMATCH[1]}" "$not_ok_index" "$test_name"
else
bats_tap_stream_not_ok "$not_ok_index" "$test_name"
fi
else
printf "ERROR: could not match not ok line: %s" "$line" >&2
exit 1
fi
;;
'# '*)
bats_tap_stream_comment "${line:2}" "$scope"
;;
'#')
bats_tap_stream_comment "" "$scope"
;;
'suite '*)
scope=suite
# pass on the
bats_tap_stream_suite "${line:6}"
;;
*)
bats_tap_stream_unknown "$line" "$scope"
;;
esac
done
}
normalize_base_path() { # <target variable> <base path>
# the relative path root to use for reporting filenames
# this is mainly intended for suite mode, where this will be the suite root folder
local base_path="$2"
# use the containing directory when --base-path is a file
if [[ ! -d "$base_path" ]]; then
base_path="$(dirname "$base_path")"
fi
# get the absolute path
base_path="$(cd "$base_path" && pwd)"
# ensure the path ends with / to strip that later on
if [[ "${base_path}" != *"/" ]]; then
base_path="$base_path/"
fi
printf -v "$1" "%s" "$base_path"
}

View File

@ -0,0 +1,22 @@
#!/usr/bin/env bash
BATS_TMPNAME="$BATS_RUN_TMPDIR/bats.$$"
BATS_PARENT_TMPNAME="$BATS_RUN_TMPDIR/bats.$PPID"
# shellcheck disable=SC2034
BATS_OUT="${BATS_TMPNAME}.out" # used in bats-exec-file
bats_preprocess_source() {
# export to make it visible to bats_evaluate_preprocessed_source
# since the latter runs in bats-exec-test's bash while this runs in bats-exec-file's
export BATS_TEST_SOURCE="${BATS_TMPNAME}.src"
bats-preprocess "$BATS_TEST_FILENAME" >"$BATS_TEST_SOURCE"
}
bats_evaluate_preprocessed_source() {
if [[ -z "${BATS_TEST_SOURCE:-}" ]]; then
BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src"
fi
# Dynamically loaded user files provided outside of Bats.
# shellcheck disable=SC1090
source "$BATS_TEST_SOURCE"
}

View File

@ -0,0 +1,107 @@
#!/usr/bin/env bash
# setup the semaphore environment for the loading file
bats_semaphore_setup() {
export -f bats_semaphore_get_free_slot_count
export -f bats_semaphore_acquire_while_locked
export BATS_SEMAPHORE_DIR="$BATS_RUN_TMPDIR/semaphores"
if command -v flock >/dev/null; then
bats_run_under_lock() {
flock "$BATS_SEMAPHORE_DIR" "$@"
}
elif command -v shlock >/dev/null; then
bats_run_under_lock() {
local lockfile="$BATS_SEMAPHORE_DIR/shlock.lock"
while ! shlock -p $$ -f "$lockfile"; do
sleep 1
done
# we got the lock now, execute the command
"$@"
local status=$?
# free the lock
rm -f "$lockfile"
return $status
}
else
printf "ERROR: flock/shlock is required for parallelization within files!\n" >&2
exit 1
fi
}
# $1 - output directory for stdout/stderr
# $@ - command to run
# run the given command in a semaphore
# block when there is no free slot for the semaphore
# when there is a free slot, run the command in background
# gather the output of the command in files in the given directory
bats_semaphore_run() {
local output_dir=$1
shift
local semaphore_slot
semaphore_slot=$(bats_semaphore_acquire_slot)
bats_semaphore_release_wrapper "$output_dir" "$semaphore_slot" "$@" &
printf "%d\n" "$!"
}
# $1 - output directory for stdout/stderr
# $@ - command to run
# this wraps the actual function call to install some traps on exiting
bats_semaphore_release_wrapper() {
local output_dir="$1"
local semaphore_name="$2"
shift 2 # all other parameters will be use for the command to execute
# shellcheck disable=SC2064 # we want to expand the semaphore_name right now!
trap "status=$?; bats_semaphore_release_slot '$semaphore_name'; exit $status" EXIT
mkdir -p "$output_dir"
"$@" 2>"$output_dir/stderr" >"$output_dir/stdout"
local status=$?
# bash bug: the exit trap is not called for the background process
bats_semaphore_release_slot "$semaphore_name"
trap - EXIT # avoid calling release twice
return $status
}
bats_semaphore_acquire_while_locked() {
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
local slot=0
while [[ -e "$BATS_SEMAPHORE_DIR/slot-$slot" ]]; do
(( ++slot ))
done
if [[ $slot -lt $BATS_SEMAPHORE_NUMBER_OF_SLOTS ]]; then
touch "$BATS_SEMAPHORE_DIR/slot-$slot" && printf "%d\n" "$slot" && return 0
fi
fi
return 1
}
# block until a semaphore slot becomes free
# prints the number of the slot that it received
bats_semaphore_acquire_slot() {
mkdir -p "$BATS_SEMAPHORE_DIR"
# wait for a slot to become free
# TODO: avoid busy waiting by using signals -> this opens op prioritizing possibilities as well
while true; do
# don't lock for reading, we are fine with spuriously getting no free slot
if [[ $(bats_semaphore_get_free_slot_count) -gt 0 ]]; then
bats_run_under_lock bash -c bats_semaphore_acquire_while_locked && break
fi
sleep 1
done
}
bats_semaphore_release_slot() {
# we don't need to lock this, since only our process owns this file
# and freeing a semaphore cannot lead to conflicts with others
rm "$BATS_SEMAPHORE_DIR/slot-$1" # this will fail if we had not acquired a semaphore!
}
bats_semaphore_get_free_slot_count() {
# find might error out without returning something useful when a file is deleted,
# while the directory is traversed -> only continue when there was no error
until used_slots=$(find "$BATS_SEMAPHORE_DIR" -name 'slot-*' 2>/dev/null | wc -l); do :; done
echo $(( BATS_SEMAPHORE_NUMBER_OF_SLOTS - used_slots ))
}

View File

@ -0,0 +1,357 @@
#!/usr/bin/env bash
BATS_TEST_DIRNAME="${BATS_TEST_FILENAME%/*}"
BATS_TEST_NAMES=()
# shellcheck source=lib/bats-core/warnings.bash
source "$BATS_ROOT/lib/bats-core/warnings.bash"
# find_in_bats_lib_path echoes the first recognized load path to
# a library in BATS_LIB_PATH or relative to BATS_TEST_DIRNAME.
#
# Libraries relative to BATS_TEST_DIRNAME take precedence over
# BATS_LIB_PATH.
#
# Library load paths are recognized using find_library_load_path.
#
# If no library is found find_in_bats_lib_path returns 1.
find_in_bats_lib_path() { # <return-var> <library-name>
local return_var="${1:?}"
local library_name="${2:?}"
local -a bats_lib_paths
IFS=: read -ra bats_lib_paths <<< "$BATS_LIB_PATH"
for path in "${bats_lib_paths[@]}"; do
if [[ -f "$path/$library_name" ]]; then
printf -v "$return_var" "%s" "$path/$library_name"
# A library load path was found, return
return 0
elif [[ -f "$path/$library_name/load.bash" ]]; then
printf -v "$return_var" "%s" "$path/$library_name/load.bash"
# A library load path was found, return
return 0
fi
done
return 1
}
# bats_internal_load expects an absolute path that is a library load path.
#
# If the library load path points to a file (a library loader) it is
# sourced.
#
# If it points to a directory all files ending in .bash inside of the
# directory are sourced.
#
# If the sourcing of the library loader or of a file in a library
# directory fails bats_internal_load prints an error message and returns 1.
#
# If the passed library load path is not absolute or is not a valid file
# or directory bats_internal_load prints an error message and returns 1.
bats_internal_load() {
local library_load_path="${1:?}"
if [[ "${library_load_path:0:1}" != / ]]; then
printf "Passed library load path is not an absolute path: %s\n" "$library_load_path" >&2
return 1
fi
# library_load_path is a library loader
if [[ -f "$library_load_path" ]]; then
# shellcheck disable=SC1090
if ! source "$library_load_path"; then
printf "Error while sourcing library loader at '%s'\n" "$library_load_path" >&2
return 1
fi
return 0
fi
printf "Passed library load path is neither a library loader nor library directory: %s\n" "$library_load_path" >&2
return 1
}
# bats_load_safe accepts an argument called 'slug' and attempts to find and
# source a library based on the slug.
#
# A slug can be an absolute path, a library name or a relative path.
#
# If the slug is an absolute path bats_load_safe attempts to find the library
# load path using find_library_load_path.
# What is considered a library load path is documented in the
# documentation for find_library_load_path.
#
# If the slug is not an absolute path it is considered a library name or
# relative path. bats_load_safe attempts to find the library load path using
# find_in_bats_lib_path.
#
# If bats_load_safe can find a library load path it is passed to bats_internal_load.
# If bats_internal_load fails bats_load_safe returns 1.
#
# If no library load path can be found bats_load_safe prints an error message
# and returns 1.
bats_load_safe() {
local slug="${1:?}"
if [[ ${slug:0:1} != / ]]; then # relative paths are relative to BATS_TEST_DIRNAME
slug="$BATS_TEST_DIRNAME/$slug"
fi
if [[ -f "$slug.bash" ]]; then
bats_internal_load "$slug.bash"
return $?
elif [[ -f "$slug" ]]; then
bats_internal_load "$slug"
return $?
fi
# loading from PATH (retained for backwards compatibility)
if [[ ! -f "$1" ]] && type -P "$1" >/dev/null; then
# shellcheck disable=SC1090
source "$1"
return $?
fi
# No library load path can be found
printf "bats_load_safe: Could not find '%s'[.bash]\n" "$slug" >&2
return 1
}
bats_require_lib_path() {
if [[ -z "${BATS_LIB_PATH:-}" ]]; then
printf "%s: requires BATS_LIB_PATH to be set!\n" "${FUNCNAME[1]}" >&2
exit 1
fi
}
bats_load_library_safe() { # <slug>
local slug="${1:?}" library_path
bats_require_lib_path
# Check for library load paths in BATS_TEST_DIRNAME and BATS_LIB_PATH
if [[ ${slug:0:1} != / ]]; then
find_in_bats_lib_path library_path "$slug"
if [[ -z "$library_path" ]]; then
printf "Could not find library '%s' relative to test file or in BATS_LIB_PATH\n" "$slug" >&2
return 1
fi
else
# absolute paths are taken as is
library_path="$slug"
if [[ ! -f "$library_path" ]]; then
printf "Could not find library on absolute path '%s'\n" "$library_path" >&2
return 1
fi
fi
bats_internal_load "$library_path"
return $?
}
# immediately exit on error, use bats_load_library_safe to catch and handle errors
bats_load_library() { # <slug>
bats_require_lib_path
if ! bats_load_library_safe "$@"; then
exit 1
fi
}
# load acts like bats_load_safe but exits the shell instead of returning 1.
load() {
if ! bats_load_safe "$@"; then
echo "${FUNCNAME[0]} $LINENO" >&3
exit 1
fi
}
bats_redirect_stderr_into_file() {
"$@" 2>>"$bats_run_separate_stderr_file" # use >> to see collisions' content
}
bats_merge_stdout_and_stderr() {
"$@" 2>&1
}
# write separate lines from <input-var> into <output-array>
bats_separate_lines() { # <output-array> <input-var>
local output_array_name="$1"
local input_var_name="$2"
if [[ $keep_empty_lines ]]; then
local bats_separate_lines_lines=()
if [[ -n "${!input_var_name}" ]]; then # avoid getting an empty line for empty input
while IFS= read -r line; do
bats_separate_lines_lines+=("$line")
done <<<"${!input_var_name}"
fi
eval "${output_array_name}=(\"\${bats_separate_lines_lines[@]}\")"
else
# shellcheck disable=SC2034,SC2206
IFS=$'\n' read -d '' -r -a "$output_array_name" <<<"${!input_var_name}" || true # don't fail due to EOF
fi
}
run() { # [!|-N] [--keep-empty-lines] [--separate-stderr] [--] <command to run...>
# This has to be restored on exit from this function to avoid leaking our trap INT into surrounding code.
# Non zero exits won't restore under the assumption that they will fail the test before it can be aborted,
# which allows us to avoid duplicating the restore code on every exit path
trap bats_interrupt_trap_in_run INT
local expected_rc=
local keep_empty_lines=
local output_case=merged
local has_flags=
# parse options starting with -
while [[ $# -gt 0 ]] && [[ $1 == -* || $1 == '!' ]]; do
has_flags=1
case "$1" in
'!')
expected_rc=-1
;;
-[0-9]*)
expected_rc=${1#-}
if [[ $expected_rc =~ [^0-9] ]]; then
printf "Usage error: run: '-NNN' requires numeric NNN (got: %s)\n" "$expected_rc" >&2
return 1
elif [[ $expected_rc -gt 255 ]]; then
printf "Usage error: run: '-NNN': NNN must be <= 255 (got: %d)\n" "$expected_rc" >&2
return 1
fi
;;
--keep-empty-lines)
keep_empty_lines=1
;;
--separate-stderr)
output_case="separate"
;;
--)
shift # eat the -- before breaking away
break
;;
*)
printf "Usage error: unknown flag '%s'" "$1" >&2
return 1
;;
esac
shift
done
if [[ -n $has_flags ]]; then
bats_warn_minimum_guaranteed_version "Using flags on \`run\`" 1.5.0
fi
local pre_command=
case "$output_case" in
merged) # redirects stderr into stdout and fills only $output/$lines
pre_command=bats_merge_stdout_and_stderr
;;
separate) # splits stderr into own file and fills $stderr/$stderr_lines too
local bats_run_separate_stderr_file
bats_run_separate_stderr_file="$(mktemp "${BATS_TEST_TMPDIR}/separate-stderr-XXXXXX")"
pre_command=bats_redirect_stderr_into_file
;;
esac
local origFlags="$-"
set +eET
local origIFS="$IFS"
if [[ $keep_empty_lines ]]; then
# 'output', 'status', 'lines' are global variables available to tests.
# preserve trailing newlines by appending . and removing it later
# shellcheck disable=SC2034
output="$($pre_command "$@"; status=$?; printf .; exit $status)" && status=0 || status=$?
output="${output%.}"
else
# 'output', 'status', 'lines' are global variables available to tests.
# shellcheck disable=SC2034
output="$($pre_command "$@")" && status=0 || status=$?
fi
bats_separate_lines lines output
if [[ "$output_case" == separate ]]; then
# shellcheck disable=SC2034
read -d '' -r stderr < "$bats_run_separate_stderr_file"
bats_separate_lines stderr_lines stderr
fi
# shellcheck disable=SC2034
BATS_RUN_COMMAND="${*}"
IFS="$origIFS"
set "-$origFlags"
if [[ ${BATS_VERBOSE_RUN:-} ]]; then
printf "%s\n" "$output"
fi
if [[ -n "$expected_rc" ]]; then
if [[ "$expected_rc" = "-1" ]]; then
if [[ "$status" -eq 0 ]]; then
BATS_ERROR_SUFFIX=", expected nonzero exit code!"
return 1
fi
elif [ "$status" -ne "$expected_rc" ]; then
# shellcheck disable=SC2034
BATS_ERROR_SUFFIX=", expected exit code $expected_rc, got $status"
return 1
fi
elif [[ "$status" -eq 127 ]]; then # "command not found"
bats_generate_warning 1 "$BATS_RUN_COMMAND"
fi
# don't leak our trap into surrounding code
trap bats_interrupt_trap INT
}
setup() {
return 0
}
teardown() {
return 0
}
skip() {
# if this is a skip in teardown ...
if [[ -n "${BATS_TEARDOWN_STARTED-}" ]]; then
# ... we want to skip the rest of teardown.
# communicate to bats_exit_trap that the teardown was completed without error
# shellcheck disable=SC2034
BATS_TEARDOWN_COMPLETED=1
# if we are already in the exit trap (e.g. due to previous skip) ...
if [[ "$BATS_TEARDOWN_STARTED" == as-exit-trap ]]; then
# ... we need to do the rest of the tear_down_trap that would otherwise be skipped after the next call to exit
bats_exit_trap
# and then do the exit (at the end of this function)
fi
# if we aren't in exit trap, the normal exit handling should suffice
else
# ... this is either skip in test or skip in setup.
# Following variables are used in bats-exec-test which sources this file
# shellcheck disable=SC2034
BATS_TEST_SKIPPED="${1:-1}"
# shellcheck disable=SC2034
BATS_TEST_COMPLETED=1
fi
exit 0
}
bats_test_begin() {
BATS_TEST_DESCRIPTION="$1"
if [[ -n "$BATS_EXTENDED_SYNTAX" ]]; then
printf 'begin %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}$BATS_TEST_DESCRIPTION" >&3
fi
setup
}
bats_test_function() {
local test_name="$1"
BATS_TEST_NAMES+=("$test_name")
}
# decides whether a failed test should be run again
bats_should_retry_test() {
# test try number starts at 1
# 0 retries means run only first try
(( BATS_TEST_TRY_NUMBER <= BATS_TEST_RETRIES ))
}

View File

@ -0,0 +1,386 @@
#!/usr/bin/env bash
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_capture_stack_trace() {
local test_file
local funcname
local i
BATS_DEBUG_LAST_STACK_TRACE=()
for ((i = 2; i != ${#FUNCNAME[@]}; ++i)); do
# Use BATS_TEST_SOURCE if necessary to work around Bash < 4.4 bug whereby
# calling an exported function erases the test file's BASH_SOURCE entry.
test_file="${BASH_SOURCE[$i]:-$BATS_TEST_SOURCE}"
funcname="${FUNCNAME[$i]}"
BATS_DEBUG_LAST_STACK_TRACE+=("${BASH_LINENO[$((i-1))]} $funcname $test_file")
case "$funcname" in
"$BATS_TEST_NAME" | setup | teardown | setup_file | teardown_file | setup_suite | teardown_suite)
break
;;
esac
if [[ "${BASH_SOURCE[$i + 1]:-}" == *"bats-exec-file" ]] && [[ "$funcname" == 'source' ]]; then
break
fi
done
}
bats_get_failure_stack_trace() {
local stack_trace_var
# See bats_debug_trap for details.
if [[ -n "${BATS_DEBUG_LAST_STACK_TRACE_IS_VALID:-}" ]]; then
stack_trace_var=BATS_DEBUG_LAST_STACK_TRACE
else
stack_trace_var=BATS_DEBUG_LASTLAST_STACK_TRACE
fi
# shellcheck disable=SC2016
eval "$(printf \
'%s=(${%s[@]+"${%s[@]}"})' \
"${1}" \
"${stack_trace_var}" \
"${stack_trace_var}")"
}
bats_print_stack_trace() {
local frame
local index=1
local count="${#@}"
local filename
local lineno
for frame in "$@"; do
bats_frame_filename "$frame" 'filename'
bats_trim_filename "$filename" 'filename'
bats_frame_lineno "$frame" 'lineno'
printf '%s' "${BATS_STACK_TRACE_PREFIX-# }"
if [[ $index -eq 1 ]]; then
printf '('
else
printf ' '
fi
local fn
bats_frame_function "$frame" 'fn'
if [[ "$fn" != "$BATS_TEST_NAME" ]] &&
# don't print "from function `source'"",
# when failing in free code during `source $test_file` from bats-exec-file
! [[ "$fn" == 'source' && $index -eq $count ]]; then
local quoted_fn
bats_quote_code quoted_fn "$fn"
printf "from function %s " "$quoted_fn"
fi
if [[ $index -eq $count ]]; then
printf 'in test file %s, line %d)\n' "$filename" "$lineno"
else
printf 'in file %s, line %d,\n' "$filename" "$lineno"
fi
((++index))
done
}
bats_print_failed_command() {
local stack_trace=("${@}")
if [[ ${#stack_trace[@]} -eq 0 ]]; then
return
fi
local frame="${stack_trace[${#stack_trace[@]} - 1]}"
local filename
local lineno
local failed_line
local failed_command
bats_frame_filename "$frame" 'filename'
bats_frame_lineno "$frame" 'lineno'
bats_extract_line "$filename" "$lineno" 'failed_line'
bats_strip_string "$failed_line" 'failed_command'
local quoted_failed_command
bats_quote_code quoted_failed_command "$failed_command"
printf '# %s ' "${quoted_failed_command}"
if [[ "$BATS_ERROR_STATUS" -eq 1 ]]; then
printf 'failed%s\n' "$BATS_ERROR_SUFFIX"
else
printf 'failed with status %d%s\n' "$BATS_ERROR_STATUS" "$BATS_ERROR_SUFFIX"
fi
}
bats_frame_lineno() {
printf -v "$2" '%s' "${1%% *}"
}
bats_frame_function() {
local __bff_function="${1#* }"
printf -v "$2" '%s' "${__bff_function%% *}"
}
bats_frame_filename() {
local __bff_filename="${1#* }"
__bff_filename="${__bff_filename#* }"
if [[ "$__bff_filename" == "$BATS_TEST_SOURCE" ]]; then
__bff_filename="$BATS_TEST_FILENAME"
fi
printf -v "$2" '%s' "$__bff_filename"
}
bats_extract_line() {
local __bats_extract_line_line
local __bats_extract_line_index=0
while IFS= read -r __bats_extract_line_line; do
if [[ "$((++__bats_extract_line_index))" -eq "$2" ]]; then
printf -v "$3" '%s' "${__bats_extract_line_line%$'\r'}"
break
fi
done <"$1"
}
bats_strip_string() {
[[ "$1" =~ ^[[:space:]]*(.*)[[:space:]]*$ ]]
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
}
bats_trim_filename() {
printf -v "$2" '%s' "${1#"$BATS_CWD"/}"
}
# normalize a windows path from e.g. C:/directory to /c/directory
# The path must point to an existing/accessable directory, not a file!
bats_normalize_windows_dir_path() { # <output-var> <path>
local output_var="$1" path="$2"
if [[ "$output_var" != NORMALIZED_INPUT ]]; then
local NORMALIZED_INPUT
fi
if [[ $path == ?:* ]]; then
NORMALIZED_INPUT="$(cd "$path" || exit 1; pwd)"
else
NORMALIZED_INPUT="$path"
fi
printf -v "$output_var" "%s" "$NORMALIZED_INPUT"
}
bats_emit_trace() {
if [[ $BATS_TRACE_LEVEL -gt 0 ]]; then
local line=${BASH_LINENO[1]}
# shellcheck disable=SC2016
if [[ $BASH_COMMAND != '"$BATS_TEST_NAME" >> "$BATS_OUT" 2>&1 4>&1' && $BASH_COMMAND != "bats_test_begin "* ]] && # don't emit these internal calls
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || $line != "$BATS_LAST_BASH_LINENO" ]] &&
# avoid printing a function twice (at call site and at definition site)
[[ $BASH_COMMAND != "$BATS_LAST_BASH_COMMAND" || ${BASH_LINENO[2]} != "$BATS_LAST_BASH_LINENO" || ${BASH_SOURCE[3]} != "$BATS_LAST_BASH_SOURCE" ]]; then
local file="${BASH_SOURCE[2]}" # index 2: skip over bats_emit_trace and bats_debug_trap
if [[ $file == "${BATS_TEST_SOURCE}" ]]; then
file="$BATS_TEST_FILENAME"
fi
local padding='$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$'
if (( BATS_LAST_STACK_DEPTH != ${#BASH_LINENO[@]} )); then
printf '%s [%s:%d]\n' "${padding::${#BASH_LINENO[@]}-4}" "${file##*/}" "$line" >&4
fi
printf '%s %s\n' "${padding::${#BASH_LINENO[@]}-4}" "$BASH_COMMAND" >&4
BATS_LAST_BASH_COMMAND="$BASH_COMMAND"
BATS_LAST_BASH_LINENO="$line"
BATS_LAST_BASH_SOURCE="${BASH_SOURCE[2]}"
BATS_LAST_STACK_DEPTH="${#BASH_LINENO[@]}"
fi
fi
}
# bats_debug_trap tracks the last line of code executed within a test. This is
# necessary because $BASH_LINENO is often incorrect inside of ERR and EXIT
# trap handlers.
#
# Below are tables describing different command failure scenarios and the
# reliability of $BASH_LINENO within different the executed DEBUG, ERR, and EXIT
# trap handlers. Naturally, the behaviors change between versions of Bash.
#
# Table rows should be read left to right. For example, on bash version
# 4.0.44(2)-release, if a test executes `false` (or any other failing external
# command), bash will do the following in order:
# 1. Call the DEBUG trap handler (bats_debug_trap) with $BASH_LINENO referring
# to the source line containing the `false` command, then
# 2. Call the DEBUG trap handler again, but with an incorrect $BASH_LINENO, then
# 3. Call the ERR trap handler, but with a (possibly-different) incorrect
# $BASH_LINENO, then
# 4. Call the DEBUG trap handler again, but with $BASH_LINENO set to 1, then
# 5. Call the EXIT trap handler, with $BASH_LINENO set to 1.
#
# bash version 4.4.20(1)-release
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
# -------------+-------------+--------------+---------+-------------+--------
# false | OK | OK | OK | BAD[1] | BAD[1]
# [[ 1 = 2 ]] | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
# (( 1 = 2 )) | OK | BAD[2] | BAD[2] | BAD[1] | BAD[1]
# ! true | OK | --- | BAD[4] | --- | BAD[1]
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
#
# bash version 4.0.44(2)-release
# command | first DEBUG | second DEBUG | ERR | third DEBUG | EXIT
# -------------+-------------+--------------+---------+-------------+--------
# false | OK | BAD[3] | BAD[3] | BAD[1] | BAD[1]
# [[ 1 = 2 ]] | OK | --- | BAD[3] | --- | BAD[1]
# (( 1 = 2 )) | OK | --- | BAD[3] | --- | BAD[1]
# ! true | OK | --- | BAD[3] | --- | BAD[1]
# $var_dne | OK | --- | --- | BAD[1] | BAD[1]
# source /dne | OK | --- | --- | BAD[1] | BAD[1]
#
# [1] The reported line number is always 1.
# [2] The reported source location is that of the beginning of the function
# calling the command.
# [3] The reported line is that of the last command executed in the DEBUG trap
# handler.
# [4] The reported source location is that of the call to the function calling
# the command.
bats_debug_trap() {
# on windows we sometimes get a mix of paths (when install via nmp install -g)
# which have C:/... or /c/... comparing them is going to be problematic.
# We need to normalize them to a common format!
local NORMALIZED_INPUT
bats_normalize_windows_dir_path NORMALIZED_INPUT "${1%/*}"
local file_excluded='' path
for path in "${BATS_DEBUG_EXCLUDE_PATHS[@]}"; do
if [[ "$NORMALIZED_INPUT" == "$path"* ]]; then
file_excluded=1
break
fi
done
# don't update the trace within library functions or we get backtraces from inside traps
# also don't record new stack traces while handling interruptions, to avoid overriding the interrupted command
if [[ -z "$file_excluded" && "${BATS_INTERRUPTED-NOTSET}" == NOTSET ]]; then
BATS_DEBUG_LASTLAST_STACK_TRACE=(
${BATS_DEBUG_LAST_STACK_TRACE[@]+"${BATS_DEBUG_LAST_STACK_TRACE[@]}"}
)
BATS_DEBUG_LAST_LINENO=(${BASH_LINENO[@]+"${BASH_LINENO[@]}"})
BATS_DEBUG_LAST_SOURCE=(${BASH_SOURCE[@]+"${BASH_SOURCE[@]}"})
bats_capture_stack_trace
bats_emit_trace
fi
}
# For some versions of Bash, the `ERR` trap may not always fire for every
# command failure, but the `EXIT` trap will. Also, some command failures may not
# set `$?` properly. See #72 and #81 for details.
#
# For this reason, we call `bats_check_status_from_trap` at the very beginning
# of `bats_teardown_trap` and check the value of `$BATS_TEST_COMPLETED` before
# taking other actions. We also adjust the exit status value if needed.
#
# See `bats_exit_trap` for an additional EXIT error handling case when `$?`
# isn't set properly during `teardown()` errors.
bats_check_status_from_trap() {
local status="$?"
if [[ -z "${BATS_TEST_COMPLETED:-}" ]]; then
BATS_ERROR_STATUS="${BATS_ERROR_STATUS:-$status}"
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
BATS_ERROR_STATUS=1
fi
trap - DEBUG
fi
}
bats_add_debug_exclude_path() { # <path>
if [[ -z "$1" ]]; then # don't exclude everything
printf "bats_add_debug_exclude_path: Exclude path must not be empty!\n" >&2
return 1
fi
if [[ "$OSTYPE" == cygwin || "$OSTYPE" == msys ]]; then
local normalized_dir
bats_normalize_windows_dir_path normalized_dir "$1"
BATS_DEBUG_EXCLUDE_PATHS+=("$normalized_dir")
else
BATS_DEBUG_EXCLUDE_PATHS+=("$1")
fi
}
bats_setup_tracing() {
# Variables for capturing accurate stack traces. See bats_debug_trap for
# details.
#
# BATS_DEBUG_LAST_LINENO, BATS_DEBUG_LAST_SOURCE, and
# BATS_DEBUG_LAST_STACK_TRACE hold data from the most recent call to
# bats_debug_trap.
#
# BATS_DEBUG_LASTLAST_STACK_TRACE holds data from two bats_debug_trap calls
# ago.
#
# BATS_DEBUG_LAST_STACK_TRACE_IS_VALID indicates that
# BATS_DEBUG_LAST_STACK_TRACE contains the stack trace of the test's error. If
# unset, BATS_DEBUG_LAST_STACK_TRACE is unreliable and
# BATS_DEBUG_LASTLAST_STACK_TRACE should be used instead.
BATS_DEBUG_LASTLAST_STACK_TRACE=()
BATS_DEBUG_LAST_LINENO=()
BATS_DEBUG_LAST_SOURCE=()
BATS_DEBUG_LAST_STACK_TRACE=()
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=
BATS_ERROR_SUFFIX=
BATS_DEBUG_EXCLUDE_PATHS=()
# exclude some paths by default
bats_add_debug_exclude_path "$BATS_ROOT/lib/"
bats_add_debug_exclude_path "$BATS_ROOT/libexec/"
exec 4<&1 # used for tracing
if [[ "${BATS_TRACE_LEVEL:-0}" -gt 0 ]]; then
# avoid undefined variable errors
BATS_LAST_BASH_COMMAND=
BATS_LAST_BASH_LINENO=
BATS_LAST_BASH_SOURCE=
BATS_LAST_STACK_DEPTH=
# try to exclude helper libraries if found, this is only relevant for tracing
while read -r path; do
bats_add_debug_exclude_path "$path"
done < <(find "$PWD" -type d -name bats-assert -o -name bats-support)
fi
local exclude_paths path
# exclude user defined libraries
IFS=':' read -r exclude_paths <<< "${BATS_DEBUG_EXCLUDE_PATHS:-}"
for path in "${exclude_paths[@]}"; do
if [[ -n "$path" ]]; then
bats_add_debug_exclude_path "$path"
fi
done
# turn on traps after setting excludes to avoid tracing the exclude setup
trap 'bats_debug_trap "$BASH_SOURCE"' DEBUG
trap 'bats_error_trap' ERR
}
bats_error_trap() {
bats_check_status_from_trap
# If necessary, undo the most recent stack trace captured by bats_debug_trap.
# See bats_debug_trap for details.
if [[ "${BASH_LINENO[*]}" = "${BATS_DEBUG_LAST_LINENO[*]:-}"
&& "${BASH_SOURCE[*]}" = "${BATS_DEBUG_LAST_SOURCE[*]:-}"
&& -z "$BATS_DEBUG_LAST_STACK_TRACE_IS_VALID" ]]; then
BATS_DEBUG_LAST_STACK_TRACE=(
${BATS_DEBUG_LASTLAST_STACK_TRACE[@]+"${BATS_DEBUG_LASTLAST_STACK_TRACE[@]}"}
)
fi
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
}
bats_interrupt_trap() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
BATS_ERROR_STATUS=130
# debug trap fires before interrupt trap but gets wrong linenumber (line 1)
# -> use last stack trace
exit $BATS_ERROR_STATUS
}
# this is used inside run()
bats_interrupt_trap_in_run() {
# mark the interruption, to handle during exit
BATS_INTERRUPTED=true
BATS_ERROR_STATUS=130
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=true
exit $BATS_ERROR_STATUS
}

View File

@ -0,0 +1,37 @@
#!/usr/bin/env bash
bats_test_count_validator() {
trap '' INT # continue forwarding
header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
# repeat the header
printf "%s\n" "$header"
# if we detect a TAP plan
if [[ "$header" =~ $header_pattern ]]; then
# extract the number of tests ...
local expected_number_of_tests="${header:3}"
# ... count the actual number of [not ] oks...
local actual_number_of_tests=0
while IFS= read -r line; do
# forward line
printf "%s\n" "$line"
case "$line" in
'ok '*)
(( ++actual_number_of_tests ))
;;
'not ok'*)
(( ++actual_number_of_tests ))
;;
esac
done
# ... and error if they are not the same
if [[ "${actual_number_of_tests}" != "${expected_number_of_tests}" ]]; then
printf '# bats warning: Executed %s instead of expected %s tests\n' "$actual_number_of_tests" "$expected_number_of_tests"
return 1
fi
else
# forward output unchanged
cat
fi
}

View File

@ -0,0 +1,35 @@
#!/usr/bin/env bash
# shellcheck source=lib/bats-core/tracing.bash
source "$BATS_ROOT/lib/bats-core/tracing.bash"
BATS_WARNING_SHORT_DESCS=(
# to start with 1
'PADDING'
# see issue #578 for context
"\`run\`'s command \`%s\` exited with code 127, indicating 'Command not found'. Use run's return code checks, e.g. \`run -127\`, to fix this message."
"%s requires at least BATS_VERSION=%s. Use \`bats_require_minimum_version %s\` to fix this message."
)
# generate a warning report for the parent call's call site
bats_generate_warning() { # <warning number> [<printf args for warning string>...]
local warning_number="$1" padding="00"
shift
if [[ $warning_number =~ [0-9]+ ]] && ((warning_number < ${#BATS_WARNING_SHORT_DESCS[@]} )); then
{
printf "BW%s: ${BATS_WARNING_SHORT_DESCS[$warning_number]}\n" "${padding:${#warning_number}}${warning_number}" "$@"
bats_capture_stack_trace
BATS_STACK_TRACE_PREFIX=' ' bats_print_stack_trace "${BATS_DEBUG_LAST_STACK_TRACE[@]}"
} >> "$BATS_WARNING_FILE" 2>&3
else
printf "Invalid Bats warning number '%s'. It must be an integer between 1 and %d." "$warning_number" "$((${#BATS_WARNING_SHORT_DESCS[@]} - 1))" >&2
exit 1
fi
}
# generate a warning if the BATS_GUARANTEED_MINIMUM_VERSION is not high enough
bats_warn_minimum_guaranteed_version() { # <feature> <minimum required version>
if bats_version_lt "$BATS_GUARANTEED_MINIMUM_VERSION" "$2"; then
bats_generate_warning 2 "$1" "$2" "$2"
fi
}

View File

@ -0,0 +1,467 @@
#!/usr/bin/env bash
set -e
export BATS_VERSION='1.7.0'
VALID_FORMATTERS="pretty, junit, tap, tap13"
version() {
printf 'Bats %s\n' "$BATS_VERSION"
}
abort() {
local print_usage=1
if [[ ${1:-} == --no-print-usage ]]; then
print_usage=
shift
fi
printf 'Error: %s\n' "$1" >&2
if [[ -n $print_usage ]]; then
usage >&2
fi
exit 1
}
usage() {
local cmd="${0##*/}"
local line
cat <<HELP_TEXT_HEADER
Usage: ${cmd} [OPTIONS] <tests>
${cmd} [-h | -v]
HELP_TEXT_HEADER
cat <<'HELP_TEXT_BODY'
<tests> is the path to a Bats test file, or the path to a directory
containing Bats test files (ending with ".bats")
-c, --count Count test cases without running any tests
--code-quote-style <style>
A two character string of code quote delimiters
or 'custom' which requires setting $BATS_BEGIN_CODE_QUOTE and
$BATS_END_CODE_QUOTE. Can also be set via $BATS_CODE_QUOTE_STYLE
-f, --filter <regex> Only run tests that match the regular expression
--filter-status <status> Only run tests with the given status in the last completed (no CTRL+C/SIGINT) run.
Valid <status> values are:
failed - runs tests that failed or were not present in the last run
missed - runs tests that were not present in the last run
-F, --formatter <type> Switch between formatters: pretty (default),
tap (default w/o term), tap13, junit, /<absolute path to formatter>
--gather-test-outputs-in <directory>
Gather the output of failing *and* passing tests
as files in directory (if existing, must be empty)
-h, --help Display this help message
-j, --jobs <jobs> Number of parallel jobs (requires GNU parallel)
--no-tempdir-cleanup Preserve test output temporary directory
--no-parallelize-across-files
Serialize test file execution instead of running
them in parallel (requires --jobs >1)
--no-parallelize-within-files
Serialize test execution within files instead of
running them in parallel (requires --jobs >1)
--report-formatter <type> Switch between reporters (same options as --formatter)
-o, --output <dir> Directory to write report files (must exist)
-p, --pretty Shorthand for "--formatter pretty"
--print-output-on-failure Automatically print the value of `$output` on failed tests
-r, --recursive Include tests in subdirectories
--show-output-of-passing-tests
Print output of passing tests
-t, --tap Shorthand for "--formatter tap"
-T, --timing Add timing information to tests
-x, --trace Print test commands as they are executed (like `set -x`)
--verbose-run Make `run` print `$output` by default
-v, --version Display the version number
For more information, see https://github.com/bats-core/bats-core
HELP_TEXT_BODY
}
expand_path() {
local path="${1%/}"
local dirname="${path%/*}"
local result="$2"
if [[ "$dirname" == "$path" ]]; then
dirname="$PWD"
else
cd "$dirname"
dirname="$PWD"
cd "$OLDPWD"
fi
printf -v "$result" '%s/%s' "$dirname" "${path##*/}"
}
BATS_LIBEXEC="$(cd "$(dirname "$(bats_readlinkf "${BASH_SOURCE[0]}")")"; pwd)"
export BATS_LIBEXEC
export BATS_CWD="$PWD"
export BATS_TEST_FILTER=
export PATH="$BATS_LIBEXEC:$PATH"
export BATS_ROOT_PID=$$
export BATS_TMPDIR="${TMPDIR:-/tmp}"
BATS_TMPDIR=${BATS_TMPDIR%/} # chop off trailing / to avoid duplication
export BATS_RUN_TMPDIR=
export BATS_GUARANTEED_MINIMUM_VERSION=0.0.0
if [[ ! -d "${BATS_TMPDIR}" ]];then
printf "Error: BATS_TMPDIR (%s) does not exist or is not a directory" "${BATS_TMPDIR}" >&2
exit 1
elif [[ ! -w "${BATS_TMPDIR}" ]];then
printf "Error: BATS_TMPDIR (%s) is not writable" "${BATS_TMPDIR}" >&2
exit 1
fi
arguments=()
# Unpack single-character options bundled together, e.g. -cr, -pr.
for arg in "$@"; do
if [[ "$arg" =~ ^-[^-]. ]]; then
index=1
while option="${arg:$((index++)):1}"; do
if [[ -z "$option" ]]; then
break
fi
arguments+=("-$option")
done
else
arguments+=("$arg")
fi
shift
done
set -- "${arguments[@]}"
arguments=()
unset flags recursive formatter_flags
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
formatter_flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
formatter='tap'
report_formatter=''
recursive=
setup_suite_file=''
export BATS_TEMPDIR_CLEANUP=1
output=
if [[ -z "${CI:-}" && -t 0 && -t 1 ]] && command -v tput >/dev/null; then
formatter='pretty'
fi
while [[ "$#" -ne 0 ]]; do
case "$1" in
-h | --help)
version
usage
exit 0
;;
-v | --version)
version
exit 0
;;
-c | --count)
flags+=('-c')
;;
-f | --filter)
shift
flags+=('-f' "$1")
;;
-F | --formatter)
shift
# allow cat formatter to see extended output but don't advertise to users
if [[ $1 =~ ^(pretty|junit|tap|tap13|cat|/.*)$ ]]; then
formatter="$1"
else
printf "Unknown formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
exit 1
fi
;;
--report-formatter)
shift
if [[ $1 =~ ^(pretty|junit|tap|tap13)$ ]]; then
report_formatter="$1"
else
printf "Unknown report formatter '%s', valid options are %s\n" "$1" "${VALID_FORMATTERS}"
exit 1
fi
;;
-o | --output)
shift
output="$1"
;;
-p | --pretty)
formatter='pretty'
;;
-j | --jobs)
shift
flags+=('-j' "$1")
;;
-r | --recursive)
recursive=1
;;
-t | --tap)
formatter='tap'
;;
-T | --timing)
flags+=('-T')
formatter_flags+=('-T')
;;
# this flag is now a no-op, as it is the parallel default
--parallel-preserve-environment)
;;
--no-parallelize-across-files)
flags+=("--no-parallelize-across-files")
;;
--no-parallelize-within-files)
flags+=("--no-parallelize-within-files")
;;
--no-tempdir-cleanup)
BATS_TEMPDIR_CLEANUP=''
;;
--tempdir) # for internal test consumption only!
BATS_RUN_TMPDIR="$2"
shift
;;
-x | --trace)
flags+=(--trace)
;;
--print-output-on-failure)
flags+=(--print-output-on-failure)
;;
--show-output-of-passing-tests)
flags+=(--show-output-of-passing-tests)
;;
--verbose-run)
flags+=(--verbose-run)
;;
--gather-test-outputs-in)
shift
output_dir="$1"
if [ -d "$output_dir" ]; then
if ! find "$output_dir" -mindepth 1 -exec false {} + 2>/dev/null; then
abort --no-print-usage "Directory '$output_dir' must be empty for --gather-test-outputs-in"
fi
elif ! mkdir "$output_dir" 2>/dev/null; then
abort --no-print-usage "Could not create '$output_dir' for --gather-test-outputs-in"
fi
flags+=(--gather-test-outputs-in "$output_dir")
;;
--setup-suite-file)
shift
setup_suite_file="$1"
;;
--code-quote-style)
shift
BATS_CODE_QUOTE_STYLE="$1"
;;
--filter-status)
shift
flags+=('--filter-status' "$1")
;;
-*)
abort "Bad command line option '$1'"
;;
*)
arguments+=("$1")
;;
esac
shift
done
if [[ -n "${BATS_RUN_TMPDIR:-}" ]];then
if [[ -d "$BATS_RUN_TMPDIR" ]]; then
printf "Error: BATS_RUN_TMPDIR (%s) already exists\n" "$BATS_RUN_TMPDIR" >&2
printf "Reusing old run directories can lead to unexpected results ... aborting!\n" >&2
exit 1
elif ! mkdir -p "$BATS_RUN_TMPDIR" ;then
printf "Error: Failed to create BATS_RUN_TMPDIR (%s)\n" "$BATS_RUN_TMPDIR" >&2
exit 1
fi
elif ! BATS_RUN_TMPDIR=$(mktemp -d "${BATS_TMPDIR}/bats-run-XXXXXX");then
printf "Error: Failed to create BATS_RUN_TMPDIR (%s) with mktemp\n" "${BATS_TMPDIR}/bats-run-XXXXXX" >&2
exit 1
fi
export BATS_WARNING_FILE="${BATS_RUN_TMPDIR}/warnings.log"
bats_exit_trap() {
if [[ -s "$BATS_WARNING_FILE" ]]; then
local pre_cat='' post_cat=''
if [[ $formatter == pretty ]]; then
pre_cat=$'\x1B[31m'
post_cat=$'\x1B[0m'
fi
printf "\nThe following warnings were encountered during tests:\n%s" "$pre_cat"
cat "$BATS_WARNING_FILE"
printf "%s" "$post_cat"
fi >&2
if [[ -n "$BATS_TEMPDIR_CLEANUP" ]]; then
rm -rf "$BATS_RUN_TMPDIR"
else
printf "BATS_RUN_TMPDIR: %s\n" "$BATS_RUN_TMPDIR" >&2
fi
}
trap bats_exit_trap EXIT
if [[ "$formatter" != "tap" ]]; then
flags+=('-x')
fi
if [[ -n "$report_formatter" && "$report_formatter" != "tap" ]]; then
flags+=('-x')
fi
if [[ "$formatter" == "junit" ]]; then
flags+=('-T')
formatter_flags+=('--base-path' "${arguments[0]}")
fi
if [[ "$report_formatter" == "junit" ]]; then
flags+=('-T')
report_formatter_flags+=('--base-path' "${arguments[0]}")
fi
if [[ "$formatter" == "pretty" ]]; then
formatter_flags+=('--base-path' "${arguments[0]}")
fi
# if we don't need to filter extended syntax, use the faster formatter
if [[ "$formatter" == tap && -z "$report_formatter" ]]; then
formatter="cat"
fi
bats_check_formatter() { # <formatter-path>
local -r formatter="$1"
if [[ ! -f "$formatter" ]]; then
printf "ERROR: Formatter '%s' is not readable!\n" "$formatter"
exit 1
elif [[ ! -x "$formatter" ]]; then
printf "ERROR: Formatter '%s' is not executable!\n" "$formatter"
exit 1
fi
}
if [[ $formatter == /* ]]; then # absolute paths are direct references to formatters
bats_check_formatter "$formatter"
interpolated_formatter="$formatter"
else
interpolated_formatter="bats-format-${formatter}"
fi
if [[ "${#arguments[@]}" -eq 0 ]]; then
abort 'Must specify at least one <test>'
fi
if [[ -n "$report_formatter" ]]; then
# default to the current directory for output
if [[ -z "$output" ]]; then
output=.
fi
# only set BATS_REPORT_FILENAME if none was given
if [[ -z "${BATS_REPORT_FILENAME:-}" ]]; then
case "$report_formatter" in
tap|tap13)
BATS_REPORT_FILE_NAME="report.tap"
;;
junit)
BATS_REPORT_FILE_NAME="report.xml"
;;
*)
BATS_REPORT_FILE_NAME="report.log"
;;
esac
fi
fi
if [[ $report_formatter == /* ]]; then # absolute paths are direct references to formatters
bats_check_formatter "$report_formatter"
interpolated_report_formatter="${report_formatter}"
else
interpolated_report_formatter="bats-format-${report_formatter}"
fi
if [[ "${BATS_CODE_QUOTE_STYLE-BATS_CODE_QUOTE_STYLE_UNSET}" == BATS_CODE_QUOTE_STYLE_UNSET ]]; then
BATS_CODE_QUOTE_STYLE="\`'"
fi
case "${BATS_CODE_QUOTE_STYLE}" in
??)
BATS_BEGIN_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE::1}"
BATS_END_CODE_QUOTE="${BATS_CODE_QUOTE_STYLE:1:1}"
export BATS_BEGIN_CODE_QUOTE BATS_END_CODE_QUOTE
;;
custom)
if [[ ${BATS_BEGIN_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET
|| ${BATS_END_CODE_QUOTE-BATS_BEGIN_CODE_QUOTE_UNSET} == BATS_BEGIN_CODE_QUOTE_UNSET ]]; then
printf "ERROR: BATS_CODE_QUOTE_STYLE=custom requires BATS_BEGIN_CODE_QUOTE and BATS_END_CODE_QUOTE to be set\n" >&2
exit 1
fi
;;
*)
printf "ERROR: Unknown BATS_CODE_QUOTE_STYLE: %s\n" "$BATS_CODE_QUOTE_STYLE" >&2
exit 1
;;
esac
if [[ -n "$output" ]]; then
if [[ ! -w "${output}" ]]; then
abort "Output path ${output} is not writeable"
fi
export BATS_REPORT_OUTPUT_PATH="$output"
fi
if [[ -n "$setup_suite_file" && ! -f "$setup_suite_file" ]]; then
abort "--setup-suite-file $setup_suite_file does not exist!"
fi
filenames=()
for filename in "${arguments[@]}"; do
expand_path "$filename" 'filename'
if [[ -z "$setup_suite_file" ]]; then
if [[ -d "$filename" ]]; then
dirname="$filename"
else
dirname="${filename%/*}"
fi
potential_setup_suite_file="$dirname/setup_suite.bash"
if [[ -e "$potential_setup_suite_file" ]]; then
setup_suite_file="$potential_setup_suite_file"
fi
fi
if [[ -d "$filename" ]]; then
shopt -s nullglob
if [[ "$recursive" -eq 1 ]]; then
while IFS= read -r -d $'\0' file; do
filenames+=("$file")
done < <(find -L "$filename" -type f -name "*.${BATS_FILE_EXTENSION:-bats}" -print0 | sort -z)
else
for suite_filename in "$filename"/*."${BATS_FILE_EXTENSION:-bats}"; do
filenames+=("$suite_filename")
done
fi
shopt -u nullglob
else
filenames+=("$filename")
fi
done
if [[ -n "$setup_suite_file" ]]; then
flags+=("--setup-suite-file" "$setup_suite_file")
fi
# shellcheck source=lib/bats-core/validator.bash
source "$BATS_ROOT/lib/bats-core/validator.bash"
trap 'BATS_INTERRUPTED=true' INT # let the lower levels handle the interruption
set -o pipefail execfail
if [[ -n "$report_formatter" ]]; then
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
tee >("$interpolated_report_formatter" "${report_formatter_flags[@]}" >"${BATS_REPORT_OUTPUT_PATH}/${BATS_REPORT_FILE_NAME}") | \
bats_test_count_validator | \
"$interpolated_formatter" "${formatter_flags[@]}"
else
exec bats-exec-suite "${flags[@]}" "${filenames[@]}" | \
bats_test_count_validator | \
"$interpolated_formatter" "${formatter_flags[@]}"
fi

View File

@ -0,0 +1,338 @@
#!/usr/bin/env bash
set -eET
flags=('--dummy-flag')
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
extended_syntax=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
declare -r BATS_RETRY_RETURN_CODE=126
export BATS_TEST_RETRIES=0 # no retries by default
while [[ "$#" -ne 0 ]]; do
case "$1" in
-j)
shift
num_jobs="$1"
;;
-T)
flags+=('-T')
;;
-x)
flags+=('-x')
extended_syntax=1
;;
--no-parallelize-within-files)
# use singular to allow for users to override in file
BATS_NO_PARALLELIZE_WITHIN_FILE=1
;;
--dummy-flag)
;;
--trace)
flags+=('--trace')
;;
--print-output-on-failure)
flags+=(--print-output-on-failure)
;;
--show-output-of-passing-tests)
flags+=(--show-output-of-passing-tests)
;;
--verbose-run)
flags+=(--verbose-run)
;;
--gather-test-outputs-in)
shift
flags+=(--gather-test-outputs-in "$1")
;;
*)
break
;;
esac
shift
done
filename="$1"
TESTS_FILE="$2"
if [[ ! -f "$filename" ]]; then
printf 'Testfile "%s" not found\n' "$filename" >&2
exit 1
fi
export BATS_TEST_FILENAME="$filename"
# shellcheck source=lib/bats-core/preprocessing.bash
# shellcheck disable=SC2153
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
bats_run_setup_file() {
# shellcheck source=lib/bats-core/tracing.bash
# shellcheck disable=SC2153
source "$BATS_ROOT/lib/bats-core/tracing.bash"
# shellcheck source=lib/bats-core/test_functions.bash
# shellcheck disable=SC2153
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
exec 3<&1
# these are defined only to avoid errors when referencing undefined variables down the line
# shellcheck disable=2034
BATS_TEST_NAME= # used in tracing.bash
# shellcheck disable=2034
BATS_TEST_COMPLETED= # used in tracing.bash
BATS_SOURCE_FILE_COMPLETED=
BATS_SETUP_FILE_COMPLETED=
BATS_TEARDOWN_FILE_COMPLETED=
# shellcheck disable=2034
BATS_ERROR_STATUS= # used in tracing.bash
touch "$BATS_OUT"
bats_setup_tracing
trap 'bats_file_teardown_trap' EXIT
local status=0
# get the setup_file/teardown_file functions for this file (if it has them)
# shellcheck disable=SC1090
source "$BATS_TEST_SOURCE" >>"$BATS_OUT" 2>&1
BATS_SOURCE_FILE_COMPLETED=1
setup_file >>"$BATS_OUT" 2>&1
BATS_SETUP_FILE_COMPLETED=1
}
bats_run_teardown_file() {
# avoid running the therdown trap due to errors in teardown_file
trap 'bats_file_exit_trap' EXIT
# rely on bats_error_trap to catch failures
teardown_file >>"$BATS_OUT" 2>&1
BATS_TEARDOWN_FILE_COMPLETED=1
}
bats_file_teardown_trap() {
bats_run_teardown_file
bats_file_exit_trap
}
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_file_exit_trap() {
trap - ERR EXIT
local failure_reason
local -i failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + 1 ))
if [[ -z "$BATS_SETUP_FILE_COMPLETED" || -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
if [[ -z "$BATS_SETUP_FILE_COMPLETED" ]]; then
failure_reason='setup_file'
elif [[ -z "$BATS_TEARDOWN_FILE_COMPLETED" ]]; then
failure_reason='teardown_file'
failure_test_index=$(( BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE + ${#tests_to_run[@]} + 1 ))
elif [[ -z "$BATS_SOURCE_FILE_COMPLETED" ]]; then
failure_reason='source'
else
failure_reason='unknown internal'
fi
printf "not ok %d %s\n" "$failure_test_index" "$failure_reason failed" >&3
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}" >&3
bats_print_failed_command "${stack_trace[@]}" >&3
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
rm -rf "$BATS_OUT"
bats_exec_file_status=1
fi
exit $bats_exec_file_status
}
function setup_file() {
return 0
}
function teardown_file() {
return 0
}
bats_forward_output_of_parallel_test() {
local test_number_in_suite=$1
local status=0
wait "$(cat "$output_folder/$test_number_in_suite/pid")" || status=1
cat "$output_folder/$test_number_in_suite/stdout"
cat "$output_folder/$test_number_in_suite/stderr" >&2
return $status
}
bats_is_next_parallel_test_finished() {
local PID
# get the pid of the next potentially finished test
PID=$(cat "$output_folder/$(( test_number_in_suite_of_last_finished_test + 1 ))/pid")
# try to send a signal to this process
# if it fails, the process exited,
# if it succeeds, the process is still running
if kill -0 "$PID" 2>/dev/null; then
return 1
fi
}
# prints output from all tests in the order they were started
# $1 == "blocking": wait for a test to finish before printing
# != "blocking": abort printing, when a test has not finished
bats_forward_output_for_parallel_tests() {
local status=0
# was the next test already started?
while (( test_number_in_suite_of_last_finished_test + 1 <= test_number_in_suite )); do
# if we are okay with waiting or if the test has already been finished
if [[ "$1" == "blocking" ]] || bats_is_next_parallel_test_finished ; then
(( ++test_number_in_suite_of_last_finished_test ))
bats_forward_output_of_parallel_test "$test_number_in_suite_of_last_finished_test" || status=$?
else
# non-blocking and the process has not finished -> abort the printing
break
fi
done
return $status
}
bats_run_test_with_retries() { # <args>
local status=0
local should_try_again=1 try_number
for ((try_number=1; should_try_again; ++try_number)); do
if "$BATS_LIBEXEC/bats-exec-test" "$@" "$try_number"; then
should_try_again=0
else
status=$?
if ((status == BATS_RETRY_RETURN_CODE)); then
should_try_again=1
else
should_try_again=0
bats_exec_file_status=$status
fi
fi
done
return $status
}
bats_run_tests_in_parallel() {
local output_folder="$BATS_RUN_TMPDIR/parallel_output"
local status=0
mkdir -p "$output_folder"
# shellcheck source=lib/bats-core/semaphore.bash
source "$BATS_ROOT/lib/bats-core/semaphore.bash"
bats_semaphore_setup
# the test_number_in_file is not yet incremented -> one before the next test to run
local test_number_in_suite_of_last_finished_test="$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE" # stores which test was printed last
local test_number_in_file=0 test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE
for test_name in "${tests_to_run[@]}"; do
# Only handle non-empty lines
if [[ $test_name ]]; then
((++test_number_in_suite))
((++test_number_in_file))
mkdir -p "$output_folder/$test_number_in_suite"
bats_semaphore_run "$output_folder/$test_number_in_suite" \
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" "$test_number_in_suite" "$test_number_in_file" \
> "$output_folder/$test_number_in_suite/pid"
fi
# print results early to get interactive feedback
bats_forward_output_for_parallel_tests non-blocking || status=1 # ignore if we did not finish yet
done
bats_forward_output_for_parallel_tests blocking || status=1
return $status
}
bats_read_tests_list_file() {
local line_number=0
tests_to_run=()
# the global test number must be visible to traps -> not local
local test_number_in_suite=''
while read -r test_line; do
# check if the line begins with filename
# filename might contain some hard to parse characters,
# use simple string operations to work around that issue
if [[ "$filename" == "${test_line::${#filename}}" ]]; then
# get the rest of the line without the separator \t
test_name=${test_line:$((1 + ${#filename} ))}
tests_to_run+=("$test_name")
# save the first test's number for later iteration
# this assumes that tests for a file are stored consecutive in the file!
if [[ -z "$test_number_in_suite" ]]; then
test_number_in_suite=$line_number
fi
fi
((++line_number))
done <"$TESTS_FILE"
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE="$test_number_in_suite"
declare -ri BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE # mark readonly (cannot merge assignment, because value would be lost)
}
bats_run_tests() {
bats_exec_file_status=0
if [[ "$num_jobs" != 1 && "${BATS_NO_PARALLELIZE_WITHIN_FILE-False}" == False ]]; then
export BATS_SEMAPHORE_NUMBER_OF_SLOTS="$num_jobs"
bats_run_tests_in_parallel "$BATS_RUN_TMPDIR/parallel_output" || bats_exec_file_status=1
else
local test_number_in_suite=$BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE \
test_number_in_file=0
for test_name in "${tests_to_run[@]}"; do
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
bats_exec_file_status=130 # bash's code for SIGINT exits
break
fi
# Only handle non-empty lines
if [[ $test_name ]]; then
((++test_number_in_suite))
((++test_number_in_file))
bats_run_test_with_retries "${flags[@]}" "$filename" "$test_name" \
"$test_number_in_suite" "$test_number_in_file" || bats_exec_file_status=$?
fi
done
fi
}
bats_create_file_tempdirs() {
local bats_files_tmpdir="${BATS_RUN_TMPDIR}/file"
if ! mkdir -p "$bats_files_tmpdir"; then
printf 'Failed to create %s\n' "$bats_files_tmpdir" >&2
exit 1
fi
BATS_FILE_TMPDIR="$bats_files_tmpdir/${BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE?}"
if ! mkdir "$BATS_FILE_TMPDIR"; then
printf 'Failed to create BATS_FILE_TMPDIR=%s\n' "$BATS_FILE_TMPDIR" >&2
exit 1
fi
ln -s "$BATS_TEST_FILENAME" "$BATS_FILE_TMPDIR-$(basename "$BATS_TEST_FILENAME").source_file"
export BATS_FILE_TMPDIR
}
trap 'BATS_INTERRUPTED=true' INT
if [[ -n "$extended_syntax" ]]; then
printf "suite %s\n" "$filename"
fi
BATS_FILE_FIRST_TEST_NUMBER_IN_SUITE=0 # predeclare as Bash 3.2 does not support declare -g
bats_read_tests_list_file
# don't run potentially expensive setup/teardown_file
# when there are no tests to run
if [[ ${#tests_to_run[@]} -eq 0 ]]; then
exit 0
fi
# requires the test list to be read but not empty
bats_create_file_tempdirs
bats_preprocess_source "$filename"
trap bats_interrupt_trap INT
bats_run_setup_file
# during tests, we don't want to get backtraces from this level
# just wait for the test to be interrupted and display their trace
trap 'BATS_INTERRUPTED=true' INT
bats_run_tests
trap bats_interrupt_trap INT
bats_run_teardown_file
exit $bats_exec_file_status

View File

@ -0,0 +1,359 @@
#!/usr/bin/env bash
set -e
count_only_flag=''
filter=''
num_jobs=${BATS_NUMBER_OF_PARALLEL_JOBS:-1}
bats_no_parallelize_across_files=${BATS_NO_PARALLELIZE_ACROSS_FILES-}
bats_no_parallelize_within_files=
filter_status=''
flags=('--dummy-flag') # add a dummy flag to prevent unset variable errors on empty array expansion in old bash versions
setup_suite_file=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
abort() {
printf 'Error: %s\n' "$1" >&2
exit 1
}
while [[ "$#" -ne 0 ]]; do
case "$1" in
-c)
count_only_flag=1
;;
-f)
shift
filter="$1"
;;
-j)
shift
num_jobs="$1"
flags+=('-j' "$num_jobs")
;;
-T)
flags+=('-T')
;;
-x)
flags+=('-x')
;;
--no-parallelize-across-files)
bats_no_parallelize_across_files=1
;;
--no-parallelize-within-files)
bats_no_parallelize_within_files=1
flags+=("--no-parallelize-within-files")
;;
--filter-status)
shift
filter_status="$1"
;;
--dummy-flag)
;;
--trace)
flags+=('--trace')
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
;;
--print-output-on-failure)
flags+=(--print-output-on-failure)
;;
--show-output-of-passing-tests)
flags+=(--show-output-of-passing-tests)
;;
--verbose-run)
flags+=(--verbose-run)
;;
--gather-test-outputs-in)
shift
flags+=(--gather-test-outputs-in "$1")
;;
--setup-suite-file)
shift
setup_suite_file="$1"
;;
*)
break
;;
esac
shift
done
if [[ "$num_jobs" != 1 ]]; then
if ! type -p parallel >/dev/null && [[ -z "$bats_no_parallelize_across_files" ]]; then
abort "Cannot execute \"${num_jobs}\" jobs without GNU parallel"
exit 1
fi
# shellcheck source=lib/bats-core/semaphore.bash
source "${BATS_ROOT}/lib/bats-core/semaphore.bash"
bats_semaphore_setup
fi
# create a file that contains all (filtered) tests to run from all files
TESTS_LIST_FILE="${BATS_RUN_TMPDIR}/test_list_file.txt"
bats_gather_tests() {
all_tests=()
for filename in "$@"; do
if [[ ! -f "$filename" ]]; then
abort "Test file \"${filename}\" does not exist"
fi
test_names=()
test_dupes=()
while read -r line; do
if [[ ! "$line" =~ ^bats_test_function\ ]]; then
continue
fi
line="${line%$'\r'}"
line="${line#* }"
test_line=$(printf "%s\t%s" "$filename" "$line")
all_tests+=("$test_line")
printf "%s\n" "$test_line" >>"$TESTS_LIST_FILE"
# avoid unbound variable errors on empty array expansion with old bash versions
if [[ ${#test_names[@]} -gt 0 && " ${test_names[*]} " == *" $line "* ]]; then
test_dupes+=("$line")
continue
fi
test_names+=("$line")
done < <(BATS_TEST_FILTER="$filter" bats-preprocess "$filename")
if [[ "${#test_dupes[@]}" -ne 0 ]]; then
abort "Duplicate test name(s) in file \"${filename}\": ${test_dupes[*]}"
fi
done
test_count="${#all_tests[@]}"
}
TEST_ROOT=${1-}
TEST_ROOT=${TEST_ROOT%/*}
BATS_RUN_LOGS_DIRECTORY="$TEST_ROOT/.bats/run-logs"
if [[ ! -d "$BATS_RUN_LOGS_DIRECTORY" ]]; then
if [[ -n "$filter_status" ]]; then
printf "Error: --filter-status needs '%s/' to save failed tests. Please create this folder, add it to .gitignore and try again.\n" "$BATS_RUN_LOGS_DIRECTORY"
exit 1
else
BATS_RUN_LOGS_DIRECTORY=
fi
# discard via sink instead of having a conditional later
export BATS_RUNLOG_FILE='/dev/null'
else
# use UTC (-u) to avoid problems with TZ changes
BATS_RUNLOG_DATE=$(date -u '+%Y-%m-%d %H:%M:%S UTC')
export BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}.log"
fi
bats_gather_tests "$@"
if [[ -n "$filter_status" ]]; then
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
case "$filter_status" in
failed)
bats_filter_test_by_status() { # <line>
! bats_binary_search "$1" "passed_tests"
}
;;
passed)
bats_filter_test_by_status() {
! bats_binary_search "$1" "failed_tests"
}
;;
missed)
bats_filter_test_by_status() {
! bats_binary_search "$1" "failed_tests" && ! bats_binary_search "$1" "passed_tests"
}
;;
*)
printf "Error: Unknown value '%s' for --filter-status. Valid values are 'failed' and 'missed'.\n" "$filter_status">&2
exit 1
;;
esac
if IFS='' read -d $'\n' -r BATS_PREVIOUS_RUNLOG_FILE < <(ls -1r "$BATS_RUN_LOGS_DIRECTORY"); then
BATS_PREVIOUS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/$BATS_PREVIOUS_RUNLOG_FILE"
if [[ $BATS_PREVIOUS_RUNLOG_FILE == "$BATS_RUNLOG_FILE" ]]; then
count=$(find "$BATS_RUN_LOGS_DIRECTORY" -name "$BATS_RUNLOG_DATE*" | wc -l)
BATS_RUNLOG_FILE="$BATS_RUN_LOGS_DIRECTORY/${BATS_RUNLOG_DATE}-$count.log"
fi
failed_tests=()
passed_tests=()
# store tests that were already filtered out in the last run for the same filter reason
last_filtered_tests=()
i=0
while read -rd $'\n' line; do
((++i))
case "$line" in
"passed "*)
passed_tests+=("${line#passed }")
;;
"failed "*)
failed_tests+=("${line#failed }")
;;
"status-filtered $filter_status"*) # pick up tests that were filtered in the last round for the same status
last_filtered_tests+=("${line#status-filtered "$filter_status" }")
;;
"status-filtered "*) # ignore other status-filtered lines
;;
"#"*) # allow for comments
;;
*)
printf "Error: %s:%d: Invalid format: %s\n" "$BATS_PREVIOUS_RUNLOG_FILE" "$i" "$line" >&2
exit 1
;;
esac
done < <(sort "$BATS_PREVIOUS_RUNLOG_FILE")
filtered_tests=()
for line in "${all_tests[@]}"; do
if bats_filter_test_by_status "$line" && ! bats_binary_search "$line" last_filtered_tests; then
printf "%s\n" "$line"
filtered_tests+=("$line")
else
printf "status-filtered %s %s\n" "$filter_status" "$line" >> "$BATS_RUNLOG_FILE"
fi
done > "$TESTS_LIST_FILE"
# save filtered tests to exclude them again in next round
for test_line in "${last_filtered_tests[@]}"; do
printf "status-filtered %s %s\n" "$filter_status" "$test_line"
done >> "$BATS_RUNLOG_FILE"
test_count="${#filtered_tests[@]}"
if [[ ${#failed_tests[@]} -eq 0 && ${#filtered_tests[@]} -eq 0 ]]; then
printf "There where no failed tests in the last recorded run.\n" >&2
fi
else
printf "No recording of previous runs found. Running all tests!\n" >&2
fi
fi
if [[ -n "$count_only_flag" ]]; then
printf '%d\n' "${test_count}"
exit
fi
if [[ -n "$bats_no_parallelize_across_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
exit 1
fi
if [[ -n "$bats_no_parallelize_within_files" ]] && [[ ! "$num_jobs" -gt 1 ]]; then
abort "The flag --no-parallelize-across-files requires at least --jobs 2"
exit 1
fi
# only abort on the lowest levels
trap 'BATS_INTERRUPTED=true' INT
bats_exec_suite_status=0
printf '1..%d\n' "${test_count}"
# No point on continuing if there's no tests.
if [[ "${test_count}" == 0 ]]; then
exit
fi
export BATS_SUITE_TMPDIR="${BATS_RUN_TMPDIR}/suite"
if ! mkdir "$BATS_SUITE_TMPDIR"; then
printf '%s\n' "Failed to create BATS_SUITE_TMPDIR" >&2
exit 1
fi
# Deduplicate filenames (without reordering) to avoid running duplicate tests n by n times.
# (see https://github.com/bats-core/bats-core/issues/329)
# If a file was specified multiple times, we already got it repeatedly in our TESTS_LIST_FILE.
# Thus, it suffices to bats-exec-file it once to run all repeated tests on it.
IFS=$'\n' read -d '' -r -a BATS_UNIQUE_TEST_FILENAMES < <(printf "%s\n" "$@"| nl | sort -k 2 | uniq -f 1 | sort -n | cut -f 2-) || true
# shellcheck source=lib/bats-core/tracing.bash
source "$BATS_ROOT/lib/bats-core/tracing.bash"
bats_setup_tracing
trap bats_suite_exit_trap EXIT
bats_suite_exit_trap() {
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" || -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
if [[ -z "${BATS_SETUP_SUITE_COMPLETED}" ]]; then
printf "not ok 1 setup_suite\n"
elif [[ -z "${BATS_TEARDOWN_SUITE_COMPLETED}" ]]; then
printf "not ok %d teardown_suite\n" $((test_count+1))
fi
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}"
bats_print_failed_command "${stack_trace[@]}"
bats_exec_suite_status=1
fi
if [[ ${BATS_INTERRUPTED-NOTSET} != NOTSET ]]; then
printf "\n# Received SIGINT, aborting ...\n\n"
fi
if [[ -d "$BATS_RUN_LOGS_DIRECTORY" && -n "${BATS_INTERRUPTED:-}" ]]; then
# aborting a test run with CTRL+C does not save the runlog file
rm "$BATS_RUNLOG_FILE"
fi
exit "$bats_exec_suite_status"
}
bats_run_teardown_suite() {
# avoid being called twice, in case this is not called through bats_teardown_suite_trap
# but from the end of file
trap bats_suite_exit_trap EXIT
set -eET
BATS_TEARDOWN_SUITE_COMPLETED=
teardown_suite 2>&1
BATS_TEARDOWN_SUITE_COMPLETED=1
set +ET
}
bats_teardown_suite_trap() {
bats_run_teardown_suite
bats_suite_exit_trap
}
setup_suite() {
:
}
teardown_suite() {
:
}
trap bats_teardown_suite_trap EXIT
if [[ -n "$setup_suite_file" ]]; then
setup_suite() {
printf "%s does not define \`setup_suite()\`\n" "$setup_suite_file" >&2
exit 1
}
# shellcheck disable=SC1090
source "$setup_suite_file"
fi
set -eET
BATS_SETUP_SUITE_COMPLETED=
setup_suite 2>&1
BATS_SETUP_SUITE_COMPLETED=1
set +ET
if [[ "$num_jobs" -gt 1 ]] && [[ -z "$bats_no_parallelize_across_files" ]]; then
# run files in parallel to get the maximum pool of parallel tasks
# shellcheck disable=SC2086,SC2068
# we need to handle the quoting of ${flags[@]} ourselves,
# because parallel can only quote it as one
parallel --keep-order --jobs "$num_jobs" bats-exec-file "$(printf "%q " "${flags[@]}")" "{}" "$TESTS_LIST_FILE" ::: "${BATS_UNIQUE_TEST_FILENAMES[@]}" 2>&1 || bats_exec_suite_status=1
else
for filename in "${BATS_UNIQUE_TEST_FILENAMES[@]}"; do
if [[ "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
bats_exec_suite_status=130 # bash's code for SIGINT exits
break
fi
bats-exec-file "${flags[@]}" "$filename" "${TESTS_LIST_FILE}" || bats_exec_suite_status=1
done
fi
set -eET
bats_run_teardown_suite
exit "$bats_exec_suite_status"

View File

@ -0,0 +1,231 @@
#!/usr/bin/env bash
set -eET
# Variables used in other scripts.
BATS_ENABLE_TIMING=''
BATS_EXTENDED_SYNTAX=''
BATS_TRACE_LEVEL="${BATS_TRACE_LEVEL:-0}"
BATS_PRINT_OUTPUT_ON_FAILURE="${BATS_PRINT_OUTPUT_ON_FAILURE:-}"
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS:-}"
BATS_VERBOSE_RUN="${BATS_VERBOSE_RUN:-}"
BATS_GATHER_TEST_OUTPUTS_IN="${BATS_GATHER_TEST_OUTPUTS_IN:-}"
BATS_TEST_NAME_PREFIX="${BATS_TEST_NAME_PREFIX:-}"
while [[ "$#" -ne 0 ]]; do
case "$1" in
-T)
BATS_ENABLE_TIMING='-T'
;;
-x)
# shellcheck disable=SC2034
BATS_EXTENDED_SYNTAX='-x'
;;
--dummy-flag)
;;
--trace)
(( ++BATS_TRACE_LEVEL )) # avoid returning 0
;;
--print-output-on-failure)
BATS_PRINT_OUTPUT_ON_FAILURE=1
;;
--show-output-of-passing-tests)
BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS=1
;;
--verbose-run)
BATS_VERBOSE_RUN=1
;;
--gather-test-outputs-in)
shift
BATS_GATHER_TEST_OUTPUTS_IN="$1"
;;
*)
break
;;
esac
shift
done
export BATS_TEST_FILENAME="$1"
export BATS_TEST_NAME="$2"
export BATS_SUITE_TEST_NUMBER="$3"
export BATS_TEST_NUMBER="$4"
BATS_TEST_TRY_NUMBER="$5"
if [[ -z "$BATS_TEST_FILENAME" ]]; then
printf 'usage: bats-exec-test <filename>\n' >&2
exit 1
elif [[ ! -f "$BATS_TEST_FILENAME" ]]; then
printf 'bats: %s does not exist\n' "$BATS_TEST_FILENAME" >&2
exit 1
fi
bats_create_test_tmpdirs() {
local tests_tmpdir="${BATS_RUN_TMPDIR}/test"
if ! mkdir -p "$tests_tmpdir"; then
printf 'Failed to create: %s\n' "$tests_tmpdir" >&2
exit 1
fi
BATS_TEST_TMPDIR="$tests_tmpdir/$BATS_SUITE_TEST_NUMBER"
if ! mkdir "$BATS_TEST_TMPDIR"; then
printf 'Failed to create BATS_TEST_TMPDIR%d: %s\n' "$BATS_TEST_TRY_NUMBER" "$BATS_TEST_TMPDIR" >&2
exit 1
fi
printf "%s\n" "$BATS_TEST_NAME" > "$BATS_TEST_TMPDIR.name"
export BATS_TEST_TMPDIR
}
# load the test helper functions like `load` or `run` that are needed to run a (preprocessed) .bats file without bash errors
# shellcheck source=lib/bats-core/test_functions.bash disable=SC2153
source "$BATS_ROOT/lib/bats-core/test_functions.bash"
# shellcheck source=lib/bats-core/tracing.bash disable=SC2153
source "$BATS_ROOT/lib/bats-core/tracing.bash"
bats_teardown_trap() {
bats_check_status_from_trap
local bats_teardown_trap_status=0
# mark the start of this function to distinguish where skip is called
# parameter 1 will signify the reason why this function was called
# this is used to identify when this is called as exit trap function
BATS_TEARDOWN_STARTED=${1:-1}
teardown >>"$BATS_OUT" 2>&1 || bats_teardown_trap_status="$?"
if [[ $bats_teardown_trap_status -eq 0 ]]; then
BATS_TEARDOWN_COMPLETED=1
elif [[ -n "$BATS_TEST_COMPLETED" ]]; then
BATS_DEBUG_LAST_STACK_TRACE_IS_VALID=1
BATS_ERROR_STATUS="$bats_teardown_trap_status"
fi
bats_exit_trap
}
# shellcheck source=lib/bats-core/common.bash
source "$BATS_ROOT/lib/bats-core/common.bash"
bats_exit_trap() {
local status
local skipped=''
trap - ERR EXIT
if [[ -n "$BATS_TEST_SKIPPED" ]]; then
skipped=' # skip'
if [[ "$BATS_TEST_SKIPPED" != '1' ]]; then
skipped+=" $BATS_TEST_SKIPPED"
fi
fi
BATS_TEST_TIME=''
if [[ -z "${skipped}" && -n "$BATS_ENABLE_TIMING" ]]; then
BATS_TEST_TIME=" in "$(( $(get_mills_since_epoch) - BATS_TEST_START_TIME ))"ms"
fi
local print_bats_out="${BATS_SHOW_OUTPUT_OF_SUCCEEDING_TESTS}"
local should_retry=''
if [[ -z "$BATS_TEST_COMPLETED" || -z "$BATS_TEARDOWN_COMPLETED" || "${BATS_INTERRUPTED-NOTSET}" != NOTSET ]]; then
if [[ "$BATS_ERROR_STATUS" -eq 0 ]]; then
# For some versions of bash, `$?` may not be set properly for some error
# conditions before triggering the EXIT trap directly (see #72 and #81).
# Thanks to the `BATS_TEARDOWN_COMPLETED` signal, this will pinpoint such
# errors if they happen during `teardown()` when `bats_perform_test` calls
# `bats_teardown_trap` directly after the test itself passes.
#
# If instead the test fails, and the `teardown()` error happens while
# `bats_teardown_trap` runs as the EXIT trap, the test will fail with no
# output, since there's no way to reach the `bats_exit_trap` call.
BATS_ERROR_STATUS=1
fi
if bats_should_retry_test; then
should_retry=1
status=126 # signify retry
rm -r "$BATS_TEST_TMPDIR" # clean up for retry
else
printf 'not ok %d %s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" >&3
local stack_trace
bats_get_failure_stack_trace stack_trace
bats_print_stack_trace "${stack_trace[@]}" >&3
bats_print_failed_command "${stack_trace[@]}" >&3
if [[ $BATS_PRINT_OUTPUT_ON_FAILURE && -n "${output:-}" ]]; then
printf "Last output:\n%s\n" "$output" >> "$BATS_OUT"
fi
print_bats_out=1
status=1
local state=failed
fi
else
printf 'ok %d %s%s\n' "$BATS_SUITE_TEST_NUMBER" "${BATS_TEST_NAME_PREFIX:-}${BATS_TEST_DESCRIPTION}${BATS_TEST_TIME}" \
"$skipped" >&3
status=0
local state=passed
fi
if [[ -z "$should_retry" ]]; then
printf "%s %s\t%s\n" "$state" "$BATS_TEST_FILENAME" "$BATS_TEST_NAME" >> "$BATS_RUNLOG_FILE"
if [[ $print_bats_out ]]; then
bats_prefix_lines_for_tap_output < "$BATS_OUT" | bats_replace_filename >&3
fi
fi
if [[ $BATS_GATHER_TEST_OUTPUTS_IN ]]; then
local try_suffix=
if [[ -n "$should_retry" ]]; then
try_suffix="-try$BATS_TEST_TRY_NUMBER"
fi
cp "$BATS_OUT" "$BATS_GATHER_TEST_OUTPUTS_IN/$BATS_SUITE_TEST_NUMBER$try_suffix-$BATS_TEST_DESCRIPTION.log"
fi
rm -f "$BATS_OUT"
exit "$status"
}
get_mills_since_epoch() {
local ms_since_epoch
ms_since_epoch=$(date +%s%N)
if [[ "$ms_since_epoch" == *N || "${#ms_since_epoch}" -lt 19 ]]; then
ms_since_epoch=$(( $(date +%s) * 1000 ))
else
ms_since_epoch=$(( ms_since_epoch / 1000000 ))
fi
printf "%d\n" "$ms_since_epoch"
}
bats_perform_test() {
if ! declare -F "$BATS_TEST_NAME" &>/dev/null; then
local quoted_test_name
bats_quote_code quoted_test_name "$BATS_TEST_NAME"
printf "bats: unknown test name %s\n" "$quoted_test_name" >&2
exit 1
fi
BATS_TEST_COMPLETED=
BATS_TEST_SKIPPED=
BATS_TEARDOWN_COMPLETED=
BATS_ERROR_STATUS=
bats_setup_tracing
# mark this call as trap call
trap 'bats_teardown_trap as-exit-trap' EXIT
BATS_TEST_START_TIME=$(get_mills_since_epoch)
"$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 4>&1
BATS_TEST_COMPLETED=1
trap 'bats_exit_trap' EXIT
bats_teardown_trap "" # pass empty parameter to signify call outside trap
}
trap bats_interrupt_trap INT
# shellcheck source=lib/bats-core/preprocessing.bash
source "$BATS_ROOT/lib/bats-core/preprocessing.bash"
exec 3<&1
bats_create_test_tmpdirs
# Run the given test.
bats_evaluate_preprocessed_source
bats_perform_test

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -e
trap '' INT
cat

View File

@ -0,0 +1,251 @@
#!/usr/bin/env bash
set -euo pipefail
# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/formatter.bash"
BASE_PATH=.
while [[ "$#" -ne 0 ]]; do
case "$1" in
--base-path)
shift
normalize_base_path BASE_PATH "$1"
;;
esac
shift
done
init_suite() {
suite_test_exec_time=0
# since we have to print the suite header before its contents but we don't know the contents before the header,
# we have to buffer the contents
_suite_buffer=""
test_result_state="" # declare for the first flush, when no test has been encountered
}
_buffer_log=
init_file() {
file_count=0
file_failures=0
file_skipped=0
file_exec_time=0
test_exec_time=0
_buffer=""
_buffer_log=""
_system_out_log=""
test_result_state="" # mark that no test has run in this file so far
}
host() {
local hostname="${HOST:-}"
[[ -z "$hostname" ]] && hostname="${HOSTNAME:-}"
[[ -z "$hostname" ]] && hostname="$(uname -n)"
[[ -z "$hostname" ]] && hostname="$(hostname -f)"
echo "$hostname"
}
# convert $1 (time in milliseconds) to seconds
milliseconds_to_seconds() {
# we cannot rely on having bc for this calculation
full_seconds=$(($1 / 1000))
remaining_milliseconds=$(($1 % 1000))
if [[ $remaining_milliseconds -eq 0 ]]; then
printf "%d" "$full_seconds"
else
printf "%d.%03d" "$full_seconds" "$remaining_milliseconds"
fi
}
suite_header() {
printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<testsuites time=\"%s\">\n" "$(milliseconds_to_seconds "${suite_test_exec_time}")"
}
file_header() {
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%S")
printf "<testsuite name=\"%s\" tests=\"%s\" failures=\"%s\" errors=\"0\" skipped=\"%s\" time=\"%s\" timestamp=\"%s\" hostname=\"%s\">\n" \
"$(xml_escape "${class}")" "${file_count}" "${file_failures}" "${file_skipped}" "$(milliseconds_to_seconds "${file_exec_time}")" "${timestamp}" "$(host)"
}
file_footer() {
printf "</testsuite>\n"
}
suite_footer() {
printf "</testsuites>\n"
}
print_test_case() {
if [[ "$test_result_state" == ok && -z "$_system_out_log" && -z "$_buffer_log" ]]; then
# pass and no output can be shortened
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\" />\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
else
printf " <testcase classname=\"%s\" name=\"%s\" time=\"%s\">\n" "$(xml_escape "${class}")" "$(xml_escape "${name}")" "$(milliseconds_to_seconds "${test_exec_time}")"
if [[ -n "$_system_out_log" ]]; then
printf " <system-out>%s</system-out>\n" "$(xml_escape "${_system_out_log}")"
fi
if [[ -n "$_buffer_log" || "$test_result_state" == not_ok ]]; then
printf " <failure type=\"failure\">%s</failure>\n" "$(xml_escape "${_buffer_log}")"
fi
if [[ "$test_result_state" == skipped ]]; then
printf " <skipped>%s</skipped>\n" "$(xml_escape "$test_skip_message")"
fi
printf " </testcase>\n"
fi
}
xml_escape() {
output=${1//&/&amp;}
output=${output//</&lt;}
output=${output//>/&gt;}
output=${output//'"'/&quot;}
output=${output//\'/&#39;}
local CONTROL_CHAR=$'\033'
output="${output//$CONTROL_CHAR/&#27;}"
printf "%s" "$output"
}
suite_buffer() {
local output
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
_suite_buffer="${_suite_buffer}${output%x}"
}
suite_flush() {
echo -n "${_suite_buffer}"
_suite_buffer=""
}
buffer() {
local output
output="$("$@"; printf "x")" # use x marker to avoid losing trailing newlines
_buffer="${_buffer}${output%x}"
}
flush() {
echo -n "${_buffer}"
_buffer=""
}
log() {
if [[ -n "$_buffer_log" ]]; then
_buffer_log="${_buffer_log}
$1"
else
_buffer_log="$1"
fi
}
flush_log() {
if [[ -n "$test_result_state" ]]; then
buffer print_test_case
fi
_buffer_log=""
_system_out_log=""
}
log_system_out() {
if [[ -n "$_system_out_log" ]]; then
_system_out_log="${_system_out_log}
$1"
else
_system_out_log="$1"
fi
}
finish_file() {
if [[ "${class-JUNIT_FORMATTER_NO_FILE_ENCOUNTERED}" != JUNIT_FORMATTER_NO_FILE_ENCOUNTERED ]]; then
file_header
printf "%s\n" "${_buffer}"
file_footer
fi
}
finish_suite() {
flush_log
suite_header
suite_flush
finish_file # must come after suite flush to not print the last file before the others
suite_footer
}
bats_tap_stream_plan() { # <number of tests>
:
}
init_suite
trap finish_suite EXIT
trap '' INT
bats_tap_stream_begin() { # <test index> <test name>
flush_log
# set after flushing to avoid overriding name of test
name="$2"
}
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
if [[ "$1" == "--duration" ]]; then
test_exec_time="${BASH_REMATCH[1]}"
else
test_exec_time=0
fi
((file_count += 1))
test_result_state='ok'
file_exec_time="$((file_exec_time + test_exec_time))"
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
}
bats_tap_stream_skipped() { # <test index> <test name> <skip reason>
((file_count += 1))
((file_skipped += 1))
test_result_state='skipped'
test_exec_time=0
test_skip_message="$3"
}
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
((file_count += 1))
((file_failures += 1))
if [[ "$1" == "--duration" ]]; then
test_exec_time="${BASH_REMATCH[1]}"
else
test_exec_time=0
fi
test_result_state=not_ok
file_exec_time="$((file_exec_time + test_exec_time))"
suite_test_exec_time=$((suite_test_exec_time + test_exec_time))
}
bats_tap_stream_comment() { # <comment text without leading '# '> <scope>
local comment="$1" scope="$2"
case "$scope" in
begin)
# everything that happens between begin and [not] ok is FD3 output from the test
log_system_out "$comment"
;;
ok)
# non failed tests can produce FD3 output
log_system_out "$comment"
;;
*)
# everything else is considered error output
log "$1"
;;
esac
}
bats_tap_stream_suite() { # <file name>
flush_log
suite_buffer finish_file
init_file
class="${1/$BASE_PATH}"
}
bats_tap_stream_unknown() { # <full line>
:
}
bats_parse_internal_extended_tap

View File

@ -0,0 +1,328 @@
#!/usr/bin/env bash
set -e
# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/formatter.bash"
BASE_PATH=.
BATS_ENABLE_TIMING=
while [[ "$#" -ne 0 ]]; do
case "$1" in
-T)
BATS_ENABLE_TIMING="-T"
;;
--base-path)
shift
normalize_base_path BASE_PATH "$1"
;;
esac
shift
done
update_count_column_width() {
count_column_width=$((${#count} * 2 + 2))
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
# additional space for ' in %s sec'
count_column_width=$((count_column_width + ${#SECONDS} + 8))
fi
# also update dependent value
update_count_column_left
}
update_screen_width() {
screen_width="$(tput cols)"
# also update dependent value
update_count_column_left
}
update_count_column_left() {
count_column_left=$((screen_width - count_column_width))
}
# avoid unset variables
count=0
screen_width=80
update_count_column_width
update_screen_width
test_result=
trap update_screen_width WINCH
begin() {
test_result= # reset to avoid carrying over result state from previous test
line_backoff_count=0
go_to_column 0
update_count_column_width
buffer_with_truncation $((count_column_left - 1)) ' %s' "$name"
clear_to_end_of_line
go_to_column $count_column_left
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
buffer "%${#count}s/${count} in %s sec" "$index" "$SECONDS"
else
buffer "%${#count}s/${count}" "$index"
fi
go_to_column 1
}
finish_test() {
move_up $line_backoff_count
go_to_column 0
buffer "$@"
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
set_color 2
buffer ' [%s]' "$TIMING"
fi
advance
move_down $(( line_backoff_count - 1 ))
}
pass() {
TIMING="${1:-}"
finish_test ' ✓ %s' "$name"
test_result=pass
}
skip() {
local reason="$1"
if [[ -n "$reason" ]]; then
reason=": $reason"
fi
BATS_ENABLE_TIMING='' finish_test ' - %s (skipped%s)' "$name" "$reason"
test_result=skip
}
fail() {
set_color 1 bold
TIMING="${1:-}"
finish_test ' ✗ %s' "$name"
test_result=fail
}
log() {
case ${test_result} in
pass)
clear_color
;;
fail)
set_color 1
;;
esac
buffer ' %s\n' "$1"
clear_color
}
summary() {
if [ "$failures" -eq 0 ] ; then
set_color 2 bold
else
set_color 1 bold
fi
buffer '\n%d test' "$count"
if [[ "$count" -ne 1 ]]; then
buffer 's'
fi
buffer ', %d failure' "$failures"
if [[ "$failures" -ne 1 ]]; then
buffer 's'
fi
if [[ "$skipped" -gt 0 ]]; then
buffer ', %d skipped' "$skipped"
fi
not_run=$((count - passed - failures - skipped))
if [[ "$not_run" -gt 0 ]]; then
buffer ', %d not run' "$not_run"
fi
if [[ -n "$BATS_ENABLE_TIMING" ]]; then
buffer " in $SECONDS seconds"
fi
buffer '\n'
clear_color
}
buffer_with_truncation() {
local width="$1"
shift
local string
# shellcheck disable=SC2059
printf -v 'string' -- "$@"
if [[ "${#string}" -gt "$width" ]]; then
buffer '%s...' "${string:0:$((width - 4))}"
else
buffer '%s' "$string"
fi
}
move_up() {
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
buffer '\x1B[%dA' "$1"
fi
}
move_down() {
if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
buffer '\x1B[%dB' "$1"
fi
}
go_to_column() {
local column="$1"
buffer '\x1B[%dG' $((column + 1))
}
clear_to_end_of_line() {
buffer '\x1B[K'
}
advance() {
clear_to_end_of_line
buffer '\n'
clear_color
}
set_color() {
local color="$1"
local weight=22
if [[ "${2:-}" == 'bold' ]]; then
weight=1
fi
buffer '\x1B[%d;%dm' "$((30 + color))" "$weight"
}
clear_color() {
buffer '\x1B[0m'
}
_buffer=
buffer() {
local content
# shellcheck disable=SC2059
printf -v content -- "$@"
_buffer+="$content"
}
prefix_buffer_with() {
local old_buffer="$_buffer"
_buffer=''
"$@"
_buffer="$_buffer$old_buffer"
}
flush() {
printf '%s' "$_buffer"
_buffer=
}
finish() {
flush
printf '\n'
}
trap finish EXIT
trap '' INT
bats_tap_stream_plan() {
count="$1"
index=0
passed=0
failures=0
skipped=0
name=
update_count_column_width
}
bats_tap_stream_begin() {
index="$1"
name="$2"
begin
flush
}
bats_tap_stream_ok() {
local duration=
if [[ "$1" == "--duration" ]]; then
duration="$2"
shift 2
fi
index="$1"
name="$2"
((++passed))
pass "$duration"
}
bats_tap_stream_skipped() {
index="$1"
name="$2"
((++skipped))
skip "$3"
}
bats_tap_stream_not_ok() {
local duration=
if [[ "$1" == "--duration" ]]; then
duration="$2"
shift 2
fi
index="$1"
name="$2"
((++failures))
fail "$duration"
}
bats_tap_stream_comment() { # <comment> <scope>
local scope=$2
# count the lines we printed after the begin text,
if [[ $line_backoff_count -eq 0 && $scope == begin ]]; then
# if this is the first line after begin, go down one line
buffer "\n"
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
fi
(( ++line_backoff_count ))
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
log "$1"
}
bats_tap_stream_suite() {
#test_file="$1"
line_backoff_count=0
index=
# indicate filename for failures
local file_name="${1/$BASE_PATH}"
name="File $file_name"
set_color 4 bold
buffer "%s\n" "$file_name"
clear_color
}
line_backoff_count=0
bats_tap_stream_unknown() { # <full line> <scope>
local scope=$2
# count the lines we printed after the begin text, (or after suite, in case of syntax errors)
if [[ $line_backoff_count -eq 0 && ( $scope == begin || $scope == suite )]]; then
# if this is the first line after begin, go down one line
buffer "\n"
(( ++line_backoff_count )) # prefix-increment to avoid "error" due to returning 0
fi
(( ++line_backoff_count ))
(( line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
buffer "%s\n" "$1"
flush
}
bats_parse_internal_extended_tap
summary

View File

@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -e
trap '' INT
# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/formatter.bash"
bats_tap_stream_plan() {
printf "1..%d\n" "$1"
}
bats_tap_stream_begin() { #<test index> <test name>
:
}
bats_tap_stream_ok() { # [--duration <milliseconds] <test index> <test name>
if [[ "$1" == "--duration" ]]; then
printf "ok %d %s # in %d ms\n" "$3" "$4" "$2"
else
printf "ok %d %s\n" "$1" "$2"
fi
}
bats_tap_stream_not_ok() { # [--duration <milliseconds>] <test index> <test name>
if [[ "$1" == "--duration" ]]; then
printf "not ok %d %s # in %d ms\n" "$3" "$4" "$2"
else
printf "not ok %d %s\n" "$1" "$2"
fi
}
bats_tap_stream_skipped() { # <test index> <test name> <reason>
if [[ -n "$3" ]]; then
printf "ok %d %s # skip %s\n" "$1" "$2" "$3"
else
printf "ok %d %s # skip\n" "$1" "$2"
fi
}
bats_tap_stream_comment() { # <comment text without leading '# '>
printf "# %s\n" "$1"
}
bats_tap_stream_suite() { # <file name>
:
}
bats_tap_stream_unknown() { # <full line>
printf "%s\n" "$1"
}
bats_parse_internal_extended_tap

View File

@ -0,0 +1,91 @@
#!/usr/bin/env bash
set -e
while [[ "$#" -ne 0 ]]; do
case "$1" in
-T)
BATS_ENABLE_TIMING="-T"
;;
esac
shift
done
header_pattern='[0-9]+\.\.[0-9]+'
IFS= read -r header
if [[ "$header" =~ $header_pattern ]]; then
printf "TAP version 13\n"
printf "%s\n" "$header"
else
# If the first line isn't a TAP plan, print it and pass the rest through
printf '%s\n' "$header"
exec cat
fi
yaml_block_open=''
add_yaml_entry() {
if [[ -z "$yaml_block_open" ]]; then
printf " ---\n"
fi
printf " %s: %s\n" "$1" "$2"
yaml_block_open=1
}
close_previous_yaml_block() {
if [[ -n "$yaml_block_open" ]]; then
printf " ...\n"
yaml_block_open=''
fi
}
trap '' INT
number_of_printed_log_lines_for_this_test_so_far=0
while IFS= read -r line; do
case "$line" in
'begin '*) ;;
'ok '*)
close_previous_yaml_block
number_of_printed_log_lines_for_this_test_so_far=0
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
timing_expr="(ok [0-9]+ .+) in ([0-9]+)ms$"
if [[ "$line" =~ $timing_expr ]]; then
printf "%s\n" "${BASH_REMATCH[1]}"
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
else
echo "Could not match output line to timing regex: $line" >&2
exit 1
fi
else
printf "%s\n" "${line}"
fi
;;
'not ok '*)
close_previous_yaml_block
number_of_printed_log_lines_for_this_test_so_far=0
timing_expr="(not ok [0-9]+ .+) in ([0-9])+ms$"
if [[ -n "${BATS_ENABLE_TIMING-}" ]]; then
if [[ "$line" =~ $timing_expr ]]; then
printf "%s\n" "${BASH_REMATCH[1]}"
add_yaml_entry "duration_ms" "${BASH_REMATCH[2]}"
else
echo "Could not match failure line to timing regex: $line" >&2
exit 1
fi
else
printf "%s\n" "${line}"
fi
;;
'# '*)
if [[ $number_of_printed_log_lines_for_this_test_so_far -eq 0 ]]; then
add_yaml_entry "message" "|" # use a multiline string for this entry
fi
((++number_of_printed_log_lines_for_this_test_so_far))
printf " %s\n" "${line:2}"
;;
'suite '*) ;;
esac
done
# close the final block if there was one
close_previous_yaml_block

View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -e
bats_encode_test_name() {
local name="$1"
local result='test_'
local hex_code
if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then
name="${name//_/-5f}"
name="${name//-/-2d}"
name="${name// /_}"
result+="$name"
else
local length="${#name}"
local char i
for ((i = 0; i < length; i++)); do
char="${name:$i:1}"
if [[ "$char" == ' ' ]]; then
result+='_'
elif [[ "$char" =~ [[:alnum:]] ]]; then
result+="$char"
else
printf -v 'hex_code' -- '-%02x' \'"$char"
result+="$hex_code"
fi
done
fi
printf -v "$2" '%s' "$result"
}
BATS_TEST_PATTERN="^[[:blank:]]*@test[[:blank:]]+(.*[^[:blank:]])[[:blank:]]+\{(.*)\$"
BATS_TEST_PATTERN_COMMENT="[[:blank:]]*([^[:blank:]()]+)[[:blank:]]*\(?\)?[[:blank:]]+\{[[:blank:]]+#[[:blank:]]*@test[[:blank:]]*\$"
test_file="$1"
tests=()
{
while IFS= read -r line; do
line="${line//$'\r'/}"
if [[ "$line" =~ $BATS_TEST_PATTERN ]] || [[ "$line" =~ $BATS_TEST_PATTERN_COMMENT ]]; then
name="${BASH_REMATCH[1]#[\'\"]}"
name="${name%[\'\"]}"
body="${BASH_REMATCH[2]:-}"
bats_encode_test_name "$name" 'encoded_name'
printf '%s() { bats_test_begin "%s"; %s\n' "${encoded_name:?}" "$name" "$body" || :
if [[ -z "$BATS_TEST_FILTER" || "$name" =~ $BATS_TEST_FILTER ]]; then
tests+=("$encoded_name")
fi
else
printf '%s\n' "$line"
fi
done
} <<<"$(<"$test_file")"$'\n'
for test_name in "${tests[@]}"; do
printf 'bats_test_function %s\n' "$test_name"
done

View File

@ -0,0 +1,197 @@
# setup paths for BATS test units
setup() {
[ ! -f ${BATS_PARENT_TMPNAME}.skip ] || skip "skip remaining tests"
bats_require_minimum_version 1.5.0
R="$BATS_TEST_DIRNAME"
# R=`cd "$T"/.. && pwd`
TMP="$BATS_FILE_TMPDIR"
load test_helper/bats-support/load
load test_helper/bats-assert/load
load test_helper/bats-file/load
ZTMP="$BATS_FILE_TMPDIR"
if [ "$ZENROOM_EXECUTABLE" == "" ]; then
ZENROOM_EXECUTABLE="/usr/local/bin/zenroom"
fi
PATH="$PATH:/usr/local/bin:/usr/local/sbin"
MNT="/media/`basename $BATS_TEST_FILENAME`"
PIM=42
# max password stdin size to veracrypt is 128 bytes
PW="91cd69b2caab05cb64f8e1c8ae22a7c5a25564e7fc5116d0303dce1ffd26861ea0483c25bdb85adb216718c19815eb59ac14ec9783bfbbb57786ca7d9038c845"
# `openssl rand -hex 64`"
FS='ext4'
SIZE='20M'
[ "$TOMB" = "" ] && TOMB=test.tomb
# default on freebsd is 1001
user_uid=1000
user_gid=1000
system="`uname -s`"
f_create=""
f_format=""
f_map=""
f_mount=""
f_close=""
case "$system" in
FreeBSD)
f_create=portable_create
f_format=freebsd_format
f_map=portable_map
f_mount=freebsd_mount
f_close=freebsd_close
user_uid=1001
user_gid=1001
;;
Linux)
f_create=portable_create
f_format=linux_format
f_map=portable_map
f_mount=linux_mount
f_close=portable_close
user_uid=1000
user_gid=1000
;;
*)
>&2 echo "Unsupported system: $system"
exit 1
esac
# cd $ZTMP
}
teardown() {
[ -n "$BATS_TEST_COMPLETED" ] || touch ${BATS_PARENT_TMPNAME}.skip
>&3 echo
}
# usage: echo PASSWORD | portable_create file size pim
portable_create() {
local file="$1" # must not exist
local size="$2" # veracrypt format (accepts M or G)
local pim="$3" # any number
# >&2 echo "portable_create $file $size $pim"
veracrypt --non-interactive --text --stdin \
-m nokernelcrypto \
-c ${file} --volume-type normal \
--hash sha512 --encryption serpent-aes \
--filesystem none --size ${size} --pim ${pim} \
--random-source /dev/urandom -k ''
return $?
}
# usage: echo PASSWORD | portable_map file pim
portable_map() {
local file="$1"
local pim="$2"
# >&2 echo "portable_map $file $pim"
veracrypt --non-interactive --text --stdin \
--protect-hidden no -m nokernelcrypto \
-k '' --pim ${pim} --filesystem none \
${file}
local loop=`veracrypt -l "$file" | awk '{print $3}'`
return $?
}
portable_close() {
local file="$1"
veracrypt -d ${file}
return $?
}
linux_format() {
local file="$1"
# loop="`losetup -j ${vera}/volume | cut -d: -f1`"
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
# losetup -l
>&2 echo "veramap format: ${loop}"
sync
sudo mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "$loop"
return $?
}
# usage: _mount /tmp/.veracrypt_ mountpoint
linux_mount() {
local file="$1"
local mnt="$2"
# local loop="`losetup -j ${vera}/volume | cut -d: -f1`"
local loop=`veracrypt -l "$file" | awk '{print $3}'`
>&3 echo "fsck $loop"
fsck.ext4 -p -C0 "$loop"
sudo mount ${loop} "$mnt"
return $?
}
freebsd_format() {
file="$1"
local loop=`veracrypt -l "$FILE" | awk '{print $3}'`
mkfs.ext4 -E root_owner="${user_uid}:${user_gid}" "${loop}"
return $?
}
freebsd_mount() {
local file="$1"
local mnt="$2"
>&2 echo `veracrypt -l "$file"`
local loop=`veracrypt -l "$file" | awk '{print $3}'`
>&3 echo "fsck $loop"
fsck.ext4 -p -C0 "$loop"
lklfuse -o type=ext4 "${loop}" "$mnt"
return $?
}
freebsd_close() {
local file="$1"
local md=`veracrypt -l "$file" | awk '{print $3}'`
# umount "$mnt"
>&2 echo "md: $md"
local mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
>&2 echo "mnt: $mnt"
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
>&3 echo "kill $lkl (lklfuse on $md)"
# trying to deail with lklfuse bug
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
renice -20 $lkl
kill $lkl
sync
sleep 1
kill -9 $lkl
# lkl should have really exited now
>&3 echo "veracrypt -d $file"
veracrypt -d "$file"
return $?
}
Z() {
tmptmp=0
if ! [ "$TMP" ]; then
TMP=`mktemp -d`; tmptmp=1
fi
script=$1
if [ "$script" == "-" ]; then
cat > $TMP/script_stdin
script=$TMP/script_stdin
else
if ! [ -r "$script" ]; then
script=$T/$script
fi
fi
if ! [ -r $script ]; then
>&2 echo "Error - script not found: $script"
return 1
fi
shift 1
if [ "${1##*.}" == "zen" ]; then
$ZENROOM_EXECUTABLE $@ -z $script
else
$ZENROOM_EXECUTABLE $@ $script
fi
if [ $tmptmp = 1 ]; then
rm -f $TMP/*
rmdir $TMP
fi
}

View File

@ -0,0 +1,26 @@
load bats_setup
@test "Find the tomb" {
assert_file_exists "$TOMB"
>&3 echo "Found: $TOMB"
}
@test "Open tomb with key" {
mkdir -p "$TOMB".mnt
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
}
@test "Check integrity of random data" {
sha512sum "$TOMB".mnt/random.data | awk '{print $1}'
cat "$TOMB".hash
>&2 echo $newhash
>&2 echo $oldhash
assert_equal "$newhash" "$oldhash"
if [ -r "$TOMB".uname ]; then
>&3 cat "$TOMB".uname
fi
}
@test "Close tomb" {
./tomb close "$TOMB"
}

View File

@ -0,0 +1,79 @@
load bats_setup
# f_create=""
# f_format=""
# f_map=""
# f_mount=""
# f_close=""
@test "Create a new tomb" {
# >&3 echo $PW
>&3 echo $TOMB
echo -n "$PW" | $f_create "$TOMB" 20M ${PIM}
}
@test "Map the tomb" {
echo -n "$PW" | $f_map "$TOMB" ${PIM}
}
@test "Format the tomb" {
$f_format "/tmp/.veracrypt_aux_mnt1" || {
$f_close "$TOMB"
return false
}
}
@test "Mount the tomb" {
mkdir -p "$MNT"
$f_mount "$TOMB" "$MNT" || {
$f_close "$TOMB"
return false
}
}
@test "Create random.data inside the tomb" {
dd if=/dev/urandom of="$MNT"/random.data bs=1024 count=10000 || {
# sudo umount testmnt
$f_close "$TOMB"
return false
}
>&3 ls -l "$MNT"/random.data
sha512sum "$MNT"/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
uname -a > "$TOMB".uname
}
@test "Close the test.tomb" {
>&3 veracrypt -l
# sudo umount "$TMP"/testmnt
$f_close "$TOMB"
}
@test "Re-Map the test.tomb" {
echo -n "$PW" | $f_map "$TOMB" ${PIM}
}
@test "Re-Mount the test.tomb" {
mkdir -p "$MNT"
$f_mount "$TOMB" "$MNT" || {
$f_close "$TOMB"
return false
}
}
@test "Re-Check integrity of random data" {
newhash=`sha512sum "$MNT"/random.data | awk '{print $1}'`
oldhash=`cat "$TOMB".hash`
>&2 echo $newhash
>&2 echo $oldhash
assert_equal "$newhash" "$oldhash"
if [ -r "$TOMB".uname ]; then
>&3 cat "$TOMB".uname
fi
}
@test "Re-Close the test.tomb" {
>&3 veracrypt -l
$f_close "$TOMB"
}

View File

@ -0,0 +1,51 @@
load bats_setup
@test "Dig tomb" {
rm -f "$TOMB"
>&3 echo "$TOMB"
./tomb dig -s 20 "$TOMB"
}
@test "Forge key" {
rm -f "$TOMB".key
./tomb forge "$TOMB".key
}
@test "Lock tomb with key" {
./tomb lock -k "$TOMB".key "$TOMB"
}
@test "Open tomb with key" {
mkdir -p "$TOMB".mnt
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
}
@test "Create random.data inside the tomb" {
dd if=/dev/urandom of="$TOMB".mnt/random.data bs=1024 count=10000
>&3 ls -l "$TOMB".mnt/random.data
sha512sum "$TOMB".mnt/random.data | awk '{print $1}' | >&3 tee "$TOMB".hash
uname -a > "$TOMB".uname
}
@test "Close tomb" {
./tomb close "$TOMB"
}
@test "Re-open tomb with key" {
./tomb open -k "$TOMB".key "$TOMB" "$TOMB".mnt
}
@test "Check integrity of random data" {
newhash=`sha512sum "$TOMB".mnt/random.data | awk '{print $1}'`
oldhash=`cat "$TOMB".hash`
>&2 echo $newhash
>&2 echo $oldhash
assert_equal "$newhash" "$oldhash"
if [ -r "$TOMB".uname ]; then
>&3 cat "$TOMB".uname
fi
}
@test "Close the tomb again" {
./tomb close "$TOMB"
}

View File

@ -0,0 +1,33 @@
# bats-assert - Common assertions for Bats
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
# Assertions are functions that perform a test and output relevant
# information on failure to help debugging. They return 1 on failure
# and 0 otherwise.
#
# All output is formatted for readability using the functions of
# `output.bash' and sent to the standard error.
# shellcheck disable=1090
source "$(dirname "${BASH_SOURCE[0]}")/src/assert.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_equal.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_not_equal.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_success.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_failure.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_line.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_line.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/assert_regex.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/refute_regex.bash"

View File

@ -0,0 +1,42 @@
# assert
# ======
#
# Summary: Fail if the given expression evaluates to false.
#
# Usage: assert <expression>
# Options:
# <expression> The expression to evaluate for truthiness.
# *__Note:__ The expression must be a simple command.
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
# such as `[[`, can be used only when executed with `bash -c`.*
#
# IO:
# STDERR - the failed expression, on failure
# Globals:
# none
# Returns:
# 0 - if expression evaluates to true
# 1 - otherwise
#
# ```bash
# @test 'assert()' {
# touch '/var/log/test.log'
# assert [ -e '/var/log/test.log' ]
# }
# ```
#
# On failure, the failed expression is displayed.
#
# ```
# -- assertion failed --
# expression : [ -e /var/log/test.log ]
# --
# ```
assert() {
if ! "$@"; then
batslib_print_kv_single 10 'expression' "$*" \
| batslib_decorate 'assertion failed' \
| fail
fi
}

View File

@ -0,0 +1,42 @@
# assert_equal
# ============
#
# Summary: Fail if the actual and expected values are not equal.
#
# Usage: assert_equal <actual> <expected>
#
# Options:
# <actual> The value being compared.
# <expected> The value to compare against.
#
# ```bash
# @test 'assert_equal()' {
# assert_equal 'have' 'want'
# }
# ```
#
# IO:
# STDERR - expected and actual values, on failure
# Globals:
# none
# Returns:
# 0 - if values equal
# 1 - otherwise
#
# On failure, the expected and actual values are displayed.
#
# ```
# -- values do not equal --
# expected : want
# actual : have
# --
# ```
assert_equal() {
if [[ $1 != "$2" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$2" \
'actual' "$1" \
| batslib_decorate 'values do not equal' \
| fail
fi
}

View File

@ -0,0 +1,78 @@
# assert_failure
# ==============
#
# Summary: Fail if `$status` is 0; or is not equal to the optionally provided status.
#
# Usage: assert_failure [<expected_status>]
#
# Options:
# <expected_status> The specific status code to check against.
# If not provided, simply asserts status is != 0.
#
# IO:
# STDERR - `$output`, on failure;
# - also, `$status` and `expected_status`, if provided
# Globals:
# status
# output
# Returns:
# 0 - if `$status' is 0,
# or if expected_status is provided but does not equal `$status'
# 1 - otherwise
#
# ```bash
# @test 'assert_failure() status only' {
# run echo 'Success!'
# assert_failure
# }
# ```
#
# On failure, `$output` is displayed.
#
# ```
# -- command succeeded, but it was expected to fail --
# output : Success!
# --
# ```
#
# ## Expected status
#
# When `expected_status` is provided, fail if `$status` does not equal the `expected_status`.
#
# ```bash
# @test 'assert_failure() with expected status' {
# run bash -c "echo 'Error!'; exit 1"
# assert_failure 2
# }
# ```
#
# On failure, both the expected and actual statuses, and `$output` are displayed.
#
# ```
# -- command failed as expected, but status differs --
# expected : 2
# actual : 1
# output : Error!
# --
# ```
assert_failure() {
: "${output?}"
: "${status?}"
(( $# > 0 )) && local -r expected="$1"
if (( status == 0 )); then
batslib_print_kv_single_or_multi 6 'output' "$output" \
| batslib_decorate 'command succeeded, but it was expected to fail' \
| fail
elif (( $# > 0 )) && (( status != expected )); then
{ local -ir width=8
batslib_print_kv_single "$width" \
'expected' "$expected" \
'actual' "$status"
batslib_print_kv_single_or_multi "$width" \
'output' "$output"
} \
| batslib_decorate 'command failed as expected, but status differs' \
| fail
fi
}

View File

@ -0,0 +1,248 @@
# assert_line
# ===========
#
# Summary: Fail if the expected line is not found in the output (default) or at a specific line number.
#
# Usage: assert_line [-n index] [-p | -e] [--] <expected>
#
# Options:
# -n, --index <idx> Match the <idx>th line
# -p, --partial Match if `expected` is a substring of `$output` or line <idx>
# -e, --regexp Treat `expected` as an extended regular expression
# <expected> The expected line string, substring, or regular expression
#
# IO:
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# lines
# Returns:
# 0 - if matching line found
# 1 - otherwise
#
# Similarly to `assert_output`, this function verifies that a command or function produces the expected output.
# (It is the logical complement of `refute_line`.)
# It checks that the expected line appears in the output (default) or at a specific line number.
# Matching can be literal (default), partial or regular expression.
#
# *__Warning:__
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
# causing line indices to change and preventing testing for empty lines.*
#
# [bats-93]: https://github.com/sstephenson/bats/pull/93
#
# ## Looking for a line in the output
#
# By default, the entire output is searched for the expected line.
# The assertion fails if the expected line is not found in `${lines[@]}`.
#
# ```bash
# @test 'assert_line() looking for line' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line 'want'
# }
# ```
#
# On failure, the expected line and the output are displayed.
#
# ```
# -- output does not contain line --
# line : want
# output (3 lines):
# have-0
# have-1
# have-2
# --
# ```
#
# ## Matching a specific line
#
# When the `--index <idx>` option is used (`-n <idx>` for short), the expected line is matched only against the line identified by the given index.
# The assertion fails if the expected line does not equal `${lines[<idx>]}`.
#
# ```bash
# @test 'assert_line() specific line' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line --index 1 'want-1'
# }
# ```
#
# On failure, the index and the compared lines are displayed.
#
# ```
# -- line differs --
# index : 1
# expected : want-1
# actual : have-1
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, a match fails if the expected *substring* is not found in the matched line.
#
# ```bash
# @test 'assert_line() partial matching' {
# run echo $'have 1\nhave 2\nhave 3'
# assert_line --partial 'want'
# }
# ```
#
# On failure, the same details are displayed as for literal matching, except that the substring replaces the expected line.
#
# ```
# -- no output line contains substring --
# substring : want
# output (3 lines):
# have 1
# have 2
# have 3
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, a match fails if the *extended regular expression* does not match the line being tested.
#
# *__Note__:
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
#
# ```bash
# @test 'assert_line() regular expression matching' {
# run echo $'have-0\nhave-1\nhave-2'
# assert_line --index 1 --regexp '^want-[0-9]$'
# }
# ```
#
# On failure, the same details are displayed as for literal matching, except that the regular expression replaces the expected line.
#
# ```
# -- regular expression does not match line --
# index : 1
# regexp : ^want-[0-9]$
# line : have-1
# --
# ```
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
assert_line() {
local -i is_match_line=0
local -i is_mode_partial=0
local -i is_mode_regexp=0
: "${lines?}"
# Handle options.
while (( $# > 0 )); do
case "$1" in
-n|--index)
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
echo "\`--index' requires an integer argument: \`$2'" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
is_match_line=1
local -ri idx="$2"
shift 2
;;
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
# Arguments.
local -r expected="$1"
if (( is_mode_regexp == 1 )) && [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_line' \
| fail
return $?
fi
# Matching.
if (( is_match_line )); then
# Specific line.
if (( is_mode_regexp )); then
if ! [[ ${lines[$idx]} =~ $expected ]]; then
batslib_print_kv_single 6 \
'index' "$idx" \
'regexp' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression does not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} != *"$expected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$expected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line does not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} != "$expected" ]]; then
batslib_print_kv_single 8 \
'index' "$idx" \
'expected' "$expected" \
'actual' "${lines[$idx]}" \
| batslib_decorate 'line differs' \
| fail
fi
fi
else
# Contained in output.
if (( is_mode_regexp )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} =~ $expected ]] && return 0
done
{ local -ar single=( 'regexp' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'no output line matches regular expression' \
| fail
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == *"$expected"* ]] && return 0
done
{ local -ar single=( 'substring' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'no output line contains substring' \
| fail
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
[[ ${lines[$idx]} == "$expected" ]] && return 0
done
{ local -ar single=( 'line' "$expected" )
local -ar may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
batslib_print_kv_single_or_multi "$width" "${may_be_multi[@]}"
} \
| batslib_decorate 'output does not contain line' \
| fail
fi
fi
}

View File

@ -0,0 +1,42 @@
# assert_not_equal
# ============
#
# Summary: Fail if the actual and unexpected values are equal.
#
# Usage: assert_not_equal <actual> <unexpected>
#
# Options:
# <actual> The value being compared.
# <unexpected> The value to compare against.
#
# ```bash
# @test 'assert_not_equal()' {
# assert_not_equal 'foo' 'foo'
# }
# ```
#
# IO:
# STDERR - expected and actual values, on failure
# Globals:
# none
# Returns:
# 0 - if actual does not equal unexpected
# 1 - otherwise
#
# On failure, the unexpected and actual values are displayed.
#
# ```
# -- values should not be equal --
# unexpected : foo
# actual : foo
# --
# ```
assert_not_equal() {
if [[ "$1" == "$2" ]]; then
batslib_print_kv_single_or_multi 10 \
'unexpected' "$2" \
'actual' "$1" \
| batslib_decorate 'values should not be equal' \
| fail
fi
}

View File

@ -0,0 +1,197 @@
# assert_output
# =============
#
# Summary: Fail if `$output' does not match the expected output.
#
# Usage: assert_output [-p | -e] [- | [--] <expected>]
#
# Options:
# -p, --partial Match if `expected` is a substring of `$output`
# -e, --regexp Treat `expected` as an extended regular expression
# -, --stdin Read `expected` value from STDIN
# <expected> The expected value, substring or regular expression
#
# IO:
# STDIN - [=$1] expected output
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# Returns:
# 0 - if output matches the expected value/partial/regexp
# 1 - otherwise
#
# This function verifies that a command or function produces the expected output.
# (It is the logical complement of `refute_output`.)
# Output matching can be literal (the default), partial or by regular expression.
# The expected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
#
# ## Literal matching
#
# By default, literal matching is performed.
# The assertion fails if `$output` does not equal the expected output.
#
# ```bash
# @test 'assert_output()' {
# run echo 'have'
# assert_output 'want'
# }
#
# @test 'assert_output() with pipe' {
# run echo 'hello'
# echo 'hello' | assert_output -
# }
#
# @test 'assert_output() with herestring' {
# run echo 'hello'
# assert_output - <<< hello
# }
# ```
#
# On failure, the expected and actual output are displayed.
#
# ```
# -- output differs --
# expected : want
# actual : have
# --
# ```
#
# ## Existence
#
# To assert that any output exists at all, omit the `expected` argument.
#
# ```bash
# @test 'assert_output()' {
# run echo 'have'
# assert_output
# }
# ```
#
# On failure, an error message is displayed.
#
# ```
# -- no output --
# expected non-empty output, but output was empty
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, the assertion fails if the expected _substring_ is not found in `$output`.
#
# ```bash
# @test 'assert_output() partial matching' {
# run echo 'ERROR: no such file or directory'
# assert_output --partial 'SUCCESS'
# }
# ```
#
# On failure, the substring and the output are displayed.
#
# ```
# -- output does not contain substring --
# substring : SUCCESS
# output : ERROR: no such file or directory
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, the assertion fails if the *extended regular expression* does not match `$output`.
#
# *__Note__:
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
# not individual lines.*
#
# ```bash
# @test 'assert_output() regular expression matching' {
# run echo 'Foobar 0.1.0'
# assert_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, the regular expression and the output are displayed.
#
# ```
# -- regular expression does not match output --
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# output : Foobar 0.1.0
# --
# ```
assert_output() {
local -i is_mode_partial=0
local -i is_mode_regexp=0
local -i is_mode_nonempty=0
local -i use_stdin=0
: "${output?}"
# Handle options.
if (( $# == 0 )); then
is_mode_nonempty=1
fi
while (( $# > 0 )); do
case "$1" in
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
-|--stdin) use_stdin=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: assert_output' \
| fail
return $?
fi
# Arguments.
local expected
if (( use_stdin )); then
expected="$(cat -)"
else
expected="${1-}"
fi
# Matching.
if (( is_mode_nonempty )); then
if [ -z "$output" ]; then
echo 'expected non-empty output, but output was empty' \
| batslib_decorate 'no output' \
| fail
fi
elif (( is_mode_regexp )); then
if [[ '' =~ $expected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$expected'" \
| batslib_decorate 'ERROR: assert_output' \
| fail
elif ! [[ $output =~ $expected ]]; then
batslib_print_kv_single_or_multi 6 \
'regexp' "$expected" \
'output' "$output" \
| batslib_decorate 'regular expression does not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output != *"$expected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$expected" \
'output' "$output" \
| batslib_decorate 'output does not contain substring' \
| fail
fi
else
if [[ $output != "$expected" ]]; then
batslib_print_kv_single_or_multi 8 \
'expected' "$expected" \
'actual' "$output" \
| batslib_decorate 'output differs' \
| fail
fi
fi
}

View File

@ -0,0 +1,56 @@
# `assert_regex`
#
# This function is similar to `assert_equal` but uses pattern matching instead
# of equality, by wrapping `[[ value =~ pattern ]]`.
#
# Fail if the value (first parameter) does not match the pattern (second
# parameter).
#
# ```bash
# @test 'assert_regex()' {
# assert_regex 'what' 'x$'
# }
# ```
#
# On failure, the value and the pattern are displayed.
#
# ```
# -- values does not match regular expression --
# value : what
# pattern : x$
# --
# ```
#
# If the value is longer than one line then it is displayed in *multi-line*
# format.
#
# An error is displayed if the specified extended regular expression is invalid.
#
# For description of the matching behavior, refer to the documentation of the
# `=~` operator in the
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
# Note that the `BASH_REMATCH` array is available immediately after the
# assertion succeeds but is fragile, i.e. prone to being overwritten as a side
# effect of other actions.
assert_regex() {
local -r value="${1}"
local -r pattern="${2}"
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
echo "Invalid extended regular expression: \`${pattern}'" \
| batslib_decorate 'ERROR: assert_regex' \
| fail
elif ! [[ "${value}" =~ ${pattern} ]]; then
if shopt -p nocasematch &>/dev/null; then
local case_sensitive=insensitive
else
local case_sensitive=sensitive
fi
batslib_print_kv_single_or_multi 8 \
'value' "${value}" \
'pattern' "${pattern}" \
'case' "${case_sensitive}" \
| batslib_decorate 'value does not match regular expression' \
| fail
fi
}

View File

@ -0,0 +1,44 @@
# assert_success
# ==============
#
# Summary: Fail if `$status` is not 0.
#
# Usage: assert_success
#
# IO:
# STDERR - `$status` and `$output`, on failure
# Globals:
# status
# output
# Returns:
# 0 - if `$status' is 0
# 1 - otherwise
#
# ```bash
# @test 'assert_success() status only' {
# run bash -c "echo 'Error!'; exit 1"
# assert_success
# }
# ```
#
# On failure, `$status` and `$output` are displayed.
#
# ```
# -- command failed --
# status : 1
# output : Error!
# --
# ```
assert_success() {
: "${output?}"
: "${status?}"
if (( status != 0 )); then
{ local -ir width=6
batslib_print_kv_single "$width" 'status' "$status"
batslib_print_kv_single_or_multi "$width" 'output' "$output"
} \
| batslib_decorate 'command failed' \
| fail
fi
}

View File

@ -0,0 +1,42 @@
# refute
# ======
#
# Summary: Fail if the given expression evaluates to true.
#
# Usage: refute <expression>
#
# Options:
# <expression> The expression to evaluate for falsiness.
# *__Note:__ The expression must be a simple command.
# [Compound commands](https://www.gnu.org/software/bash/manual/bash.html#Compound-Commands),
# such as `[[`, can be used only when executed with `bash -c`.*
#
# IO:
# STDERR - the successful expression, on failure
# Globals:
# none
# Returns:
# 0 - if expression evaluates to false
# 1 - otherwise
#
# ```bash
# @test 'refute()' {
# rm -f '/var/log/test.log'
# refute [ -e '/var/log/test.log' ]
# }
# ```
#
# On failure, the successful expression is displayed.
#
# ```
# -- assertion succeeded, but it was expected to fail --
# expression : [ -e /var/log/test.log ]
# --
# ```
refute() {
if "$@"; then
batslib_print_kv_single 10 'expression' "$*" \
| batslib_decorate 'assertion succeeded, but it was expected to fail' \
| fail
fi
}

View File

@ -0,0 +1,271 @@
# refute_line
# ===========
#
# Summary: Fail if the unexpected line is found in the output (default) or at a specific line number.
#
# Usage: refute_line [-n index] [-p | -e] [--] <unexpected>
#
# Options:
# -n, --index <idx> Match the <idx>th line
# -p, --partial Match if `unexpected` is a substring of `$output` or line <idx>
# -e, --regexp Treat `unexpected` as an extended regular expression
# <unexpected> The unexpected line string, substring, or regular expression.
#
# IO:
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# lines
# Returns:
# 0 - if match not found
# 1 - otherwise
#
# Similarly to `refute_output`, this function verifies that a command or function does not produce the unexpected output.
# (It is the logical complement of `assert_line`.)
# It checks that the unexpected line does not appear in the output (default) or at a specific line number.
# Matching can be literal (default), partial or regular expression.
#
# *__Warning:__
# Due to a [bug in Bats][bats-93], empty lines are discarded from `${lines[@]}`,
# causing line indices to change and preventing testing for empty lines.*
#
# [bats-93]: https://github.com/sstephenson/bats/pull/93
#
# ## Looking for a line in the output
#
# By default, the entire output is searched for the unexpected line.
# The assertion fails if the unexpected line is found in `${lines[@]}`.
#
# ```bash
# @test 'refute_line() looking for line' {
# run echo $'have-0\nwant\nhave-2'
# refute_line 'want'
# }
# ```
#
# On failure, the unexpected line, the index of its first match and the output with the matching line highlighted are displayed.
#
# ```
# -- line should not be in output --
# line : want
# index : 1
# output (3 lines):
# have-0
# > want
# have-2
# --
# ```
#
# ## Matching a specific line
#
# When the `--index <idx>` option is used (`-n <idx>` for short), the unexpected line is matched only against the line identified by the given index.
# The assertion fails if the unexpected line equals `${lines[<idx>]}`.
#
# ```bash
# @test 'refute_line() specific line' {
# run echo $'have-0\nwant-1\nhave-2'
# refute_line --index 1 'want-1'
# }
# ```
#
# On failure, the index and the unexpected line are displayed.
#
# ```
# -- line should differ --
# index : 1
# line : want-1
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, a match fails if the unexpected *substring* is found in the matched line.
#
# ```bash
# @test 'refute_line() partial matching' {
# run echo $'have 1\nwant 2\nhave 3'
# refute_line --partial 'want'
# }
# ```
#
# On failure, in addition to the details of literal matching, the substring is also displayed.
# When used with `--index <idx>` the substring replaces the unexpected line.
#
# ```
# -- no line should contain substring --
# substring : want
# index : 1
# output (3 lines):
# have 1
# > want 2
# have 3
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, a match fails if the *extended regular expression* matches the line being tested.
#
# *__Note__:
# As expected, the anchors `^` and `$` bind to the beginning and the end (respectively) of the matched line.*
#
# ```bash
# @test 'refute_line() regular expression matching' {
# run echo $'Foobar v0.1.0\nRelease date: 2015-11-29'
# refute_line --index 0 --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, in addition to the details of literal matching, the regular expression is also displayed.
# When used with `--index <idx>` the regular expression replaces the unexpected line.
#
# ```
# -- regular expression should not match line --
# index : 0
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# line : Foobar v0.1.0
# --
# ```
# FIXME(ztombol): Display `${lines[@]}' instead of `$output'!
refute_line() {
local -i is_match_line=0
local -i is_mode_partial=0
local -i is_mode_regexp=0
: "${lines?}"
# Handle options.
while (( $# > 0 )); do
case "$1" in
-n|--index)
if (( $# < 2 )) || ! [[ $2 =~ ^-?([0-9]|[1-9][0-9]+)$ ]]; then
echo "\`--index' requires an integer argument: \`$2'" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
is_match_line=1
local -ri idx="$2"
shift 2
;;
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
# Arguments.
local -r unexpected="$1"
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_line' \
| fail
return $?
fi
# Matching.
if (( is_match_line )); then
# Specific line.
if (( is_mode_regexp )); then
if [[ ${lines[$idx]} =~ $unexpected ]]; then
batslib_print_kv_single 6 \
'index' "$idx" \
'regexp' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'regular expression should not match line' \
| fail
fi
elif (( is_mode_partial )); then
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
batslib_print_kv_single 9 \
'index' "$idx" \
'substring' "$unexpected" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should not contain substring' \
| fail
fi
else
if [[ ${lines[$idx]} == "$unexpected" ]]; then
batslib_print_kv_single 5 \
'index' "$idx" \
'line' "${lines[$idx]}" \
| batslib_decorate 'line should differ' \
| fail
fi
fi
else
# Line contained in output.
if (( is_mode_regexp )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} =~ $unexpected ]]; then
{ local -ar single=( 'regexp' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'no line should match the regular expression' \
| fail
return $?
fi
done
elif (( is_mode_partial )); then
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == *"$unexpected"* ]]; then
{ local -ar single=( 'substring' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'no line should contain substring' \
| fail
return $?
fi
done
else
local -i idx
for (( idx = 0; idx < ${#lines[@]}; ++idx )); do
if [[ ${lines[$idx]} == "$unexpected" ]]; then
{ local -ar single=( 'line' "$unexpected" 'index' "$idx" )
local -a may_be_multi=( 'output' "$output" )
local -ir width="$( batslib_get_max_single_line_key_width "${single[@]}" "${may_be_multi[@]}" )"
batslib_print_kv_single "$width" "${single[@]}"
if batslib_is_single_line "${may_be_multi[1]}"; then
batslib_print_kv_single "$width" "${may_be_multi[@]}"
else
may_be_multi[1]="$( printf '%s' "${may_be_multi[1]}" | batslib_prefix | batslib_mark '>' "$idx" )"
batslib_print_kv_multi "${may_be_multi[@]}"
fi
} \
| batslib_decorate 'line should not be in output' \
| fail
return $?
fi
done
fi
fi
}

View File

@ -0,0 +1,199 @@
# refute_output
# =============
#
# Summary: Fail if `$output' matches the unexpected output.
#
# Usage: refute_output [-p | -e] [- | [--] <unexpected>]
#
# Options:
# -p, --partial Match if `unexpected` is a substring of `$output`
# -e, --regexp Treat `unexpected` as an extended regular expression
# -, --stdin Read `unexpected` value from STDIN
# <unexpected> The unexpected value, substring, or regular expression
#
# IO:
# STDIN - [=$1] unexpected output
# STDERR - details, on failure
# error message, on error
# Globals:
# output
# Returns:
# 0 - if output matches the unexpected value/partial/regexp
# 1 - otherwise
#
# This function verifies that a command or function does not produce the unexpected output.
# (It is the logical complement of `assert_output`.)
# Output matching can be literal (the default), partial or by regular expression.
# The unexpected output can be specified either by positional argument or read from STDIN by passing the `-`/`--stdin` flag.
#
# ## Literal matching
#
# By default, literal matching is performed.
# The assertion fails if `$output` equals the unexpected output.
#
# ```bash
# @test 'refute_output()' {
# run echo 'want'
# refute_output 'want'
# }
#
# @test 'refute_output() with pipe' {
# run echo 'hello'
# echo 'world' | refute_output -
# }
#
# @test 'refute_output() with herestring' {
# run echo 'hello'
# refute_output - <<< world
# }
# ```
#
# On failure, the output is displayed.
#
# ```
# -- output equals, but it was expected to differ --
# output : want
# --
# ```
#
# ## Existence
#
# To assert that there is no output at all, omit the matching argument.
#
# ```bash
# @test 'refute_output()' {
# run foo --silent
# refute_output
# }
# ```
#
# On failure, an error message is displayed.
#
# ```
# -- unexpected output --
# expected no output, but output was non-empty
# --
# ```
#
# ## Partial matching
#
# Partial matching can be enabled with the `--partial` option (`-p` for short).
# When used, the assertion fails if the unexpected _substring_ is found in `$output`.
#
# ```bash
# @test 'refute_output() partial matching' {
# run echo 'ERROR: no such file or directory'
# refute_output --partial 'ERROR'
# }
# ```
#
# On failure, the substring and the output are displayed.
#
# ```
# -- output should not contain substring --
# substring : ERROR
# output : ERROR: no such file or directory
# --
# ```
#
# ## Regular expression matching
#
# Regular expression matching can be enabled with the `--regexp` option (`-e` for short).
# When used, the assertion fails if the *extended regular expression* matches `$output`.
#
# *__Note__:
# The anchors `^` and `$` bind to the beginning and the end (respectively) of the entire output;
# not individual lines.*
#
# ```bash
# @test 'refute_output() regular expression matching' {
# run echo 'Foobar v0.1.0'
# refute_output --regexp '^Foobar v[0-9]+\.[0-9]+\.[0-9]$'
# }
# ```
#
# On failure, the regular expression and the output are displayed.
#
# ```
# -- regular expression should not match output --
# regexp : ^Foobar v[0-9]+\.[0-9]+\.[0-9]$
# output : Foobar v0.1.0
# --
# ```
refute_output() {
local -i is_mode_partial=0
local -i is_mode_regexp=0
local -i is_mode_empty=0
local -i use_stdin=0
: "${output?}"
# Handle options.
if (( $# == 0 )); then
is_mode_empty=1
fi
while (( $# > 0 )); do
case "$1" in
-p|--partial) is_mode_partial=1; shift ;;
-e|--regexp) is_mode_regexp=1; shift ;;
-|--stdin) use_stdin=1; shift ;;
--) shift; break ;;
*) break ;;
esac
done
if (( is_mode_partial )) && (( is_mode_regexp )); then
echo "\`--partial' and \`--regexp' are mutually exclusive" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Arguments.
local unexpected
if (( use_stdin )); then
unexpected="$(cat -)"
else
unexpected="${1-}"
fi
if (( is_mode_regexp == 1 )) && [[ '' =~ $unexpected ]] || (( $? == 2 )); then
echo "Invalid extended regular expression: \`$unexpected'" \
| batslib_decorate 'ERROR: refute_output' \
| fail
return $?
fi
# Matching.
if (( is_mode_empty )); then
if [ -n "$output" ]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output non-empty, but expected no output' \
| fail
fi
elif (( is_mode_regexp )); then
if [[ $output =~ $unexpected ]]; then
batslib_print_kv_single_or_multi 6 \
'regexp' "$unexpected" \
'output' "$output" \
| batslib_decorate 'regular expression should not match output' \
| fail
fi
elif (( is_mode_partial )); then
if [[ $output == *"$unexpected"* ]]; then
batslib_print_kv_single_or_multi 9 \
'substring' "$unexpected" \
'output' "$output" \
| batslib_decorate 'output should not contain substring' \
| fail
fi
else
if [[ $output == "$unexpected" ]]; then
batslib_print_kv_single_or_multi 6 \
'output' "$output" \
| batslib_decorate 'output equals, but it was expected to differ' \
| fail
fi
fi
}

View File

@ -0,0 +1,66 @@
# `refute_regex`
#
# This function is similar to `refute_equal` but uses pattern matching instead
# of equality, by wrapping `! [[ value =~ pattern ]]`.
#
# Fail if the value (first parameter) matches the pattern (second parameter).
#
# ```bash
# @test 'refute_regex()' {
# refute_regex 'WhatsApp' 'Threema'
# }
# ```
#
# On failure, the value, the pattern and the match are displayed.
#
# ```
# @test 'refute_regex()' {
# refute_regex 'WhatsApp' 'What.'
# }
#
# -- value matches regular expression --
# value : WhatsApp
# pattern : What.
# match : Whats
# case : sensitive
# --
# ```
#
# If the value or pattern is longer than one line then it is displayed in
# *multi-line* format.
#
# An error is displayed if the specified extended regular expression is invalid.
#
# For description of the matching behavior, refer to the documentation of the
# `=~` operator in the
# [Bash manual]: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html.
#
# Note that the `BASH_REMATCH` array is available immediately after the
# assertion fails but is fragile, i.e. prone to being overwritten as a side
# effect of other actions like calling `run`. Thus, it's good practice to avoid
# using `BASH_REMATCH` in conjunction with `refute_regex()`. The valuable
# information the array contains is the matching part of the value which is
# printed in the failing test log, as mentioned above.
refute_regex() {
local -r value="${1}"
local -r pattern="${2}"
if [[ '' =~ ${pattern} ]] || (( ${?} == 2 )); then
echo "Invalid extended regular expression: \`${pattern}'" \
| batslib_decorate 'ERROR: refute_regex' \
| fail
elif [[ "${value}" =~ ${pattern} ]]; then
if shopt -p nocasematch &>/dev/null; then
local case_sensitive=insensitive
else
local case_sensitive=sensitive
fi
batslib_print_kv_single_or_multi 8 \
'value' "${value}" \
'pattern' "${pattern}" \
'match' "${BASH_REMATCH[0]}" \
'case' "${case_sensitive}" \
| batslib_decorate 'value matches regular expression' \
| fail
fi
}

View File

@ -0,0 +1,2 @@
source "$(dirname "${BASH_SOURCE[0]}")/src/file.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/temp.bash"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
#
# bats-file - Common filesystem assertions and helpers for Bats
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
#
# temp.bash
# ---------
#
# Functions for handling temporary directories.
#
# Create a temporary directory for the current test in `BATS_TMPDIR`,
# and display its path on the standard output.
#
# The directory name is derived from the test's filename and number, and
# a random string for uniqueness.
#
# <test-filename>-<test-number>-<random-string>
#
# When `--prefix <prefix>' is specified, `<prefix>' is prepended to the
# directory name.
#
# <prefix><test-filename>-<test-number>-<random-string>
#
# Must be called from `setup', `@test' or `teardown'.
#
# Example:
#
# setup() {
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
# }
#
# teardown() {
# temp_del "$TEST_TEMP_DIR"
# }
#
# Globals:
# BATS_TEST_NAME
# BATS_TEST_FILENAME
# BATS_TEST_NUMBER
# BATS_TMPDIR
# Arguments:
# none
# Options:
# -p, --prefix <prefix> - prefix the directory name with `<prefix>'
# Returns:
# 0 - on success
# 1 - otherwise
# Outputs:
# STDOUT - path of temporary directory
# STDERR - error messages
temp_make() {
# Check caller.
if ! ( batslib_is_caller --indirect 'setup' \
|| batslib_is_caller --indirect 'setup_file' \
|| batslib_is_caller --indirect "$BATS_TEST_NAME" \
|| batslib_is_caller --indirect 'teardown' \
|| batslib_is_caller --indirect 'teardown_file' )
then
echo "Must be called from \`setup', \`@test' or \`teardown'" \
| batslib_decorate 'ERROR: temp_make' \
| fail
return $?
fi
# Handle options.
local prefix=''
while (( $# > 0 )); do
case "$1" in
-p|--prefix)
if (( $# < 2 )); then
echo "\`--prefix' requires an argument" \
| batslib_decorate 'ERROR: temp_make' \
| fail
return $?
fi
prefix="$2"
shift 2
;;
--) shift; break ;;
*) break ;;
esac
done
# Create directory.
local template="$prefix"
template+="${BATS_TEST_FILENAME##*/}"
template+="-${BATS_TEST_NUMBER}"
template+='-XXXXXXXXXX'
local path
path="$(mktemp -d -- "${BATS_TMPDIR}/${template}" 2>&1)"
if (( $? )); then
echo "$path" \
| batslib_decorate 'ERROR: temp_make' \
| fail
return $?
fi
echo "$path"
}
# Delete a temporary directory, typically created with `temp_make', and
# its contents.
#
# Note: Actually, this function can be used to delete any file or
# directory. However, it is most useful in deleting temporary
# directories created with `temp_make', hence the naming.
#
# For development and debugging, deletion can be prevented using
# environment variables.
#
# When `BATSLIB_TEMP_PRESERVE' is set to 1, the function succeeds but
# the directory is not deleted.
#
# When `BATSLIB_TEMP_PRESERVE_ON_FAILURE' is set to 1 and `temp_del' is
# called, directly or indirectly, from `teardown', the function succeeds
# but the directory is not deleted if the test has failed.
#
# Example:
#
# setup() {
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
# }
#
# teardown() {
# temp_del "$TEST_TEMP_DIR"
# }
#
# Globals:
# BATSLIB_TEMP_PRESERVE
# BATSLIB_TEMP_PRESERVE_ON_FAILURE
# BATS_TEST_COMPLETED
# Arguments:
# $1 - path of directory
# Returns:
# 0 - on success
# 1 - otherwise
# Outputs:
# STDERR - error messages
temp_del() {
local -r path="$1"
# Environment variables.
if [[ ${BATSLIB_TEMP_PRESERVE-} == '1' ]]; then
return 0
elif [[ ${BATSLIB_TEMP_PRESERVE_ON_FAILURE-} == '1' ]]; then
# Check caller.
if ! ( batslib_is_caller --indirect 'teardown' \
|| batslib_is_caller --indirect 'teardown_file' )
then
echo "Must be called from \`teardown' or \`teardown_file' when using \`BATSLIB_TEMP_PRESERVE_ON_FAILURE'" \
| batslib_decorate 'ERROR: temp_del' \
| fail
return $?
fi
(( ${BATS_TEST_COMPLETED:-0} != 1 )) && return 0
fi
# Delete directory.
local result
result="$(rm -r -- "$path" 2>&1 </dev/null)"
if (( $? )); then
echo "$result" \
| batslib_decorate 'ERROR: temp_del' \
| fail
return $?
fi
}

View File

@ -0,0 +1,3 @@
source "$(dirname "${BASH_SOURCE[0]}")/src/output.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/error.bash"
source "$(dirname "${BASH_SOURCE[0]}")/src/lang.bash"

View File

@ -0,0 +1,41 @@
#
# bats-support - Supporting library for Bats test helpers
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
#
# error.bash
# ----------
#
# Functions implementing error reporting. Used by public helper
# functions or test suits directly.
#
# Fail and display a message. When no parameters are specified, the
# message is read from the standard input. Other functions use this to
# report failure.
#
# Globals:
# none
# Arguments:
# $@ - [=STDIN] message
# Returns:
# 1 - always
# Inputs:
# STDIN - [=$@] message
# Outputs:
# STDERR - message
fail() {
(( $# == 0 )) && batslib_err || batslib_err "$@"
return 1
}

View File

@ -0,0 +1,73 @@
#
# bats-util - Various auxiliary functions for Bats
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
#
# lang.bash
# ---------
#
# Bash language and execution related functions. Used by public helper
# functions.
#
# Check whether the calling function was called from a given function.
#
# By default, direct invocation is checked. The function succeeds if the
# calling function was called directly from the given function. In other
# words, if the given function is the next element on the call stack.
#
# When `--indirect' is specified, indirect invocation is checked. The
# function succeeds if the calling function was called from the given
# function with any number of intermediate calls. In other words, if the
# given function can be found somewhere on the call stack.
#
# Direct invocation is a form of indirect invocation with zero
# intermediate calls.
#
# Globals:
# FUNCNAME
# Options:
# -i, --indirect - check indirect invocation
# Arguments:
# $1 - calling function's name
# Returns:
# 0 - current function was called from the given function
# 1 - otherwise
batslib_is_caller() {
local -i is_mode_direct=1
# Handle options.
while (( $# > 0 )); do
case "$1" in
-i|--indirect) is_mode_direct=0; shift ;;
--) shift; break ;;
*) break ;;
esac
done
# Arguments.
local -r func="$1"
# Check call stack.
if (( is_mode_direct )); then
[[ $func == "${FUNCNAME[2]}" ]] && return 0
else
local -i depth
for (( depth=2; depth<${#FUNCNAME[@]}; ++depth )); do
[[ $func == "${FUNCNAME[$depth]}" ]] && return 0
done
fi
return 1
}

View File

@ -0,0 +1,279 @@
#
# bats-support - Supporting library for Bats test helpers
#
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
#
# output.bash
# -----------
#
# Private functions implementing output formatting. Used by public
# helper functions.
#
# Print a message to the standard error. When no parameters are
# specified, the message is read from the standard input.
#
# Globals:
# none
# Arguments:
# $@ - [=STDIN] message
# Returns:
# none
# Inputs:
# STDIN - [=$@] message
# Outputs:
# STDERR - message
batslib_err() {
{ if (( $# > 0 )); then
echo "$@"
else
cat -
fi
} >&2
}
# Count the number of lines in the given string.
#
# TODO(ztombol): Fix tests and remove this note after #93 is resolved!
# NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
# give the same result as `${#lines[@]}' when the output contains
# empty lines.
# See PR #93 (https://github.com/sstephenson/bats/pull/93).
#
# Globals:
# none
# Arguments:
# $1 - string
# Returns:
# none
# Outputs:
# STDOUT - number of lines
batslib_count_lines() {
local -i n_lines=0
local line
while IFS='' read -r line || [[ -n $line ]]; do
(( ++n_lines ))
done < <(printf '%s' "$1")
echo "$n_lines"
}
# Determine whether all strings are single-line.
#
# Globals:
# none
# Arguments:
# $@ - strings
# Returns:
# 0 - all strings are single-line
# 1 - otherwise
batslib_is_single_line() {
for string in "$@"; do
(( $(batslib_count_lines "$string") > 1 )) && return 1
done
return 0
}
# Determine the length of the longest key that has a single-line value.
#
# This function is useful in determining the correct width of the key
# column in two-column format when some keys may have multi-line values
# and thus should be excluded.
#
# Globals:
# none
# Arguments:
# $odd - key
# $even - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - length of longest key
batslib_get_max_single_line_key_width() {
local -i max_len=-1
while (( $# != 0 )); do
local -i key_len="${#1}"
batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
shift 2
done
echo "$max_len"
}
# Print key-value pairs in two-column format.
#
# Keys are displayed in the first column, and their corresponding values
# in the second. To evenly line up values, the key column is fixed-width
# and its width is specified with the first parameter (possibly computed
# using `batslib_get_max_single_line_key_width').
#
# Globals:
# none
# Arguments:
# $1 - width of key column
# $even - key
# $odd - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_single() {
local -ir col_width="$1"; shift
while (( $# != 0 )); do
printf '%-*s : %s\n' "$col_width" "$1" "$2"
shift 2
done
}
# Print key-value pairs in multi-line format.
#
# The key is displayed first with the number of lines of its
# corresponding value in parenthesis. Next, starting on the next line,
# the value is displayed. For better readability, it is recommended to
# indent values using `batslib_prefix'.
#
# Globals:
# none
# Arguments:
# $odd - key
# $even - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_multi() {
while (( $# != 0 )); do
printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
printf '%s\n' "$2"
shift 2
done
}
# Print all key-value pairs in either two-column or multi-line format
# depending on whether all values are single-line.
#
# If all values are single-line, print all pairs in two-column format
# with the specified key column width (identical to using
# `batslib_print_kv_single').
#
# Otherwise, print all pairs in multi-line format after indenting values
# with two spaces for readability (identical to using `batslib_prefix'
# and `batslib_print_kv_multi')
#
# Globals:
# none
# Arguments:
# $1 - width of key column (for two-column format)
# $even - key
# $odd - value of the previous key
# Returns:
# none
# Outputs:
# STDOUT - formatted key-value pairs
batslib_print_kv_single_or_multi() {
local -ir width="$1"; shift
local -a pairs=( "$@" )
local -a values=()
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
values+=( "${pairs[$i]}" )
done
if batslib_is_single_line "${values[@]}"; then
batslib_print_kv_single "$width" "${pairs[@]}"
else
local -i i
for (( i=1; i < ${#pairs[@]}; i+=2 )); do
pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
done
batslib_print_kv_multi "${pairs[@]}"
fi
}
# Prefix each line read from the standard input with the given string.
#
# Globals:
# none
# Arguments:
# $1 - [= ] prefix string
# Returns:
# none
# Inputs:
# STDIN - lines
# Outputs:
# STDOUT - prefixed lines
batslib_prefix() {
local -r prefix="${1:- }"
local line
while IFS='' read -r line || [[ -n $line ]]; do
printf '%s%s\n' "$prefix" "$line"
done
}
# Mark select lines of the text read from the standard input by
# overwriting their beginning with the given string.
#
# Usually the input is indented by a few spaces using `batslib_prefix'
# first.
#
# Globals:
# none
# Arguments:
# $1 - marking string
# $@ - indices (zero-based) of lines to mark
# Returns:
# none
# Inputs:
# STDIN - lines
# Outputs:
# STDOUT - lines after marking
batslib_mark() {
local -r symbol="$1"; shift
# Sort line numbers.
set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
local line
local -i idx=0
while IFS='' read -r line || [[ -n $line ]]; do
if (( ${1:--1} == idx )); then
printf '%s\n' "${symbol}${line:${#symbol}}"
shift
else
printf '%s\n' "$line"
fi
(( ++idx ))
done
}
# Enclose the input text in header and footer lines.
#
# The header contains the given string as title. The output is preceded
# and followed by an additional newline to make it stand out more.
#
# Globals:
# none
# Arguments:
# $1 - title
# Returns:
# none
# Inputs:
# STDIN - text
# Outputs:
# STDOUT - decorated text
batslib_decorate() {
echo
echo "-- $1 --"
cat -
echo '--'
echo
}

403
extras/portable/tomb Executable file
View File

@ -0,0 +1,403 @@
#!/bin/sh
#
# Tomb v3, the Crypto Undertaker portable version 3
#
# A commandline tool to easily operate encryption of secret data
#
# {{{ License
# Copyright (C) 2007-2022 Dyne.org Foundation
#
# Tomb is designed, written and maintained by Denis Roio <jaromil@dyne.org>
#
# Please refer to the AUTHORS file for more information.
#
# This source code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This source code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please refer
# to the GNU Public License for more details.
#
# You should have received a copy of the GNU Public License along with
# this source code; if not, , see <https://www.gnu.org/licenses/>.
# }}} - License
# {{{ Global variables
VERSION="3.1.0"
DATE="Nov/2022"
TOMBEXEC="$0"
TMPDIR="${TMP:-/tmp}"
TOMBTMPFILES=""
PATH="${PATH}:.:/usr/local/bin"
exitcode=0
# }}}
# {{{ Logging functions
_success() {
echo "[*] " "$1" "$2" "$3" "$4" 1>&2
}
_message() {
echo " . " "$1" "$2" "$3" "$4" 1>&2
}
_warning() {
echo "[W] " "$1" "$2" "$3" "$4" 1>&2
}
_verbose() {
echo "[D] " "$1" "$2" "$3" "$4" 1>&2
}
_error() {
echo "[!] " "$1" "$2" "$3" "$4" 1>&2
exitcode=1
}
_failure() {
echo "[!!] " "$1" "$2" "$3" "$4" 1>&2
exitcode=1
exit 1
}
# }}}
_success "Starting Tomb v$VERSION"
# {{{ Internal functions
_random_string() {
len=${1:-32}
[ "$1" != "" ] && {
echo "print(O.random($len):base58())" | zenroom 2>/dev/null
}
return $?
}
_tmp_create() {
[ -d "$TMPDIR" ] || {
# we create the tempdir with the sticky bit on
mkdir -m 1777 "$TMPDIR"
[ $? = 0 ] || {
_failure "Fatal error creating the temporary directory: %s" "$TMPDIR"
}
}
tfile="${TMPDIR}/`_random_string 64`" # Temporary file
umask 066
[ $? = 0 ] || {
_failure "Fatal error setting the permission umask for temporary files"
}
[ -r "$tfile" ] && {
_failure "Someone is messing up with us trying to hijack temporary files."
}
touch "$tfile"
[ $? = 0 ] || {
_failure "Fatal error creating a temporary file: %s" "$tfile"
}
_verbose "Created tempfile: ::1 temp file::" "$tfile"
TOMBTMP="$tfile"
TOMBTMPFILES="$TOMBTMPFILES:$tfile"
return 0
}
# }}}
# {{{ FreeBSD
freebsd_mount() {
file="$1"
mnt="$2"
_verbose `veracrypt -l "$file"`
loop=`veracrypt -l "$file" | awk '{print $3}'`
_verbose "fsck $loop"
fsck.ext4 -p -C0 "$loop"
lklfuse -o type=ext4 "${loop}" "$mnt"
return $?
}
freebsd_close() {
file="$1"
md=`veracrypt -l "$file" | awk '{print $3}'`
# umount "$mnt"
_verbose "md: $md"
mnt=`pgrep -lf "lklfuse.*$md" | awk '{print $6}'`
_verbose "mnt: $mnt"
lkl=`pgrep -f "lklfuse.*$md" | awk '{print $1}'`
_verbose "kill $lkl (lklfuse on $md)"
# trying to deail with lklfuse bug
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=239831
renice -20 $lkl
kill $lkl
sync
sleep 1
kill -9 $lkl
# lkl should have really exited now
_verbose "veracrypt -d $file"
veracrypt --text --non-interactive -d "$file"
return $?
}
# }}}
# {{{ Linux
# usage: _mount /tmp/.veracrypt_ mountpoint
linux_mount() {
file="$1"
mnt="$2"
veralist=`veracrypt -l "$file" | awk '{print($2,":",$3,":",$4)}'`
[ "$veralist" = "" ] && {
_error "Cannot mount tomb not yet mapped " "$file"
return 1
}
loop=`echo $veralist | cut -d: -f2 | xargs`
[ "`echo $veralist | cut -d: -f3 | xargs`" != "-" ] && {
_error "Tomb already mounted " "$file on $loop"
return 1
}
_verbose "fsck $loop"
fsck.ext4 -p -C0 "$loop" 1>&2
_verbose "linux_mount $mnt"
mount "$loop" "$mnt"
return $?
}
# }}}
# {{{ POSIX portable
# usage: echo PASSWORD | posix_create file size pim
posix_create() {
file="$1" # must not exist
size="$2" # size in bytes
pim="$3" # any number
_verbose "posix_create $file $size $pim"
veracrypt --text --non-interactive --stdin \
-m nokernelcrypto \
-c "$file" --volume-type normal \
--hash sha512 --encryption serpent-aes \
--filesystem none --size "${size}" --pim "$pim" \
--random-source /dev/urandom -k ''
return $?
}
posix_format() {
file="$1"
loop=`veracrypt -l "$file" | awk '{print $3}'`
_verbose "posix_format: ${loop}"
mkfs.ext4 -L "`basename $file`" "$loop" # -E root_owner="${user_uid}:${user_gid}" "$loop"
return $?
}
# usage: echo PASSWORD | posix_map file pim
posix_map() {
file="$1"
pim="$2"
_verbose "posix_map $file $pim"
veracrypt --text --non-interactive --stdin \
--protect-hidden no -m nokernelcrypto \
-k '' --pim "$pim" --filesystem none \
"$file"
return $?
}
posix_close() {
file="$1"
_verbose "posix_close $file"
veracrypt --text --non-interactive -d "$file"
return $?
}
# }}}
# {{{ Initialization
system="unknown"
create=""
format=""
map=""
mount=""
close=""
tomb_init() {
system="`uname -s`"
case "$system" in
FreeBSD)
cat <<EOF
create=posix_create
format=posix_format
map=posix_map
mount=freebsd_mount
close=freebsd_close
EOF
;;
Linux)
cat <<EOF
create=posix_create
format=posix_format
map=posix_map
mount=linux_mount
close=posix_close
EOF
;;
*)
_failure "Unsupported system: %s" "$system"
esac
echo "system=$system"
# _verbose "system detected: %s" $system
}
# }}}
# {{{ Main
[ "$1" = "source" ] && return 0
eval "`tomb_init`"
tombfile=""
tombsize=""
PIM=69
cmd="$1"
shift 1
case "$cmd" in
dig)
args=2
while [ $args -gt 0 ]; do
[ "$1" = '-s' ] && { tombsize="$2"; shift 2; }
[ "$1" != "" ] && { tombfile="$1"; shift 1; }
args=$(( $args - 1 ))
done
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
[ "$tombsize" = "" ] && _failure "Size argument missing, use -s"
# TODO: check if size is integer
# [ "$tombsize" -ge 10 ] || _failure "Tombs can't be smaller than 10 mebibytes"
[ -e "$tombfile" ] && {
_error "A tomb exists already. I'm not digging here:"
ls -l "$tombfile"; exit 1
}
_message "Commanded to dig tomb " "$tombfile" " sized $tombsize MiB"
touch "$tombfile" || _failure "Error creating the tomb " "$tombfile"
# dd if=/dev/urandom bs=1048576 count=$tombsize of="$tombfile" || {
# _failure "Error creating the tomb " "$tombfile"
# exit 1
# }
bytesize="$(( $tombsize * 1048576 ))"
[ "$bytesize" = "" ] &&
_failure "Error reading tomb size " "$tombsize"
if [ "$system" = "Linux" ]; then
fallocate -x -l "$bytesize" "$tombfile" ||
_failure "Error creating the tomb " "$tombfile"
else
dd if=/dev/zero of="$tombfile" count="$tombsize" bs=1048576 ||
_failure "Error creating the tomb " "$tombfile of $tombsize MiB"
fi
;;
forge)
args=1
[ "$1" = "" ] && _failure "Missing argument in command " "forge"
while [ $args -gt 0 ]; do
case "$1" in
'-k') tombkey="$2"; shift 2 ;;
*) tombkey="$1"; shift 1 ;;
esac
args=$(( $args - 1 ))
done
[ "$tombkey" = "" ] && _failure "Missing path to tomb key"
[ -e "$tombkey" ] && {
_error "A tomb key exists already. I'm not forging here:"
ls -l "$tombkey"; exit 1
}
_message "Commanded to forge tomb key " "$tombkey"
touch "$tombkey" || _failure "Error creating the tomb key " "$tombkey"
cat <<EOF | zenroom -z | cut -d\" -f4 > "$tombkey"
rule check version 2.0.0
Given nothing
When I create the random object of '64' bytes
Then print the 'random object' as 'hex'
EOF
;;
lock)
args=2
while [ $args -gt 0 ]; do
case "$1" in
'-k') tombkey="$2"; shift 2 ;;
*) tombfile="$1"; shift 1 ;;
esac
args=$(( $args - 1 ))
done
case "$system" in
Linux)
bytesize=`stat "$tombfile" | awk '/Size:/ {print $2; exit}'`
;;
*)
bytesize=`stat -f '%z' "$tombfile"`
;;
esac
_message "Commanded to lock tomb " "$tombfile with key $tombkey"
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
[ -r "$tombfile" ] || _failure "Tomb file not readable"
[ "$tombkey" = "" ] && _failure "Missing path to key"
[ -r "$tombkey" ] || _failure "Key file not readable"
"$create" "$tombfile" "$bytesize" "$PIM" < "$tombkey" || {
_failure "Error creating the tomb " "$tombfile"
exit 1
}
"$map" "$tombfile" "$PIM" < "$tombkey" ||
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
"$format" "$tombfile" ||
_error "Error formatting tomb " "$tombfile"
"$close" "$tombfile"
[ $exitcode = 0 ] &&
_success "Success locking tomb " "$tombfile with key $tombkey"
;;
open)
args=3
while [ $args -gt 0 ]; do
case "$1" in
'-k') tombkey="$2"; shift 2 ;;
*) if [ "$tombfile" = "" ]; then
tombfile="$1"
else
tombmount="$1"
fi
shift 1 ;;
esac
args=$(( $args - 1 ))
done
_message "Commanded to open tomb " "$tombfile with key $tombkey on $tombmount"
[ "$tombfile" = "" ] && _failure "Missing path to tomb"
[ -r "$tombfile" ] || _failure "Tomb file not readable"
[ "$tombkey" = "" ] && _failure "Missing path to key"
[ -r "$tombkey" ] || _failure "Key file not readable"
[ "$tombmount" = "" ] && tombmount="`basename $tombfile`"
"$map" "$tombfile" "$PIM" < "$tombkey"
[ $? != 0 ] &&
_failure "Error mapping the tomb " "$tombfile with key $tombkey"
"$mount" "$tombfile" "$tombmount" || {
"$close" "$tombfile"
_failure "Error mounting the tomb " "$tombfile on $tombmount"
}
;;
close)
# args=1
if [ "$1" = "all" ]; then
"$close"
elif [ -e "$1" ]; then
"$close" "`realpath $1`" ||
_failure "Error closing " "$1"
elif [ "$1" = "" ]; then
_failure "Missing argument: tomb file or mountpoint"
else
_failure "Wrong argument: $1"
fi
;;
list)
veracrypt -l
;;
esac
# }}}
exit 0

View File

@ -3,3 +3,9 @@ Build with 'qmake' and then 'make'
Requires QT version 5.4 or above
To install copy the binary in your PATH
then all contents of pixmaps to /usr/share/pixmaps
and all contents of i18n to /usr/share/i18n
The prefix /usr/local may be preferred if ever packaged.

2
extras/test/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
gnupg/
test-results/

42
extras/test/00_create.sh Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env zsh
export test_description="Testing tomb creation"
source ./setup
test_cleanup
test_export "test"
test_expect_success 'Testing tomb creation: dig, forge and lock' '
tt_dig -s 20 &&
tt_forge --tomb-pwd $DUMMYPASS &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| xxd &&
tt_lock --tomb-pwd $DUMMYPASS
'
if test_have_prereq SPHINX ORACLE; then
test_export "sphinx_test"
test_expect_success 'Testing tomb creation: dig, forge and lock (sphinx password handling)' '
tt_dig -s 20 &&
tt_forge --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
print $(echo $DUMMYPASS | sphinx get $DUMMYUSER $DUMMYHOST) \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| xxd &&
tt_lock --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST
'
fi
if test_have_prereq DOAS; then
test_export "doas_test"
test_expect_success 'Testing tomb creation: dig, forge and lock (using doas instead of sudo)' '
tt_dig --sudo doas -s 20 &&
tt_forge --sudo doas --tomb-pwd $DUMMYPASS &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| xxd &&
tt_lock --sudo doas --tomb-pwd $DUMMYPASS
'
fi
test_done

View File

@ -0,0 +1,63 @@
#!/usr/bin/env zsh
export test_description="Testing common operations on tomb"
source ./setup
_getaccess() { stat --format=%X "$1"; }
_getmodif() { stat --format=%Y "$1"; }
test_export "test" # Using already generated tomb
test_expect_success 'Testing open with wrong password ' '
test_must_fail tt_open --tomb-pwd wrongpassword
'
test_expect_success 'Testing open with good password ' '
tt_open --tomb-pwd $DUMMYPASS &&
tt_close
'
test_expect_success 'Testing open in read only mode' '
chmod -w $tomb &&
tt_open --tomb-pwd $DUMMYPASS -o ro,noatime,nodev &&
tt_close &&
chmod +w $tomb
'
test_expect_success 'Testing tomb files stat restoration' '
access=$(_getaccess $tomb_key) &&
modif=$(_getmodif $tomb_key) &&
tt_open --tomb-pwd $DUMMYPASS &&
tt_close &&
[[ "$access" == "$(_getaccess $tomb_key)" ]] &&
[[ "$modif" == "$(_getmodif $tomb_key)" ]]
'
if test_have_prereq LSOF; then
gcc -Wall -o $TMP/close_block $TEST_HOME/close_block.c
test_expect_success 'Testing functionality of the slam operation (use of lsof)' '
mkdir $TMP/testmount &&
tt_open $TMP/testmount --tomb-pwd $DUMMYPASS &&
tt_set_ownership $TMP/testmount &&
$TMP/close_block $TMP/testmount/occupied 20 &
tt slam
'
fi
if test_have_prereq SPHINX ORACLE; then
test_export "sphinx_test" # Using already generated tomb
test_expect_success 'Testing open with good password (sphinx)' '
tt_open --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
tt_close
'
fi
if test_have_prereq DOAS; then
test_export "doas_test" # Using already generated tomb
test_expect_success 'Testing open with good password (using doas instead of sudo)' '
tt_open --sudo doas --tomb-pwd $DUMMYPASS &&
tt_close --sudo doas
'
fi
test_done

View File

@ -0,0 +1,61 @@
#!/usr/bin/env zsh
export test_description="Testing tomb with GnuPG keys"
source ./setup
if test_have_prereq GPGRCPT; then
test_export "recipient"
test_expect_success 'Testing tomb with GnuPG keys: creation' '
tt_dig -s 20 &&
tt_forge -g -r $KEY1 &&
tt_lock -g -r $KEY1
'
test_expect_success 'Testing tomb with GnuPG keys: open & close' '
tt_open -g &&
tt_close
'
test_export "default"
test_expect_success 'Testing tomb with GnuPG keys using the default recipient' '
tt_dig -s 20 &&
tt_forge -g &&
tt_lock -g &&
gpg -d --status-fd 2 $tomb_key 1> /dev/null 2> $TMP/default.tmp &&
[[ ! -z "$(grep "Tomb Test 2" $TMP/default.tmp)" ]]
'
test_export "hidden"
test_expect_success 'Testing tomb with GnuPG keys using hidden recipient' '
tt_dig -s 20 &&
tt_forge -g -R $KEY1 &&
tt_lock -g -R $KEY1
'
test_export "subkeys"
test_expect_success 'Testing tomb with GnuPG subkeys' '
tt_dig -s 20 &&
tt_forge -g -R $SUBKEY2 &&
tt_lock -g -R $SUBKEY2
'
test_export "shared"
test_expect_success 'Testing tomb with GnuPG keys and shared tomb' '
tt_dig -s 20 &&
tt_forge -g -r $KEY1,$KEY2 &&
tt_lock -g -r $KEY1 &&
tt_open -g &&
tt_close
'
test_export "untrusted"
test_expect_success 'Testing tomb creation with untrusted GnuPG keys' '
tt_dig -s 20 &&
test_must_fail tt_forge -g -r $KEY_UNTRUSTED
'
fi
test_done

31
extras/test/30_kdf.sh Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env zsh
export test_description="Testing tomb with KDF key"
source ./setup
if test_have_prereq KDF; then
test_export "kdf"
test_expect_success 'Testing KDF: tomb creation' '
tt_dig -s 20 &&
tt_forge --tomb-pwd $DUMMYPASS --kdf 1 &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key \
| xxd &&
tt_lock --tomb-pwd $DUMMYPASS --kdf 1
'
test_expect_success 'Testing KDF: tomb passwd' '
tt passwd -k $tomb_key --kdf 1 \
--unsafe --tomb-old-pwd $DUMMYPASS --tomb-pwd $DUMMYPASSNEW &&
tt passwd -k $tomb_key --kdf 1 \
--unsafe --tomb-old-pwd $DUMMYPASSNEW --tomb-pwd $DUMMYPASS
'
test_expect_success 'Testing KDF: tomb open & close' '
tt_open --tomb-pwd $DUMMYPASS --kdf 1 &&
tt_close
'
fi
test_done

View File

@ -0,0 +1,40 @@
#!/usr/bin/env zsh
export test_description="Tomb regression tests"
source ./setup
autoload -U is-at-least
TOMB_VERSION=("2.4" "2.3" "2.2" "2.0.1" "2.1")
zshversion=$(zsh --version | awk 'NR==1 {print $2}')
{ is-at-least "5.3" $zshversion } && TOMB_VERSION=("2.3")
for version in "${TOMB_VERSION[@]}"; do
URL="https://files.dyne.org/tomb/old-releases/Tomb-$version.tar.gz"
curl "$URL" > "$TMP/tomb-regression.tar.gz"
mkdir -p "$TMP/tomb-regression"
tar xfz "$TMP/tomb-regression.tar.gz" \
--strip-components 1 -C "$TMP/tomb-regression"
T="$TMP/tomb-regression/tomb"
[[ "$version" == "$(${T} -v |& awk 'NR==1 {print $3}')" ]] || continue
test_export "regression_$version"
test_expect_success "Regression tests: opening old tomb ($version) with Tomb" "
tt_dig -s 20 &&
tt_forge --tomb-pwd $DUMMYPASS &&
tt_lock --tomb-pwd $DUMMYPASS &&
T='$TOMB_BIN' &&
tt_open --tomb-pwd $DUMMYPASS &&
tt_close
"
test_export "test" # Using already generated tomb
test_expect_success "Regression tests: opening new tomb with Tomb $version" "
export T='$TMP/tomb-regression/tomb' &&
tt_open --tomb-pwd $DUMMYPASS &&
tt_close
"
done
test_done

View File

@ -0,0 +1,20 @@
#!/usr/bin/env zsh
export test_description="Testing file integrity"
source ./setup
test_export "test" # Using already generated tomb
test_expect_success 'Testing contents integrity' '
tt_open --tomb-pwd $DUMMYPASS &&
tt_set_ownership "$MEDIA/$testname" &&
tt dig -s 10 "$MEDIA/$testname/datacheck.raw" &&
CRC1=$(sha256sum "$MEDIA/$testname/datacheck.raw") &&
tt_close --unsafe &&
tt_open --tomb-pwd $DUMMYPASS &&
CRC2=$(sha256sum "$MEDIA/$testname/datacheck.raw") &&
[[ "$CRC1" == "$CRC2" ]] &&
tt_close
'
test_done

28
extras/test/60_resize.sh Normal file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env zsh
export test_description="Testing tomb resize feature"
source ./setup
if test_have_prereq RESIZER; then
test_export "test" # Using already generated tomb
test_expect_success 'Testing resize to 30 MB tomb' '
tt resize -s 30 $tomb -k $tomb_key --unsafe --tomb-pwd $DUMMYPASS
'
if test_have_prereq GPGRCPT; then
test_export "recipient" # Using already generated tomb
test_expect_success 'Testing resize to 30 MB tomb with GnuPG keys' '
tt resize -s 30 $tomb -k $tomb_key -g -r $KEY2
'
fi
fi # RESIZER
if test_have_prereq RESIZER SPHINX ORACLE; then
test_export "sphinx_test" # Using already generated tomb
test_expect_success 'Testing resize to 30 MB tomb (sphinx)' '
tt resize -s 30 $tomb -k $tomb_key --unsafe --tomb-pwd $DUMMYPASS --sphx-user $DUMMYUSER --sphx-host $DUMMYHOST
'
fi
test_done

36
extras/test/65_passwd.sh Normal file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env zsh
export test_description="Testing changing tomb password"
source ./setup
test_export "test" # Using already generated tomb
test_expect_success 'Testing tomb with GnuPG keys: passwd' '
tt passwd -f -k $tomb_key --unsafe \
--tomb-old-pwd $DUMMYPASS --tomb-pwd $DUMMYPASSNEW &&
tt passwd -f -k $tomb_key --unsafe \
--tomb-old-pwd $DUMMYPASSNEW --tomb-pwd $DUMMYPASS
'
if test_have_prereq GPGRCPT; then
test_export "recipient" # Using already generated tomb
test_expect_success 'Testing tomb with GnuPG keys: passwd' '
tt passwd -f -k $tomb_key -g -r $KEY2
'
fi
if test_have_prereq SPHINX ORACLE; then
test_export "sphinx_test" # Using already generated tomb
test_expect_success 'Testing changing tomb password with sphinx' '
tt passwd -f -k $tomb_key --unsafe \
--tomb-old-pwd $DUMMYPASS --tomb-pwd $DUMMYPASSNEW \
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
tt passwd -f -k $tomb_key --unsafe \
--tomb-old-pwd $DUMMYPASSNEW --tomb-pwd $DUMMYPASS \
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST
'
fi
test_done

View File

@ -0,0 +1,14 @@
#!/usr/bin/env zsh
export test_description="Testing rendering QR Code key"
source ./setup
if test_have_prereq QRENCODE; then
test_export "test"
test_expect_success 'Testing rendering a QR printable key backup' '
tt engrave -k $tomb_key
'
fi
test_done

30
extras/test/75_hooks.sh Normal file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env zsh
export test_description="Testing tomb bind hooks"
TEMPHOME=$HOME
source ./setup
test_export "test" # Using already generated tomb
test_expect_success 'Testing bind hooks' '
tt_open --tomb-pwd $DUMMYPASS &&
tt_set_ownership "$MEDIA/$testname" &&
RND="$RANDOM" &&
bindtest="dyne-tomb-bind-test-$RND" &&
echo "$RND" > "$MEDIA/$testname/$bindtest" &&
rm -f "$MEDIA/$testname/bind-hooks" &&
echo "$bindtest $bindtest" > "$MEDIA/$testname/bind-hooks" &&
tt_close &&
touch "$TEMPHOME/$bindtest" &&
tt_open --tomb-pwd $DUMMYPASS &&
echo "$RND" &&
cat "$TEMPHOME/$bindtest" &&
tt list $testname &&
tt_close
'
# RND2=$(cat "$TEMPHOME/$bindtest") &&
# [[ "$RND" = "$RND2" ]] &&
test_done

View File

@ -0,0 +1,77 @@
#!/usr/bin/env zsh
export test_description="Testing steganography features"
source ./setup
if test_have_prereq STEGHIDE; then
test_export "test" # Using already generated tomb
cp -f "$TEST_HOME/arditi.jpg" "$tomb_img"
test_expect_success 'Testing tomb and steganographic: bury' '
tt bury -k $tomb_key $tomb_img \
--unsafe --tomb-pwd $DUMMYPASS
'
test_expect_success 'Testing tomb and steganographic: exhume' '
tt exhume -k $tomb_key_steg $tomb_img \
--unsafe --tomb-pwd $DUMMYPASS
'
test_expect_success 'Testing tomb and steganographic: open' '
tt open -k $tomb_key_steg $tomb --unsafe --tomb-pwd $DUMMYPASS &&
tt_close
'
test_expect_success 'Testing tomb and steganographic: piping keys using -k -' '
tkey=`tt exhume $tomb_img --unsafe --tomb-pwd $DUMMYPASS` &&
print "$tkey" | tt open -k - $tomb --unsafe --tomb-pwd $DUMMYPASS &&
tt_close
'
test_expect_success 'Testing tomb and steganographic: open image.jpeg' '
tt open -k $tomb_img $tomb --unsafe --tomb-pwd $DUMMYPASS &&
tt_close
'
if test_have_prereq GPGRCPT; then
test_export "recipient" # Using already generated tomb
cp -f "$TEST_HOME/arditi.jpg" "$tomb_img"
test_expect_success 'Testing tomb with GnuPG keys and steganographic: bury' '
tt bury -k $tomb_key $tomb_img -g -r $KEY1 \
--unsafe --tomb-pwd $DUMMYPASS
'
test_expect_success 'Testing tomb with GnuPG keys and steganographic: exhume' '
tt exhume -k $tomb_key_steg $tomb_img \
--unsafe --tomb-pwd $DUMMYPASS
'
test_expect_success 'Testing tomb with GnuPG keys and steganographic: open' '
tt open -k $tomb_key_steg $tomb -g &&
tt_close
'
test_expect_success 'Testing tomb with GnuPG keys and steganographic: piping keys using -k -' '
tkey=`tt exhume $tomb_img --unsafe --tomb-pwd $DUMMYPASS` &&
print "$tkey" | tt open -k - $tomb -g &&
tt_close
'
test_expect_success 'Testing tomb with GnuPG keys and steganographic: open image.jpeg' '
tt open -k $tomb_img $tomb -g --unsafe --tomb-pwd $DUMMYPASS &&
tt_close
'
fi # GPGRCPT
fi # STEGHIDE
if test_have_prereq PYTHON2 CLOAKIFY DECLOAKIFY; then
test_expect_success 'Testing tomb and steganographic: cloak' '
tt cloak -k $tomb_key $TEST_HOME/cipher-amphibians $tomb_text
'
test_expect_success 'Testing tomb and steganographic: uncloak' '
tt uncloak -k $tomb_key_cloak $tomb_text $TEST_HOME/cipher-amphibians
'
fi
test_done

50
extras/test/90_setkey.sh Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env zsh
export test_description="Testing set key"
source ./setup
test_export "test" # Using already generated tomb
test_expect_success 'Testing set key' '
tt forge -f -k $tomb_key_new --tomb-pwd $DUMMYPASS \
--ignore-swap --unsafe --force &&
tt setkey -f -k $tomb_key_new $tomb_key $tomb \
--unsafe --tomb-pwd $DUMMYPASS --tomb-old-pwd $DUMMYPASS &&
tt open -f -k $tomb_key_new $tomb \
--unsafe --tomb-pwd $DUMMYPASS &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key_new \
| xxd &&
tt_close
'
if test_have_prereq GPGRCPT; then
test_export "recipient" # Using already generated tomb
test_expect_success 'Testing tomb with GnuPG keys: setkey' '
tt forge -f $tomb_key_new -g -r $KEY2 --ignore-swap --unsafe &&
tt setkey -f -k $tomb_key_new $tomb_key $tomb -g -r $KEY2 &&
tt open -f -k $tomb_key_new $tomb -g &&
tt_close
'
fi
if test_have_prereq SPHINX ORACLE; then
test_export "sphinx_test" # Using already generated tomb
test_expect_success 'Testing set key (sphinx)' '
tt forge -f -k $tomb_key_new --tomb-pwd $DUMMYPASS \
--ignore-swap --unsafe --force \
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
tt setkey -f -k $tomb_key_new $tomb_key $tomb \
--unsafe --tomb-pwd $DUMMYPASS --tomb-old-pwd $DUMMYPASS \
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
tt open -f -k $tomb_key_new $tomb \
--unsafe --tomb-pwd $DUMMYPASS \
--sphx-user $DUMMYUSER --sphx-host $DUMMYHOST &&
print $DUMMYPASS \
| gpg --batch --passphrase-fd 0 --no-tty --no-options -d $tomb_key_new \
| xxd &&
tt_close
'
fi
test_done

15
extras/test/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM dyne/devuan:chimaera
RUN echo "deb http://deb.devuan.org/merged chimaera main" >> /etc/apt/sources.list
RUN apt-get update -y -q --allow-releaseinfo-change
RUN apt-get install -y -q zsh cryptsetup gpg gawk libgcrypt20-dev steghide qrencode python python2.7 python3-pip python3-dev libssl-dev make gcc sudo gettext bsdmainutils file pinentry-curses xxd libsodium23 libsodium-dev doas
RUN pip3 install setuptools wheel
COPY . /Tomb/
COPY extras/test/doas.conf /etc/doas.conf
RUN chmod 400 /etc/doas.conf
WORKDIR /Tomb
RUN make --directory=extras/kdf-keys
RUN make --directory=extras/kdf-keys install

View File

@ -1,2 +1,58 @@
all:
@./runtests
# Run tests
#
# Copyright (c) 2011-2012 Mathias Lafeldt
# Copyright (c) 2005-2012 Git project
# Copyright (c) 2005-2012 Junio C Hamano
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/ .
SHELL := $(shell which zsh)
SHELL_PATH ?= $(SHELL)
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
RM ?= rm -f
PROVE ?= prove
AGGREGATE_SCRIPT ?= aggregate-results
DEFAULT_TEST_TARGET ?= test
TEST_OPTS ?= --verbose --immediate --root=/tmp/tomb/sharness
T = $(sort $(wildcard *.sh))
all: $(DEFAULT_TEST_TARGET)
test: pre-clean
$(MAKE) aggregate-results-and-cleanup
prove: pre-clean
@echo "*** prove ***"; $(PROVE) --exec '$(SHELL_PATH_SQ)' $(PROVE_OPTS) $(T) :: $(TEST_OPTS)
$(T):
@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(TEST_OPTS)
pre-clean:
$(RM) -r test-results
clean:
$(RM) .prove
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
$(MAKE) clean
aggregate-results:
for f in test-results/*.counts; do \
echo "$$f"; \
done | '$(SHELL_PATH_SQ)' '$(AGGREGATE_SCRIPT)'
.PHONY: all test prove $(T) pre-clean clean
.PHONY: aggregate-results-and-cleanup aggregate-results

View File

@ -0,0 +1,57 @@
#!/usr/bin/env zsh
#
# Copyright (c) 2008-2012 Git project
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/ .
failed_tests=
fixed=0
success=0
failed=0
broken=0
total=0
while read file; do
while read type value; do
case $type in
'')
continue ;;
fixed)
fixed=$(($fixed + $value)) ;;
success)
success=$(($success + $value)) ;;
failed)
failed=$(($failed + $value))
if test $value != 0; then
test_name=$(expr "$file" : 'test-results/\(.*\)\.[0-9]*\.counts')
failed_tests="$failed_tests $test_name"
fi
;;
broken)
broken=$(($broken + $value)) ;;
total)
total=$(($total + $value)) ;;
esac
done <"$file"
done
if test -n "$failed_tests"; then
printf "\nfailed test(s):$failed_tests\n\n"
fi
printf "%-8s%d\n" fixed $fixed
printf "%-8s%d\n" success $success
printf "%-8s%d\n" failed $failed
printf "%-8s%d\n" broken $broken
printf "%-8s%d\n" total $total

View File

@ -0,0 +1,66 @@
Allophrynidae
Pipidae
Dicroglossidae
Myobatrachidae
Mavortium
Indotyphlidae
Elongatus
Croceater
Xanthoptica
Telmatobiidae
Arthroleptidae
Phrynobatrachidae
Hynobiidae
Rivularis
Torosa
Ranidae
Leptodactylidae
Dendrobatidae
Alsodidae
Eleutherodactylidae
Bufonidae
Craugastoridae
Ambystomatidae
Hylodidae
Croceum
Sierrae
Ambystoma
Micrixalidae
Nyctibatrachidae
Taricha
Typhlonectidae
Attenuatus
Brachycephalidae
Dicamptodon
Platycephalus
Ichthyophiidae
Centrolenidae
Plethodontidae
Oregonensis
Hyperoliidae
Batrachoseps
Hylidae
Rhacophoridae
Diabolicus
Ensatina
Gavilanensis
Nigriventris
Californiense
Rhyacotritonidae
Salamandridae
Altasierrae
Plethodon
Megophryidae
Sigillatum
Variegatus
Tenebrosus
Microhylidae
Pacificus
Odontobatrachidae
Ceratobatrachidae
Odontophrynidae
Strabomantidae
Mantellidae
Hydromantes
Siphonopidae
Pyxicephalidae

41
extras/test/close_block.c Normal file
View File

@ -0,0 +1,41 @@
/*
* Small program which simply opens a file in a tomb to block the
* $ tomb close
* operation
*
* Hard coded assumption on command line arguments
* 2) Path to open
* 3) How long to open the file (in seconds and can be optional)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[]) {
FILE *file_ptr;
unsigned int to_wait=10;
if ( argc < 2 ) {
fprintf(stderr, "Usage: %s path [time]\n", argv[0]);
exit(EXIT_FAILURE);
}
if ( argc == 3 ) {
to_wait = atoi(argv[2]);
}
file_ptr = fopen(argv[1],"w");
if ( file_ptr == NULL ) {
fprintf(stderr, "Error while opening the file.\n");
exit(EXIT_FAILURE);
}
sleep(to_wait);
fclose(file_ptr);
return 0;
}

20
extras/test/doas.conf Normal file
View File

@ -0,0 +1,20 @@
permit nopass root cmd losetup
permit nopass root cmd lsblk
permit nopass root cmd mkfs.ext3
permit nopass root cmd mkfs.ext4
permit nopass root cmd mkfs.btrfs
permit nopass root cmd touch
permit nopass root cmd fsck
permit nopass root cmd btrfs
permit nopass root cmd tune2fs
permit nopass root cmd mkdir
permit nopass root cmd mount
permit nopass root cmd rmdir
permit nopass root cmd chown
permit nopass root cmd umount
permit nopass root cmd findmnt
permit nopass root cmd e2fsck
permit nopass root cmd resize2fs
permit nopass root cmd lsof
permit nopass root cmd kill
permit nopass root cmd cryptsetup

View File

@ -0,0 +1,8 @@
# ~/.gnupg/gpg.conf
# Description of options can be found here:
# http://www.gnupg.org/documentation/manuals/gnupg-devel/GPG-Options.html
# GPG Configuration Options
default-key 0B2235E660753AB0475FB3E23DC836481F44B31E
default-recipient-self

Some files were not shown because too many files have changed in this diff Show More