composefs/fsverity/
ioctl.rs1#![allow(unsafe_code)]
8
9use core::mem::size_of;
10
11use std::{io::Error, os::fd::AsFd};
12
13use rustix::{
14 io::Errno,
15 ioctl::{ioctl, opcode, Opcode, Setter, Updater},
16};
17
18pub use super::{EnableVerityError, FsVerityHashValue, MeasureVerityError};
19
20#[repr(C)]
22#[derive(Debug)]
23struct FsVerityEnableArg {
24 version: u32,
25 hash_algorithm: u32,
26 block_size: u32,
27 salt_size: u32,
28 salt_ptr: u64,
29 sig_size: u32,
30 __reserved1: u32,
31 sig_ptr: u64,
32 __reserved2: [u64; 11],
33}
34
35const FS_IOC_ENABLE_VERITY: Opcode = opcode::write::<FsVerityEnableArg>(b'f', 133);
37
38pub(super) fn fs_ioc_enable_verity<H: FsVerityHashValue>(
42 fd: impl AsFd,
43) -> Result<(), EnableVerityError> {
44 unsafe {
45 match ioctl(
46 fd,
47 Setter::<{ FS_IOC_ENABLE_VERITY }, FsVerityEnableArg>::new(FsVerityEnableArg {
48 version: 1,
49 hash_algorithm: H::ALGORITHM as u32,
50 block_size: 4096,
51 salt_size: 0,
52 salt_ptr: 0,
53 sig_size: 0,
54 __reserved1: 0,
55 sig_ptr: 0,
56 __reserved2: [0; 11],
57 }),
58 ) {
59 Err(Errno::NOTTY) | Err(Errno::OPNOTSUPP) => {
60 Err(EnableVerityError::FilesystemNotSupported)
61 }
62 Err(Errno::EXIST) => Err(EnableVerityError::AlreadyEnabled),
63 Err(Errno::TXTBSY) => Err(EnableVerityError::FileOpenedForWrite),
64 Err(e) => Err(Error::from(e).into()),
65 Ok(_) => Ok(()),
66 }
67 }
68}
69
70#[repr(C)]
72#[derive(Debug)]
73struct FsVerityDigest<F> {
74 digest_algorithm: u16,
75 digest_size: u16,
76 digest: F,
77}
78
79const FS_IOC_MEASURE_VERITY: Opcode = opcode::read_write::<FsVerityDigest<()>>(b'f', 134);
81
82pub(super) fn fs_ioc_measure_verity<H: FsVerityHashValue>(
84 fd: impl AsFd,
85) -> Result<H, MeasureVerityError> {
86 let digest_size = size_of::<H>() as u16;
87 let digest_algorithm = H::ALGORITHM as u16;
88
89 let mut digest = FsVerityDigest::<H> {
90 digest_algorithm,
91 digest_size,
92 digest: H::EMPTY,
93 };
94
95 let r = unsafe {
96 ioctl(
97 fd,
98 Updater::<{ FS_IOC_MEASURE_VERITY }, FsVerityDigest<H>>::new(&mut digest),
99 )
100 };
101 match r {
102 Ok(()) => {
103 if digest.digest_algorithm != digest_algorithm {
104 return Err(MeasureVerityError::InvalidDigestAlgorithm {
105 expected: digest.digest_algorithm,
106 found: digest_algorithm,
107 });
108 }
109 if digest.digest_size != digest_size {
110 return Err(MeasureVerityError::InvalidDigestSize {
111 expected: digest.digest_size,
112 });
113 }
114 Ok(digest.digest)
115 }
116 Err(Errno::NODATA) => Err(MeasureVerityError::VerityMissing),
117 Err(Errno::NOTTY | Errno::OPNOTSUPP) => Err(MeasureVerityError::FilesystemNotSupported),
118 Err(Errno::OVERFLOW) => Err(MeasureVerityError::InvalidDigestSize {
119 expected: digest.digest_size,
120 }),
121 Err(e) => Err(Error::from(e).into()),
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use std::{mem::ManuallyDrop, os::fd::OwnedFd};
128
129 use rustix::fd::FromRawFd;
130 use tempfile::tempfile_in;
131
132 use crate::{fsverity::Sha256HashValue, test::tempfile};
133
134 use super::*;
135
136 #[test]
137 fn test_measure_verity_opt() {
138 let tf = tempfile();
139 assert!(matches!(
140 fs_ioc_measure_verity::<Sha256HashValue>(&tf),
141 Err(MeasureVerityError::VerityMissing)
142 ));
143 }
144
145 #[test_with::path(/dev/shm)]
146 #[test]
147 fn test_measure_verity_not_supported() {
148 let tf = tempfile_in("/dev/shm").unwrap();
149 assert!(matches!(
150 fs_ioc_measure_verity::<Sha256HashValue>(&tf),
151 Err(MeasureVerityError::FilesystemNotSupported)
152 ));
153 }
154
155 #[test_with::path(/dev/shm)]
156 #[test]
157 fn test_fs_ioc_enable_verity_wrong_fs() {
158 let file = tempfile_in("/dev/shm").unwrap();
159 let fd = OwnedFd::from(file);
160 let err = fs_ioc_enable_verity::<Sha256HashValue>(&fd).unwrap_err();
161 assert!(matches!(err, EnableVerityError::FilesystemNotSupported));
162 assert_eq!(err.to_string(), "Filesystem does not support fs-verity",);
163 }
164
165 #[test]
166 fn test_fs_ioc_enable_verity_bad_fd() {
167 let fd = ManuallyDrop::new(unsafe { OwnedFd::from_raw_fd(123456) });
168 let res = fs_ioc_enable_verity::<Sha256HashValue>(fd.as_fd());
169 let err = res.err().unwrap();
170 assert!(matches!(err, EnableVerityError::Io(..)));
171 assert_eq!(err.to_string(), "Bad file descriptor (os error 9)",);
172 }
173}