diff options
author | Eric Biggers <ebiggers@google.com> | 2021-06-15 00:51:11 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-06-15 00:51:11 +0000 |
commit | a58f0a3a9a5356ef50072414864ec97abaa9d752 (patch) | |
tree | 93cf5c5e908b291e65da8c8f3c97930c91ad0d78 | |
parent | 980e8fe649dbd23741fe5123a170136a0a543a64 (diff) | |
parent | 410ed4977f4dfd3500e2230a128203171b8cb970 (diff) | |
download | fsverity-utils-a58f0a3a9a5356ef50072414864ec97abaa9d752.tar.gz |
Upgrade fsverity-utils to v1.4 am: 410ed4977f
Original change: https://android-review.googlesource.com/c/platform/external/fsverity-utils/+/1736357
Change-Id: I0d87f5107faaab99cb2563c9d1ffc90361488312
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 33 | ||||
-rw-r--r-- | NEWS.md | 11 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | common/fsverity_uapi.h | 14 | ||||
-rw-r--r-- | include/libfsverity.h | 48 | ||||
-rw-r--r-- | lib/compute_digest.c | 130 | ||||
-rw-r--r-- | lib/libfsverity.pc.in | 2 | ||||
-rw-r--r-- | man/fsverity.1.md | 191 | ||||
-rw-r--r-- | programs/cmd_digest.c | 7 | ||||
-rw-r--r-- | programs/cmd_dump_metadata.c | 163 | ||||
-rw-r--r-- | programs/cmd_sign.c | 17 | ||||
-rw-r--r-- | programs/fsverity.c | 94 | ||||
-rw-r--r-- | programs/fsverity.h | 10 | ||||
-rw-r--r-- | programs/test_compute_digest.c | 133 | ||||
-rw-r--r-- | programs/utils.c | 59 | ||||
-rw-r--r-- | programs/utils.h | 3 | ||||
-rwxr-xr-x | scripts/do-release.sh | 6 | ||||
-rwxr-xr-x | scripts/run-tests.sh | 2 |
19 files changed, 901 insertions, 38 deletions
@@ -1,4 +1,6 @@ +*.[1-9] *.a +*.exe *.o *.patch *.so @@ -25,6 +25,9 @@ # Define LIBDIR to override where to install libraries, like './configure # --libdir' in autotools-based projects (default: PREFIX/lib) # +# Define MANDIR to override where to install man pages, like './configure +# --mandir' in autotools-based projects (default: PREFIX/share/man) +# # Define DESTDIR to override the installation destination directory # (default: empty string) # @@ -61,12 +64,14 @@ QUIET_CCLD = @echo ' CCLD ' $@; QUIET_AR = @echo ' AR ' $@; QUIET_LN = @echo ' LN ' $@; QUIET_GEN = @echo ' GEN ' $@; +QUIET_PANDOC = @echo ' PANDOC ' $@; endif USE_SHARED_LIB ?= PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin INCDIR ?= $(PREFIX)/include LIBDIR ?= $(PREFIX)/lib +MANDIR ?= $(PREFIX)/share/man DESTDIR ?= ifneq ($(MINGW),1) PKGCONF ?= pkg-config @@ -92,6 +97,7 @@ FSVERITY := fsverity$(EXEEXT) fi DEFAULT_TARGETS := +EXTRA_TARGETS := COMMON_HEADERS := $(wildcard common/*.h) LDLIBS := $(shell "$(PKGCONF)" libcrypto --libs 2>/dev/null || echo -lcrypto) CFLAGS += $(shell "$(PKGCONF)" libcrypto --cflags 2>/dev/null || echo) @@ -160,6 +166,7 @@ FSVERITY_PROG_OBJ := $(PROG_COMMON_OBJ) \ programs/fsverity.o ifneq ($(MINGW),1) FSVERITY_PROG_OBJ += \ + programs/cmd_dump_metadata.o \ programs/cmd_enable.o \ programs/cmd_measure.o endif @@ -186,9 +193,22 @@ DEFAULT_TARGETS += $(FSVERITY) $(TEST_PROGRAMS): %$(EXEEXT): programs/%.o $(PROG_COMMON_OBJ) libfsverity.a $(QUIET_CCLD) $(CC) -o $@ $+ $(CFLAGS) $(LDFLAGS) $(LDLIBS) +EXTRA_TARGETS += $(TEST_PROGRAMS) + ############################################################################## -SPECIAL_TARGETS := all test_programs check install uninstall help clean +#### Manual pages + +man/fsverity.1:man/fsverity.1.md + $(QUIET_PANDOC) pandoc $+ -s -t man > $@ + +MAN_PAGES := man/fsverity.1 +EXTRA_TARGETS += $(MAN_PAGES) + +############################################################################## + +SPECIAL_TARGETS := all test_programs check install install-man uninstall \ + help clean FORCE: @@ -232,6 +252,10 @@ install:all > $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc chmod 644 $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc +install-man:$(MAN_PAGES) + install -d $(DESTDIR)$(MANDIR)/man1 + install -m644 $+ $(DESTDIR)$(MANDIR)/man1/ + uninstall: rm -f $(DESTDIR)$(BINDIR)/$(FSVERITY) rm -f $(DESTDIR)$(LIBDIR)/libfsverity.a @@ -239,15 +263,18 @@ uninstall: rm -f $(DESTDIR)$(LIBDIR)/libfsverity.so rm -f $(DESTDIR)$(LIBDIR)/pkgconfig/libfsverity.pc rm -f $(DESTDIR)$(INCDIR)/libfsverity.h + for page in $(notdir $(MAN_PAGES)); do \ + rm -f $(DESTDIR)$(MANDIR)/man1/$$page; \ + done help: @echo "Available targets:" @echo "------------------" - @for target in $(DEFAULT_TARGETS) $(TEST_PROGRAMS) $(SPECIAL_TARGETS); \ + @for target in $(DEFAULT_TARGETS) $(EXTRA_TARGETS) $(SPECIAL_TARGETS); \ do \ echo $$target; \ done clean: - rm -f $(DEFAULT_TARGETS) $(TEST_PROGRAMS) \ + rm -f $(DEFAULT_TARGETS) $(EXTRA_TARGETS) \ lib/*.o programs/*.o .build-config fsverity.sig @@ -1,5 +1,16 @@ # fsverity-utils release notes +## Version 1.4 + +* Added a manual page for the `fsverity` utility. + +* Added the `fsverity dump_metadata` subcommand. + +* Added the `--out-merkle-tree` and `--out-descriptor` options to + `fsverity digest` and `fsverity sign`. + +* Added metadata callbacks support to `libfsverity_compute_digest()`. + ## Version 1.3 * Added a `fsverity digest` subcommand. @@ -24,20 +24,22 @@ See `libfsverity.h` for the API of this library. ## Building and installing -fsverity-utils uses the OpenSSL library, so you first must install the -needed development files. For example, on Debian-based systems, run: +To build fsverity-utils, first install the needed build dependencies. For +example, on Debian-based systems, run: ```bash sudo apt-get install libssl-dev + sudo apt-get install pandoc # optional ``` -OpenSSL must be version 1.0.0 or later. +OpenSSL must be version 1.0.0 or later. This is the only runtime dependency. Then, to build and install fsverity-utils: ```bash make sudo make install + sudo make install-man # optional ``` By default, the following targets are built and installed: the program @@ -45,6 +47,9 @@ By default, the following targets are built and installed: the program `libfsverity.so`. You can also run `make check` to build and run the tests, or `make help` to display all available build targets. +`make install-man` installs the `fsverity.1` manual page. This step requires +that `pandoc` be installed. + By default, `fsverity` is statically linked to `libfsverity`. You can use `make USE_SHARED_LIB=1` to use dynamic linking instead. @@ -63,6 +68,9 @@ A Windows build of OpenSSL/libcrypto needs to be available. ## Examples +Full usage information for `fsverity` can be found in the manual page +(`man fsverity`). Here, we just show some typical examples. + ### Basic use ```bash diff --git a/common/fsverity_uapi.h b/common/fsverity_uapi.h index a739c9a..c59a897 100644 --- a/common/fsverity_uapi.h +++ b/common/fsverity_uapi.h @@ -85,7 +85,21 @@ struct fsverity_formatted_digest { __u8 digest[]; }; +#define FS_VERITY_METADATA_TYPE_MERKLE_TREE 1 +#define FS_VERITY_METADATA_TYPE_DESCRIPTOR 2 +#define FS_VERITY_METADATA_TYPE_SIGNATURE 3 + +struct fsverity_read_metadata_arg { + __u64 metadata_type; + __u64 offset; + __u64 length; + __u64 buf_ptr; + __u64 __reserved; +}; + #define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg) #define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest) +#define FS_IOC_READ_VERITY_METADATA \ + _IOWR('f', 135, struct fsverity_read_metadata_arg) #endif /* _UAPI_LINUX_FSVERITY_H */ diff --git a/include/libfsverity.h b/include/libfsverity.h index 6c42e5e..6cefa2b 100644 --- a/include/libfsverity.h +++ b/include/libfsverity.h @@ -22,7 +22,7 @@ extern "C" { #include <stdint.h> #define FSVERITY_UTILS_MAJOR_VERSION 1 -#define FSVERITY_UTILS_MINOR_VERSION 3 +#define FSVERITY_UTILS_MINOR_VERSION 4 #define FS_VERITY_HASH_ALG_SHA256 1 #define FS_VERITY_HASH_ALG_SHA512 2 @@ -61,8 +61,18 @@ struct libfsverity_merkle_tree_params { /** @reserved1: must be 0 */ uint64_t reserved1[8]; + /** + * @metadata_callbacks: if non-NULL, this gives a set of callback + * functions to which libfsverity_compute_digest() will pass the Merkle + * tree blocks and fs-verity descriptor after they are computed. + * Normally this isn't useful, but this can be needed in rare cases + * where the metadata needs to be consumed by something other than one + * of the native Linux kernel implementations of fs-verity. + */ + const struct libfsverity_metadata_callbacks *metadata_callbacks; + /** @reserved2: must be 0 */ - uintptr_t reserved2[8]; + uintptr_t reserved2[7]; }; struct libfsverity_digest { @@ -78,6 +88,37 @@ struct libfsverity_signature_params { uintptr_t reserved2[8]; /* must be 0 */ }; +struct libfsverity_metadata_callbacks { + + /** @ctx: context passed to the below callbacks (opaque to library) */ + void *ctx; + + /** + * @merkle_tree_size: if non-NULL, called with the total size of the + * Merkle tree in bytes, prior to any call to @merkle_tree_block. Must + * return 0 on success, or a negative errno value on failure. + */ + int (*merkle_tree_size)(void *ctx, uint64_t size); + + /** + * @merkle_tree_block: if non-NULL, called with each block of the + * Merkle tree after it is computed. The offset is the offset in bytes + * to the block within the Merkle tree, using the Merkle tree layout + * used by FS_IOC_READ_VERITY_METADATA. The offsets won't necessarily + * be in increasing order. Must return 0 on success, or a negative + * errno value on failure. + */ + int (*merkle_tree_block)(void *ctx, const void *block, size_t size, + uint64_t offset); + + /** + * @descriptor: if non-NULL, called with the fs-verity descriptor after + * it is computed. Must return 0 on success, or a negative errno value + * on failure. + */ + int (*descriptor)(void *ctx, const void *descriptor, size_t size); +}; + /* * libfsverity_read_fn_t - callback that incrementally provides a file's data * @fd: the user-provided "file descriptor" (opaque to library) @@ -101,7 +142,8 @@ typedef int (*libfsverity_read_fn_t)(void *fd, void *buf, size_t count); * * Returns: * * 0 for success, -EINVAL for invalid input arguments, -ENOMEM if libfsverity - * failed to allocate memory, or an error returned by @read_fn. + * failed to allocate memory, or an error returned by @read_fn or by one of + * the @params->metadata_callbacks. * * digest_ret returns a pointer to the digest on success. The digest object * is allocated by libfsverity and must be freed by the caller using free(). */ diff --git a/lib/compute_digest.c b/lib/compute_digest.c index a4f649c..c5b0100 100644 --- a/lib/compute_digest.c +++ b/lib/compute_digest.c @@ -24,9 +24,8 @@ struct block_buffer { /* * Hash a block, writing the result to the next level's pending block buffer. - * Returns true if the next level's block became full, else false. */ -static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, +static void hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, u32 block_size, const u8 *salt, u32 salt_size) { struct block_buffer *next = cur + 1; @@ -41,8 +40,60 @@ static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, next->filled += hash->alg->digest_size; cur->filled = 0; +} + +static bool block_is_full(const struct block_buffer *block, u32 block_size, + struct hash_ctx *hash) +{ + /* Would the next hash put us over the limit? */ + return block->filled + hash->alg->digest_size > block_size; +} + +static int report_merkle_tree_size(const struct libfsverity_metadata_callbacks *cbs, + u64 size) +{ + if (cbs && cbs->merkle_tree_size) { + int err = cbs->merkle_tree_size(cbs->ctx, size); - return next->filled + hash->alg->digest_size > block_size; + if (err) { + libfsverity_error_msg("error processing Merkle tree size"); + return err; + } + } + return 0; +} + +static int report_merkle_tree_block(const struct libfsverity_metadata_callbacks *cbs, + const struct block_buffer *block, + u32 block_size, u64 *level_offset) +{ + + if (cbs && cbs->merkle_tree_block) { + int err = cbs->merkle_tree_block(cbs->ctx, block->data, + block_size, + *level_offset * block_size); + + if (err) { + libfsverity_error_msg("error processing Merkle tree block"); + return err; + } + (*level_offset)++; + } + return 0; +} + +static int report_descriptor(const struct libfsverity_metadata_callbacks *cbs, + const void *descriptor, size_t size) +{ + if (cbs && cbs->descriptor) { + int err = cbs->descriptor(cbs->ctx, descriptor, size); + + if (err) { + libfsverity_error_msg("error processing fs-verity descriptor"); + return err; + } + } + return 0; } /* @@ -52,6 +103,7 @@ static bool hash_one_block(struct hash_ctx *hash, struct block_buffer *cur, static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, u64 file_size, struct hash_ctx *hash, u32 block_size, const u8 *salt, u32 salt_size, + const struct libfsverity_metadata_callbacks *metadata_cbs, u8 *root_hash) { const u32 hashes_per_block = block_size / hash->alg->digest_size; @@ -60,6 +112,7 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, u64 blocks; int num_levels = 0; int level; + u64 level_offset[FS_VERITY_MAX_LEVELS]; struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {}; struct block_buffer *buffers = &_buffers[1]; u64 offset; @@ -68,7 +121,7 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, /* Root hash of empty file is all 0's */ if (file_size == 0) { memset(root_hash, 0, hash->alg->digest_size); - return 0; + return report_merkle_tree_size(metadata_cbs, 0); } if (salt_size != 0) { @@ -78,15 +131,39 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, memcpy(padded_salt, salt, salt_size); } - /* Compute number of levels */ - for (blocks = DIV_ROUND_UP(file_size, block_size); blocks > 1; - blocks = DIV_ROUND_UP(blocks, hashes_per_block)) { + /* Compute number of levels and the number of blocks in each level. */ + blocks = DIV_ROUND_UP(file_size, block_size); + while (blocks > 1) { if (WARN_ON(num_levels >= FS_VERITY_MAX_LEVELS)) { err = -EINVAL; goto out; } - num_levels++; + blocks = DIV_ROUND_UP(blocks, hashes_per_block); + /* + * Temporarily use level_offset[] to store the number of blocks + * in each level. It will be overwritten later. + */ + level_offset[num_levels++] = blocks; + } + + /* + * Compute the starting block of each level, using the convention where + * the root level is first, i.e. the convention used by + * FS_IOC_READ_VERITY_METADATA. At the same time, compute the total + * size of the Merkle tree. These values are only needed for the + * metadata callbacks (if they were given), as the hash computation + * itself doesn't prescribe an ordering of the levels and doesn't + * prescribe any special meaning to the total size of the Merkle tree. + */ + offset = 0; + for (level = num_levels - 1; level >= 0; level--) { + blocks = level_offset[level]; + level_offset[level] = offset; + offset += blocks; } + err = report_merkle_tree_size(metadata_cbs, offset * block_size); + if (err) + goto out; /* * Allocate the block buffers. Buffer "-1" is for data blocks. @@ -112,21 +189,33 @@ static int compute_root_hash(void *fd, libfsverity_read_fn_t read_fn, goto out; } - level = -1; - while (hash_one_block(hash, &buffers[level], block_size, - padded_salt, padded_salt_size)) { - level++; - if (WARN_ON(level >= num_levels)) { - err = -EINVAL; + hash_one_block(hash, &buffers[-1], block_size, + padded_salt, padded_salt_size); + for (level = 0; level < num_levels; level++) { + if (!block_is_full(&buffers[level], block_size, hash)) + break; + hash_one_block(hash, &buffers[level], block_size, + padded_salt, padded_salt_size); + err = report_merkle_tree_block(metadata_cbs, + &buffers[level], + block_size, + &level_offset[level]); + if (err) goto out; - } } } /* Finish all nonempty pending tree blocks */ for (level = 0; level < num_levels; level++) { - if (buffers[level].filled != 0) + if (buffers[level].filled != 0) { hash_one_block(hash, &buffers[level], block_size, padded_salt, padded_salt_size); + err = report_merkle_tree_block(metadata_cbs, + &buffers[level], + block_size, + &level_offset[level]); + if (err) + goto out; + } } /* Root hash was filled by the last call to hash_one_block() */ @@ -217,8 +306,13 @@ libfsverity_compute_digest(void *fd, libfsverity_read_fn_t read_fn, } err = compute_root_hash(fd, read_fn, params->file_size, hash, - block_size, params->salt, - params->salt_size, desc.root_hash); + block_size, params->salt, params->salt_size, + params->metadata_callbacks, desc.root_hash); + if (err) + goto out; + + err = report_descriptor(params->metadata_callbacks, + &desc, sizeof(desc)); if (err) goto out; diff --git a/lib/libfsverity.pc.in b/lib/libfsverity.pc.in index be3ef44..03496fe 100644 --- a/lib/libfsverity.pc.in +++ b/lib/libfsverity.pc.in @@ -4,7 +4,7 @@ includedir=@INCDIR@ Name: libfsverity Description: fs-verity library -Version: 1.3 +Version: 1.4 Libs: -L${libdir} -lfsverity Requires.private: libcrypto Cflags: -I${includedir} diff --git a/man/fsverity.1.md b/man/fsverity.1.md new file mode 100644 index 0000000..e1007f5 --- /dev/null +++ b/man/fsverity.1.md @@ -0,0 +1,191 @@ +% FSVERITY(1) fsverity-utils v1.4 | User Commands +% +% June 2021 + +# NAME + +fsverity - userspace utility for fs-verity + +# SYNOPSIS +**fsverity digest** [*OPTION*...] *FILE*... \ +**fsverity dump_metadata** [*OPTION*...] *TYPE* *FILE* \ +**fsverity enable** [*OPTION*...] *FILE* \ +**fsverity measure** *FILE*... \ +**fsverity sign \-\-key**=*KEYFILE* [*OPTION*...] *FILE* *OUT_SIGFILE* + +# DESCRIPTION + +**fsverity** is a userspace utility for fs-verity. fs-verity is a Linux kernel +filesystem feature that does transparent on-demand verification of the contents +of read-only files using Merkle trees. + +**fsverity** can enable fs-verity on files, retrieve the digests of fs-verity +files, and sign files for use with fs-verity (among other things). +**fsverity**'s functionality is divided among various subcommands. + +This manual page focuses on documenting all **fsverity** subcommands and +options. For examples and more information about the fs-verity kernel feature, +see the references at the end of this page. + +# OPTIONS + +**fsverity** always accepts the following options: + +**\-\-help** +: Show the help, for either one subcommand or for all subcommands. + +**\-\-version** +: Show the version of fsverity-utils. + +# SUBCOMMANDS + +## **fsverity digest** [*OPTION*...] *FILE*... + +Compute the fs-verity digest of the given file(s). This is mainly intended to +used in preparation for signing the digest. In some cases **fsverity sign** +can be used instead to digest and sign the file in one step. + +Options accepted by **fsverity digest**: + +**\-\-block-size**=*BLOCK_SIZE* +: The Merkle tree block size (in bytes) to use. This must be a power of 2 and + at least twice the size of the hash values. However, note that currently + (as of Linux kernel v5.13), the Linux kernel implementations of fs-verity + only support the case where the Merkle tree block size is equal to the + system page size, usually 4096 bytes. The default value of this option is + 4096. + +**\-\-compact** +: When printing the file digest, only print the actual digest hex string; + don't print the algorithm name and filename. + +**\-\-for-builtin-sig** +: Format the file digest in a way that is compatible with the Linux kernel's + fs-verity built-in signature verification support. This means formatting it + as a `struct fsverity_formatted_digest`. Use this option if you are using + built-in signatures but are not using **fsverity sign** to do the signing. + +**\-\-hash-alg**=*HASH_ALG* +: The hash algorithm to use to build the Merkle tree. Valid options are + sha256 and sha512. Default is sha256. + +**\-\-out-merkle-tree**=*FILE* +: Write the computed Merkle tree to the given file. The Merkle tree layout + will be the same as that used by the Linux kernel's + `FS_IOC_READ_VERITY_METADATA` ioctl. + + Normally this option isn't useful, but it can be needed in cases where the + fs-verity metadata needs to be consumed by something other than one of the + native Linux kernel implementations of fs-verity. This is not needed for + file signing. + +**\-\-out-descriptor**=*FILE* +: Write the computed fs-verity descriptor to the given file. + + Normally this option isn't useful, but it can be needed in cases where the + fs-verity metadata needs to be consumed by something other than one of the + native Linux kernel implementations of fs-verity. This is not needed for + file signing. + +**\-\-salt**=*SALT* +: The salt to use in the Merkle tree, as a hex string. The salt is a value + that is prepended to every hashed block; it can be used to personalize the + hashing for a particular file or device. The default is no salt. + +## **fsverity dump_metadata** [*OPTION*...] *TYPE* *FILE* + +Dump the fs-verity metadata of the given file. The file must have fs-verity +enabled, and the filesystem must support the `FS_IOC_READ_VERITY_METADATA` ioctl +(it was added in Linux v5.12). This subcommand normally isn't useful, but it +can be useful in cases where a userspace server program is serving a verity file +to a client which implements fs-verity compatible verification. + +*TYPE* may be "merkle\_tree", "descriptor", or "signature", indicating the type +of metadata to dump. "signature" refers to the built-in signature, if present; +userspace-managed signatures will not be included. + +Options accepted by **fsverity dump_metadata**: + +**\-\-length**=*LENGTH* +: Length in bytes to dump from the specified metadata item. Only accepted in + combination with **\-\-offset**. + +**\-\-offset**=*offset* +: Offset in bytes into the specified metadata item at which to start dumping. + Only accepted in combination with **\-\-length**. + +## **fsverity enable** [*OPTION*...] *FILE* + +Enable fs-verity on the specified file. This will only work if the filesystem +supports fs-verity. + +Options accepted by **fsverity enable**: + +**\-\-block-size**=*BLOCK_SIZE* +: Same as for **fsverity digest**. + +**\-\-hash-alg**=*HASH_ALG* +: Same as for **fsverity digest**. + +**\-\-salt**=*SALT* +: Same as for **fsverity digest**. + +**\-\-signature**=*SIGFILE* +: Specifies the built-in signature to apply to the file. *SIGFILE* must be a + file that contains the signature in PKCS#7 DER format, e.g. as produced by + the **fsverity sign** command. + + Note that this option is only needed if the Linux kernel's fs-verity + built-in signature verification support is being used. It is not needed if + the signatures will be verified in userspace, as in that case the signatures + should be stored separately. + +## **fsverity measure** *FILE*... + +Display the fs-verity digest of the given file(s). The files must have +fs-verity enabled. The output will be the same as **fsverity digest** with +the appropriate parameters, but **fsverity measure** will take constant time +for each file regardless of the size of the file. + +**fsverity measure** does not accept any options. + +## **fsverity sign** **\-\-key**=*KEYFILE* [*OPTION*...] *FILE* *OUT_SIGFILE* + +Sign the given file for fs-verity, in a way that is compatible with the Linux +kernel's fs-verity built-in signature verification support. The signature will +be written to *OUT_SIGFILE* in PKCS#7 DER format. + +Options accepted by **fsverity sign**: + +**\-\-block-size**=*BLOCK_SIZE* +: Same as for **fsverity digest**. + +**\-\-cert**=*CERTFILE* +: Specifies the file that contains the certificate, in PEM format. This + option is required if *KEYFILE* contains only the private key and not also + the certificate. + +**\-\-hash-alg**=*HASH_ALG* +: Same as for **fsverity digest**. + +**\-\-key**=*KEYFILE* +: Specifies the file that contains the private key, in PEM format. This + option is required. + +**\-\-out-descriptor**=*FILE* +: Same as for **fsverity digest**. + +**\-\-out-merkle-tree**=*FILE* +: Same as for **fsverity digest**. + +**\-\-salt**=*SALT* +: Same as for **fsverity digest**. + +# SEE ALSO + +For example commands and more information, see the +[README file for +fsverity-utils](https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git/tree/README.md). + +Also see the [kernel documentation for +fs-verity](https://www.kernel.org/doc/html/latest/filesystems/fsverity.html). diff --git a/programs/cmd_digest.c b/programs/cmd_digest.c index 1a3c769..fd9f4de 100644 --- a/programs/cmd_digest.c +++ b/programs/cmd_digest.c @@ -18,6 +18,8 @@ static const struct option longopts[] = { {"hash-alg", required_argument, NULL, OPT_HASH_ALG}, {"block-size", required_argument, NULL, OPT_BLOCK_SIZE}, {"salt", required_argument, NULL, OPT_SALT}, + {"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE}, + {"out-descriptor", required_argument, NULL, OPT_OUT_DESCRIPTOR}, {"compact", no_argument, NULL, OPT_COMPACT}, {"for-builtin-sig", no_argument, NULL, OPT_FOR_BUILTIN_SIG}, {NULL, 0, NULL, 0} @@ -40,6 +42,8 @@ int fsverity_cmd_digest(const struct fsverity_command *cmd, case OPT_HASH_ALG: case OPT_BLOCK_SIZE: case OPT_SALT: + case OPT_OUT_MERKLE_TREE: + case OPT_OUT_DESCRIPTOR: if (!parse_tree_param(c, optarg, &tree_params)) goto out_usage; break; @@ -114,7 +118,8 @@ int fsverity_cmd_digest(const struct fsverity_command *cmd, } status = 0; out: - destroy_tree_params(&tree_params); + if (!destroy_tree_params(&tree_params) && status == 0) + status = 1; return status; out_err: diff --git a/programs/cmd_dump_metadata.c b/programs/cmd_dump_metadata.c new file mode 100644 index 0000000..94cc8ec --- /dev/null +++ b/programs/cmd_dump_metadata.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: MIT +/* + * The 'fsverity dump_metadata' command + * + * Copyright 2021 Google LLC + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + +#include "fsverity.h" + +#include <fcntl.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <unistd.h> + +static const struct option longopts[] = { + {"offset", required_argument, NULL, OPT_OFFSET}, + {"length", required_argument, NULL, OPT_LENGTH}, + {NULL, 0, NULL, 0} +}; + +static const struct { + const char *name; + int val; +} metadata_types[] = { + {"merkle_tree", FS_VERITY_METADATA_TYPE_MERKLE_TREE}, + {"descriptor", FS_VERITY_METADATA_TYPE_DESCRIPTOR}, + {"signature", FS_VERITY_METADATA_TYPE_SIGNATURE}, +}; + +static bool parse_metadata_type(const char *name, __u64 *val_ret) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(metadata_types); i++) { + if (strcmp(name, metadata_types[i].name) == 0) { + *val_ret = metadata_types[i].val; + return true; + } + } + error_msg("unknown metadata type: %s", name); + fputs(" Expected", stderr); + for (i = 0; i < ARRAY_SIZE(metadata_types); i++) { + if (i != 0 && ARRAY_SIZE(metadata_types) > 2) + putc(',', stderr); + putc(' ', stderr); + if (i != 0 && i == ARRAY_SIZE(metadata_types) - 1) + fputs("or ", stderr); + fprintf(stderr, "\"%s\"", metadata_types[i].name); + } + fprintf(stderr, "\n"); + return false; +} + +/* Dump the fs-verity metadata of the given file. */ +int fsverity_cmd_dump_metadata(const struct fsverity_command *cmd, + int argc, char *argv[]) +{ + bool offset_specified = false; + bool length_specified = false; + struct filedes file = { .fd = -1 }; + struct filedes stdout_filedes = { .fd = STDOUT_FILENO, + .name = "stdout" }; + struct fsverity_read_metadata_arg arg = { .length = 32768 }; + void *buf = NULL; + char *tmp; + int c; + int status; + int bytes_read; + + while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) { + switch (c) { + case OPT_OFFSET: + if (offset_specified) { + error_msg("--offset can only be specified once"); + goto out_usage; + } + errno = 0; + arg.offset = strtoull(optarg, &tmp, 10); + if (errno || *tmp) { + error_msg("invalid value for --offset"); + goto out_usage; + } + offset_specified = true; + break; + case OPT_LENGTH: + if (length_specified) { + error_msg("--length can only be specified once"); + goto out_usage; + } + errno = 0; + arg.length = strtoull(optarg, &tmp, 10); + if (errno || *tmp || arg.length > SIZE_MAX) { + error_msg("invalid value for --length"); + goto out_usage; + } + length_specified = true; + break; + default: + goto out_usage; + } + } + + argv += optind; + argc -= optind; + + if (argc != 2) + goto out_usage; + + if (!parse_metadata_type(argv[0], &arg.metadata_type)) + goto out_usage; + + if (length_specified && !offset_specified) { + error_msg("--length specified without --offset"); + goto out_usage; + } + if (offset_specified && !length_specified) { + error_msg("--offset specified without --length"); + goto out_usage; + } + + buf = xzalloc(arg.length); + arg.buf_ptr = (uintptr_t)buf; + + if (!open_file(&file, argv[1], O_RDONLY, 0)) + goto out_err; + + /* + * If --offset and --length were specified, then do only the single read + * requested. Otherwise read until EOF. + */ + do { + bytes_read = ioctl(file.fd, FS_IOC_READ_VERITY_METADATA, &arg); + if (bytes_read < 0) { + error_msg_errno("FS_IOC_READ_VERITY_METADATA failed on '%s'", + file.name); + goto out_err; + } + if (bytes_read == 0) + break; + if (!full_write(&stdout_filedes, buf, bytes_read)) + goto out_err; + arg.offset += bytes_read; + } while (!length_specified); + + status = 0; +out: + free(buf); + filedes_close(&file); + return status; + +out_err: + status = 1; + goto out; + +out_usage: + usage(cmd, stderr); + status = 2; + goto out; +} diff --git a/programs/cmd_sign.c b/programs/cmd_sign.c index 47ba6a2..81a4ddc 100644 --- a/programs/cmd_sign.c +++ b/programs/cmd_sign.c @@ -27,11 +27,13 @@ static bool write_signature(const char *filename, const u8 *sig, u32 sig_size) } static const struct option longopts[] = { - {"hash-alg", required_argument, NULL, OPT_HASH_ALG}, - {"block-size", required_argument, NULL, OPT_BLOCK_SIZE}, - {"salt", required_argument, NULL, OPT_SALT}, - {"key", required_argument, NULL, OPT_KEY}, - {"cert", required_argument, NULL, OPT_CERT}, + {"hash-alg", required_argument, NULL, OPT_HASH_ALG}, + {"block-size", required_argument, NULL, OPT_BLOCK_SIZE}, + {"salt", required_argument, NULL, OPT_SALT}, + {"out-merkle-tree", required_argument, NULL, OPT_OUT_MERKLE_TREE}, + {"out-descriptor", required_argument, NULL, OPT_OUT_DESCRIPTOR}, + {"key", required_argument, NULL, OPT_KEY}, + {"cert", required_argument, NULL, OPT_CERT}, {NULL, 0, NULL, 0} }; @@ -54,6 +56,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, case OPT_HASH_ALG: case OPT_BLOCK_SIZE: case OPT_SALT: + case OPT_OUT_MERKLE_TREE: + case OPT_OUT_DESCRIPTOR: if (!parse_tree_param(c, optarg, &tree_params)) goto out_usage; break; @@ -117,7 +121,8 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, status = 0; out: filedes_close(&file); - destroy_tree_params(&tree_params); + if (!destroy_tree_params(&tree_params) && status == 0) + status = 1; free(digest); free(sig); return status; diff --git a/programs/fsverity.c b/programs/fsverity.c index b911b2e..f6aff3a 100644 --- a/programs/fsverity.c +++ b/programs/fsverity.c @@ -11,6 +11,7 @@ #include "fsverity.h" +#include <fcntl.h> #include <limits.h> static const struct fsverity_command { @@ -27,9 +28,16 @@ static const struct fsverity_command { .usage_str = " fsverity digest FILE...\n" " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" +" [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n" " [--compact] [--for-builtin-sig]\n" #ifndef _WIN32 }, { + .name = "dump_metadata", + .func = fsverity_cmd_dump_metadata, + .short_desc = "Dump the fs-verity metadata of the given file", + .usage_str = +" fsverity dump_metadata TYPE FILE [--offset=OFFSET] [--length=LENGTH]\n" + }, { .name = "enable", .func = fsverity_cmd_enable, .short_desc = "Enable fs-verity on a file", @@ -52,6 +60,7 @@ static const struct fsverity_command { .usage_str = " fsverity sign FILE OUT_SIGFILE --key=KEYFILE\n" " [--hash-alg=HASH_ALG] [--block-size=BLOCK_SIZE] [--salt=SALT]\n" +" [--out-merkle-tree=FILE] [--out-descriptor=FILE]\n" " [--cert=CERTFILE]\n" } }; @@ -194,6 +203,74 @@ static bool parse_salt_option(const char *arg, u8 **salt_ptr, return true; } +struct metadata_callback_ctx { + struct filedes merkle_tree_file; + struct filedes descriptor_file; + struct libfsverity_metadata_callbacks callbacks; +}; + +static int handle_merkle_tree_size(void *_ctx, u64 size) +{ + struct metadata_callback_ctx *ctx = _ctx; + + if (!preallocate_file(&ctx->merkle_tree_file, size)) + return -EIO; + return 0; +} + +static int handle_merkle_tree_block(void *_ctx, const void *block, size_t size, + u64 offset) +{ + struct metadata_callback_ctx *ctx = _ctx; + + if (!full_pwrite(&ctx->merkle_tree_file, block, size, offset)) + return -EIO; + return 0; +} + +static int handle_descriptor(void *_ctx, const void *descriptor, size_t size) +{ + struct metadata_callback_ctx *ctx = _ctx; + + if (!full_write(&ctx->descriptor_file, descriptor, size)) + return -EIO; + return 0; +} + +static bool parse_out_metadata_option(int opt_char, const char *arg, + const struct libfsverity_metadata_callbacks **cbs) +{ + struct metadata_callback_ctx *ctx; + struct filedes *file; + const char *opt_name; + + if (*cbs) { + ctx = (*cbs)->ctx; + } else { + ctx = xzalloc(sizeof(*ctx)); + ctx->merkle_tree_file.fd = -1; + ctx->descriptor_file.fd = -1; + ctx->callbacks.ctx = ctx; + *cbs = &ctx->callbacks; + } + + if (opt_char == OPT_OUT_MERKLE_TREE) { + file = &ctx->merkle_tree_file; + opt_name = "--out-merkle-tree"; + ctx->callbacks.merkle_tree_size = handle_merkle_tree_size; + ctx->callbacks.merkle_tree_block = handle_merkle_tree_block; + } else { + file = &ctx->descriptor_file; + opt_name = "--out-descriptor"; + ctx->callbacks.descriptor = handle_descriptor; + } + if (file->fd >= 0) { + error_msg("%s can only be specified once", opt_name); + return false; + } + return open_file(file, arg, O_WRONLY|O_CREAT|O_TRUNC, 0644); +} + bool parse_tree_param(int opt_char, const char *arg, struct libfsverity_merkle_tree_params *params) { @@ -205,15 +282,30 @@ bool parse_tree_param(int opt_char, const char *arg, case OPT_SALT: return parse_salt_option(arg, (u8 **)¶ms->salt, ¶ms->salt_size); + case OPT_OUT_MERKLE_TREE: + case OPT_OUT_DESCRIPTOR: + return parse_out_metadata_option(opt_char, arg, + ¶ms->metadata_callbacks); default: ASSERT(0); } } -void destroy_tree_params(struct libfsverity_merkle_tree_params *params) +bool destroy_tree_params(struct libfsverity_merkle_tree_params *params) { + bool ok = true; + free((u8 *)params->salt); + if (params->metadata_callbacks) { + struct metadata_callback_ctx *ctx = + params->metadata_callbacks->ctx; + + ok &= filedes_close(&ctx->merkle_tree_file); + ok &= filedes_close(&ctx->descriptor_file); + free(ctx); + } memset(params, 0, sizeof(*params)); + return ok; } int main(int argc, char *argv[]) diff --git a/programs/fsverity.h b/programs/fsverity.h index 45c4fe1..fe24087 100644 --- a/programs/fsverity.h +++ b/programs/fsverity.h @@ -27,6 +27,10 @@ enum { OPT_FOR_BUILTIN_SIG, OPT_HASH_ALG, OPT_KEY, + OPT_LENGTH, + OPT_OFFSET, + OPT_OUT_DESCRIPTOR, + OPT_OUT_MERKLE_TREE, OPT_SALT, OPT_SIGNATURE, }; @@ -37,6 +41,10 @@ struct fsverity_command; int fsverity_cmd_digest(const struct fsverity_command *cmd, int argc, char *argv[]); +/* cmd_dump_metadata.c */ +int fsverity_cmd_dump_metadata(const struct fsverity_command *cmd, + int argc, char *argv[]); + /* cmd_enable.c */ int fsverity_cmd_enable(const struct fsverity_command *cmd, int argc, char *argv[]); @@ -53,6 +61,6 @@ int fsverity_cmd_sign(const struct fsverity_command *cmd, void usage(const struct fsverity_command *cmd, FILE *fp); bool parse_tree_param(int opt_char, const char *arg, struct libfsverity_merkle_tree_params *params); -void destroy_tree_params(struct libfsverity_merkle_tree_params *params); +bool destroy_tree_params(struct libfsverity_merkle_tree_params *params); #endif /* PROGRAMS_FSVERITY_H */ diff --git a/programs/test_compute_digest.c b/programs/test_compute_digest.c index e7f2645..67266fa 100644 --- a/programs/test_compute_digest.c +++ b/programs/test_compute_digest.c @@ -13,6 +13,7 @@ #include <ctype.h> #include <inttypes.h> +#include <openssl/sha.h> struct mem_file { u8 *data; @@ -37,6 +38,13 @@ static int error_read_fn(void *fd __attribute__((unused)), return -EIO; } +static int zeroes_read_fn(void *fd __attribute__((unused)), + void *buf, size_t count) +{ + memset(buf, 0, count); + return 0; +} + static const struct test_case { u32 hash_algorithm; u32 block_size; @@ -249,6 +257,130 @@ static void test_invalid_params(void) ASSERT(d == NULL); } +static struct { + u64 merkle_tree_size; + u64 merkle_tree_block; + u64 descriptor; +} metadata_callback_counts; + +static int handle_merkle_tree_size(void *ctx, u64 size) +{ + metadata_callback_counts.merkle_tree_size++; + + /* Test that the ctx argument is passed through correctly. */ + ASSERT(ctx == (void *)1); + + /* Test that the expected Merkle tree size is reported. */ + ASSERT(size == 5 * 1024); + return 0; +} + +static int handle_merkle_tree_block(void *ctx, const void *block, size_t size, + u64 offset) +{ + u8 digest[SHA256_DIGEST_LENGTH]; + u64 count = metadata_callback_counts.merkle_tree_block++; + const char *expected_digest; + + /* Test that ->merkle_tree_size() was called first. */ + ASSERT(metadata_callback_counts.merkle_tree_size == 1); + + /* Test that the ctx argument is passed through correctly. */ + ASSERT(ctx == (void *)1); + + /* + * Test that this Merkle tree block has the expected size, offset, and + * contents. The 4 blocks at "level 0" should be reported first, in + * order; then the 1 block at "level 1" should be reported last (but the + * level 1 block should have the smallest offset). + */ + ASSERT(size == 1024); + SHA256(block, size, digest); + if (count == 4) { + /* 1 block at level 1 */ + ASSERT(offset == 0); + expected_digest = "\x68\xc5\x38\xe1\x19\x58\xd6\x5d" + "\x68\xb6\xfe\x8e\x9f\xb8\xcc\xab" + "\xec\xfd\x92\x8b\x01\xd0\x63\x44" + "\xe2\x23\xed\x41\xdd\xc4\x54\x4a"; + } else { + /* 4 blocks at level 0 */ + ASSERT(offset == 1024 + (count * 1024)); + if (count < 3) { + expected_digest = "\xf7\x89\xba\xab\x53\x85\x9f\xaf" + "\x36\xd6\xd7\x5d\x10\x42\x06\x42" + "\x94\x20\x2d\x6e\x13\xe7\x71\x6f" + "\x39\x4f\xba\x43\x4c\xcc\x49\x86"; + } else { + expected_digest = "\x00\xfe\xd0\x3c\x5d\x6e\xab\x21" + "\x31\x43\xf3\xd9\x6a\x5c\xa3\x1c" + "\x2b\x89\xf5\x68\x4e\x6c\x8e\x07" + "\x87\x3e\x5e\x97\x65\x17\xb4\x8f"; + } + } + ASSERT(!memcmp(digest, expected_digest, SHA256_DIGEST_LENGTH)); + return 0; +} + +static const u8 expected_file_digest[SHA256_DIGEST_LENGTH] = + "\x09\xcb\xba\xee\xd2\xa0\x4c\x2d\xa2\x42\xc1\x0e\x15\x68\xd9\x6f" + "\x35\x8a\x16\xaa\x1e\xbe\x8c\xf0\x28\x61\x20\xc1\x3c\x93\x66\xd1"; + +static int handle_descriptor(void *ctx, const void *descriptor, size_t size) +{ + u8 digest[SHA256_DIGEST_LENGTH]; + + metadata_callback_counts.descriptor++; + /* Test that the ctx argument is passed through correctly. */ + ASSERT(ctx == (void *)1); + + /* Test that the fs-verity descriptor is reported correctly. */ + ASSERT(size == 256); + SHA256(descriptor, size, digest); + ASSERT(!memcmp(digest, expected_file_digest, SHA256_DIGEST_LENGTH)); + return 0; +} + +static const struct libfsverity_metadata_callbacks metadata_callbacks = { + .ctx = (void *)1, /* arbitrary value for testing purposes */ + .merkle_tree_size = handle_merkle_tree_size, + .merkle_tree_block = handle_merkle_tree_block, + .descriptor = handle_descriptor, +}; + +/* Test that the libfsverity_metadata_callbacks work correctly. */ +static void test_metadata_callbacks(void) +{ + /* + * For a useful test, we want a file whose Merkle tree will have at + * least 2 levels (this one will have exactly 2). The contents of the + * file aren't too important. + */ + struct libfsverity_merkle_tree_params params = { + .version = 1, + .hash_algorithm = FS_VERITY_HASH_ALG_SHA256, + .block_size = 1024, + .file_size = 100000, + .metadata_callbacks = &metadata_callbacks, + }; + struct libfsverity_digest *d; + + ASSERT(libfsverity_compute_digest(NULL, zeroes_read_fn, + ¶ms, &d) == 0); + + /* Test that the callbacks were called the correct number of times. */ + ASSERT(metadata_callback_counts.merkle_tree_size == 1); + ASSERT(metadata_callback_counts.merkle_tree_block == 5); + ASSERT(metadata_callback_counts.descriptor == 1); + + /* Test that the computed file digest is as expected. */ + ASSERT(d->digest_algorithm == FS_VERITY_HASH_ALG_SHA256); + ASSERT(d->digest_size == SHA256_DIGEST_LENGTH); + ASSERT(!memcmp(d->digest, expected_file_digest, SHA256_DIGEST_LENGTH)); + + free(d); +} + int main(int argc, char *argv[]) { const bool update = (argc == 2 && !strcmp(argv[1], "--update")); @@ -305,6 +437,7 @@ int main(int argc, char *argv[]) } test_invalid_params(); + test_metadata_callbacks(); printf("test_compute_digest passed\n"); return 0; } diff --git a/programs/utils.c b/programs/utils.c index ce19b57..116eb95 100644 --- a/programs/utils.c +++ b/programs/utils.c @@ -13,10 +13,14 @@ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <limits.h> #include <stdarg.h> #include <sys/stat.h> #include <unistd.h> +#ifdef _WIN32 +# include <windows.h> +#endif /* ========== Memory allocation ========== */ @@ -126,6 +130,26 @@ bool get_file_size(struct filedes *file, u64 *size_ret) return true; } +bool preallocate_file(struct filedes *file, u64 size) +{ + int res; + + if (size == 0) + return true; +#ifdef _WIN32 + /* Not exactly the same as posix_fallocate(), but good enough... */ + res = _chsize_s(file->fd, size); +#else + res = posix_fallocate(file->fd, 0, size); +#endif + if (res != 0) { + error_msg_errno("preallocating %" PRIu64 "-byte file '%s'", + size, file->name); + return false; + } + return true; +} + bool full_read(struct filedes *file, void *buf, size_t count) { while (count) { @@ -160,6 +184,41 @@ bool full_write(struct filedes *file, const void *buf, size_t count) return true; } +static int raw_pwrite(int fd, const void *buf, int count, u64 offset) +{ +#ifdef _WIN32 + HANDLE h = (HANDLE)_get_osfhandle(fd); + OVERLAPPED pos = { .Offset = offset, .OffsetHigh = offset >> 32 }; + DWORD written = 0; + + /* Not exactly the same as pwrite(), but good enough... */ + if (!WriteFile(h, buf, count, &written, &pos)) { + errno = EIO; + return -1; + } + return written; +#else + return pwrite(fd, buf, count, offset); +#endif +} + +bool full_pwrite(struct filedes *file, const void *buf, size_t count, + u64 offset) +{ + while (count) { + int n = raw_pwrite(file->fd, buf, min(count, INT_MAX), offset); + + if (n < 0) { + error_msg_errno("writing to '%s'", file->name); + return false; + } + buf += n; + count -= n; + offset += n; + } + return true; +} + bool filedes_close(struct filedes *file) { int res; diff --git a/programs/utils.h b/programs/utils.h index ab5005f..9a5c97a 100644 --- a/programs/utils.h +++ b/programs/utils.h @@ -40,8 +40,11 @@ struct filedes { bool open_file(struct filedes *file, const char *filename, int flags, int mode); bool get_file_size(struct filedes *file, u64 *size_ret); +bool preallocate_file(struct filedes *file, u64 size); bool full_read(struct filedes *file, void *buf, size_t count); bool full_write(struct filedes *file, const void *buf, size_t count); +bool full_pwrite(struct filedes *file, const void *buf, size_t count, + u64 offset); bool filedes_close(struct filedes *file); int read_callback(void *file, void *buf, size_t count); diff --git a/scripts/do-release.sh b/scripts/do-release.sh index 352fcf1..9f6bf73 100755 --- a/scripts/do-release.sh +++ b/scripts/do-release.sh @@ -25,11 +25,17 @@ git clean -fdx major=$(echo "$VERS" | cut -d. -f1) minor=$(echo "$VERS" | cut -d. -f2) +month=$(LC_ALL=C date +%B) +year=$(LC_ALL=C date +%Y) + sed -E -i -e "/FSVERITY_UTILS_MAJOR_VERSION/s/[0-9]+/$major/" \ -e "/FSVERITY_UTILS_MINOR_VERSION/s/[0-9]+/$minor/" \ include/libfsverity.h sed -E -i "/Version:/s/[0-9]+\.[0-9]+/$VERS/" \ lib/libfsverity.pc.in +sed -E -i -e "/^% /s/fsverity-utils v[0-9]+(\.[0-9]+)+/fsverity-utils v$VERS/" \ + -e "/^% /s/[a-zA-Z]+ 2[0-9]{3}/$month $year/" \ + man/*.[1-9].md git commit -a --signoff --message="v$VERS" git tag --sign "v$VERS" --message="$PKG" diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh index 530fe34..9cdc7c1 100755 --- a/scripts/run-tests.sh +++ b/scripts/run-tests.sh @@ -101,7 +101,7 @@ log "Check that all files have license and copyright info" list="$TMPDIR/filelist" filter_license_info() { # files to exclude from license and copyright info checks - grep -E -v '(\.gitignore|LICENSE|NEWS|README|testdata|fsverity_uapi\.h|libfsverity\.pc\.in)' + grep -E -v '(\.gitignore|LICENSE|.*\.md|testdata|fsverity_uapi\.h|libfsverity\.pc\.in)' } git grep -L 'SPDX-License-Identifier: MIT' \ | filter_license_info > "$list" || true |