]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
mke2fs: enable copying of fs-verity metadata
authorAllison Karlitskaya <allison.karlitskaya@redhat.com>
Mon, 25 Nov 2024 08:46:30 +0000 (09:46 +0100)
committerTheodore Ts'o <tytso@mit.edu>
Sat, 24 May 2025 20:32:16 +0000 (16:32 -0400)
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 <allison.karlitskaya@redhat.com>
configure
configure.ac
debugfs/debugfs.c
lib/config.h.in
misc/create_inode.c
misc/create_inode.h

index f9a7aa4e2e864cae9d871cc9911777a6b299e0a3..4b3a4df7f1f6baa9f343a2fb1aff7f6172b7b376 100755 (executable)
--- 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
index 1f67604036b5281a5147aa4351f8f44d6b085a6f..95fd35f305d89aaa019b50f29373853257c67242 100644 (file)
@@ -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
index 9fbf1f3187327f786aa00a7ff6e9617395c4cc81..87ae7463278da340bff86f3498b4d3a91755a55f 100644 (file)
@@ -1766,7 +1766,7 @@ void do_write(int argc, ss_argv_t argv, int sci_idx EXT2FS_ATTR((unused)),
                                "<native file> <new file>", 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);
 }
index 819c433137924771819f26d77fd745512ae94c64..a46451f9994ea7f59f11d7e9483b7f4e3ad610cf 100644 (file)
 /* Define to 1 if you have the <linux/fsmap.h> header file. */
 #undef HAVE_LINUX_FSMAP_H
 
+/* Define to 1 if you have the <linux/fsverity.h> header file. */
+#undef HAVE_LINUX_FSVERITY_H
+
 /* Define to 1 if you have the <linux/loop.h> header file. */
 #undef HAVE_LINUX_LOOP_H
 
index 2e58f615967a66787cd3bdb26c727ac3d442b79b..c11c00f2a0b038ff52892cb1338c1d5934f4047d 100644 (file)
 #ifdef HAVE_SYS_SYSMACROS_H
 #include <sys/sysmacros.h>
 #endif
+#ifdef HAVE_LINUX_FSVERITY_H
+#include <linux/fsverity.h>
+#include <linux/fs.h>
+#endif
 
 #include <ext2fs/ext2fs.h>
 #include <ext2fs/ext2_types.h>
@@ -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);
index 4a296ded64707a5298875a34bb96e9fcebb5413e..4472162eb7723dfa8e0f7c33951e24a71f25ff0d 100644 (file)
@@ -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,