From: Allison Karlitskaya Date: Mon, 25 Nov 2024 08:46:30 +0000 (+0100) Subject: mke2fs: enable copying of fs-verity metadata X-Git-Tag: v1.47.3-rc1~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6bfa843b4435334ac073e42950b48d8bacb54977;p=thirdparty%2Fe2fsprogs.git mke2fs: enable copying of fs-verity metadata When creating a filesystem with `mke2fs -O verity` and populating content via `-d`, check if that content is fs-verity enabled, and if it is, copy the fs-verity metadata from the host-native filesystem into the created filesystem. Closes: https://github.com/tytso/e2fsprogs/pull/201 Signed-off-by: Allison Karlitskaya --- diff --git a/configure b/configure index f9a7aa4e..4b3a4df7 100755 --- a/configure +++ b/configure @@ -11976,6 +11976,12 @@ if test "x$ac_cv_header_linux_fsmap_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_FSMAP_H 1" >>confdefs.h +fi +ac_fn_c_check_header_compile "$LINENO" "linux/fsverity.h" "ac_cv_header_linux_fsverity_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_fsverity_h" = xyes +then : + printf "%s\n" "#define HAVE_LINUX_FSVERITY_H 1" >>confdefs.h + fi ac_fn_c_check_header_compile "$LINENO" "linux/major.h" "ac_cv_header_linux_major_h" "$ac_includes_default" if test "x$ac_cv_header_linux_major_h" = xyes diff --git a/configure.ac b/configure.ac index 1f676040..95fd35f3 100644 --- a/configure.ac +++ b/configure.ac @@ -1009,6 +1009,7 @@ AC_CHECK_HEADERS(m4_flatten([ linux/falloc.h linux/fd.h linux/fsmap.h + linux/fsverity.h linux/major.h linux/loop.h linux/types.h diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c index 9fbf1f31..87ae7463 100644 --- a/debugfs/debugfs.c +++ b/debugfs/debugfs.c @@ -1766,7 +1766,7 @@ void do_write(int argc, ss_argv_t argv, int sci_idx EXT2FS_ATTR((unused)), " ", CHECK_FS_RW)) return; - retval = do_write_internal(current_fs, cwd, argv[1], argv[2], root); + retval = do_write_internal(current_fs, cwd, argv[1], argv[2], 0, root); if (retval) com_err(argv[0], retval, 0); } diff --git a/lib/config.h.in b/lib/config.h.in index 819c4331..a46451f9 100644 --- a/lib/config.h.in +++ b/lib/config.h.in @@ -199,6 +199,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_FSMAP_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_FSVERITY_H + /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_LOOP_H diff --git a/misc/create_inode.c b/misc/create_inode.c index 2e58f615..c11c00f2 100644 --- a/misc/create_inode.c +++ b/misc/create_inode.c @@ -30,6 +30,10 @@ #ifdef HAVE_SYS_SYSMACROS_H #include #endif +#ifdef HAVE_LINUX_FSVERITY_H +#include +#include +#endif #include #include @@ -457,7 +461,6 @@ static errcode_t copy_file_chunk(ext2_filsys fs, int fd, ext2_file_t e2_file, { off_t off, bpos; ssize_t got, blen; - unsigned int written; char *ptr; errcode_t err = 0; @@ -587,8 +590,110 @@ out: } #endif /* FS_IOC_FIEMAP */ +#ifdef HAVE_LINUX_FSVERITY_H +static inline off_t round_up(off_t n, off_t blksz, off_t bias) +{ + return ((n - bias + (blksz - 1)) & ~(blksz - 1)) + bias; +} + +static errcode_t copy_fs_verity_data(ext2_file_t e2_file, ext2_off_t e2_offset, + int fd, uint64_t metadata_type, + __u32 *written) +{ + errcode_t err; + char buf[COPY_FILE_BUFLEN]; + int size; + + *written = 0; + + do { + struct fsverity_read_metadata_arg arg = { + .metadata_type = metadata_type, + .buf_ptr = (uint64_t) buf, + .length = sizeof(buf), + .offset = *written, + }; + + size = ioctl(fd, FS_IOC_READ_VERITY_METADATA, &arg); + if (size < 0) { + if (errno == EINTR) + continue; + return errno; + } + err = write_all(e2_file, e2_offset, buf, size); + if (err) + return err; + + e2_offset += size; + *written += size; + } while (size != 0); + + return 0; +} + +static errcode_t copy_fs_verity(ext2_filsys fs, int fd, + ext2_file_t e2_file, off_t st_size) +{ + ext2_ino_t ino = ext2fs_file_get_inode_num(e2_file); + struct ext2_inode *inode = ext2fs_file_get_inode(e2_file); + off_t offset = round_up(st_size, 65536, 0); + __u32 written; + errcode_t err; + + if (!ino || !inode || + !(inode->i_flags & EXT4_EXTENTS_FL) || + (inode->i_flags & EXT4_INLINE_DATA_FL)) + return 0; + + /* We read the existing fs-verity data out of the host + * filesystem and write it verbatim into the file we're + * creating, as blocks following the end of the file. + * + * https://docs.kernel.org/filesystems/fsverity.html#fs-ioc-read-verity-metadata + * https://docs.kernel.org/filesystems/ext4/overview.html#verity-files + */ + + /* Copy Merkel tree data: might be empty (for empty files) */ + err = copy_fs_verity_data(e2_file, offset, fd, + FS_VERITY_METADATA_TYPE_MERKLE_TREE, + &written); + /* These errors are if the file/filesystem/kernel doesn't have + * fs-verity. If those happened before we wrote anything, + * then we already did the right thing. + */ + if ((err == ENODATA || err == ENOTTY || err == ENOTSUP) && !written) + return 0; + if (err) + return err; + offset = round_up(offset+written, fs->blocksize, 0); + + /* + * Write the verity descriptor (we don't handle signature blobs), + * starting at the next file system block boundary + */ + err = copy_fs_verity_data(e2_file, offset, fd, + FS_VERITY_METADATA_TYPE_DESCRIPTOR, + &written); + offset = round_up(offset+written, fs->blocksize, -4); + + /* + * Write the size of the verity descriptor in bytes, as a + * 4-byte little endian integer. + */ + written = ext2fs_cpu_to_le32(written); + err = write_all(e2_file, offset, (const char *) &written, 4); + if (err) + return err; + + /* Reset size in the inode to the original file size */; + ext2fs_inode_size_set(fs, inode, st_size); + inode->i_flags |= EXT4_VERITY_FL; + return ext2fs_write_inode(fs, ino, inode); +} +#endif + static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf, - ext2_ino_t ino) + unsigned long flags, ext2_ino_t ino) { ext2_file_t e2_file; char *buf = NULL, *zerobuf = NULL; @@ -606,10 +711,9 @@ static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf, if (err) goto out; + err = EXT2_ET_UNIMPLEMENTED; #if defined(SEEK_DATA) && defined(SEEK_HOLE) err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf); -#else - err = EXT2_ET_UNIMPLEMENTED; #endif #if defined(FS_IOC_FIEMAP) @@ -620,6 +724,12 @@ static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf, if (err == EXT2_ET_UNIMPLEMENTED) err = copy_file_chunk(fs, fd, e2_file, 0, statbuf->st_size, buf, zerobuf); + +#ifdef HAVE_LINUX_FSVERITY_H + if (!err && (flags & EXT4_VERITY_FL)) + err = copy_fs_verity(fs, fd, e2_file, statbuf->st_size); +#endif + out: ext2fs_free_mem(&zerobuf); ext2fs_free_mem(&buf); @@ -643,7 +753,8 @@ static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino) /* Copy the native file to the fs */ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src, - const char *dest, ext2_ino_t root) + const char *dest, unsigned long flags, + ext2_ino_t root) { int fd; struct stat statbuf; @@ -735,7 +846,7 @@ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src, goto out; } if (LINUX_S_ISREG(inode.i_mode)) { - retval = copy_file(fs, fd, &statbuf, newfile); + retval = copy_file(fs, fd, &statbuf, flags, newfile); if (retval) goto out; } @@ -828,6 +939,7 @@ static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino, const char *name; struct dirent **dent; struct stat st; + unsigned long fl; unsigned int save_inode; ext2_ino_t ino; errcode_t retval = 0; @@ -862,6 +974,8 @@ static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino, name); goto out; } + if (fgetflags(name, &fl) < 0) + fl = 0; /* Check for hardlinks */ save_inode = 0; @@ -955,7 +1069,7 @@ static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino, #endif /* !_WIN32 */ case S_IFREG: retval = do_write_internal(fs, parent_ino, name, name, - root); + fl, root); if (retval) { com_err(__func__, retval, _("while writing file \"%s\""), name); diff --git a/misc/create_inode.h b/misc/create_inode.h index 4a296ded..4472162e 100644 --- a/misc/create_inode.h +++ b/misc/create_inode.h @@ -63,7 +63,7 @@ extern errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name, ext2_ino_t root); extern errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src, const char *dest, - ext2_ino_t root); + unsigned long flags, ext2_ino_t root); extern errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino, ext2_ino_t ino, const char *name); extern errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,