22
33#![ allow( dead_code) ]
44
5+ use std:: io:: BufReader ;
56use std:: { collections:: BTreeMap , io:: Read , path:: PathBuf } ;
67
78use anyhow:: Context ;
89use cap_std_ext:: cap_std;
9- use cap_std_ext:: cap_std:: fs:: { Dir as CapStdDir , MetadataExt , PermissionsExt } ;
10+ use cap_std_ext:: cap_std:: fs:: { Dir as CapStdDir , PermissionsExt } ;
11+ use composefs:: fsverity:: { FsVerityHashValue , Sha256HashValue } ;
1012use openssl:: hash:: DigestBytes ;
1113use rustix:: fs:: readlinkat;
1214
1315#[ derive( Debug ) ]
1416struct Metadata {
1517 content_hash : String ,
1618 metadata_hash : String ,
19+ verity : Option < String > ,
1720}
1821
1922impl Metadata {
20- fn new ( content_hash : String , metadata_hash : String ) -> Self {
23+ fn new ( content_hash : String , metadata_hash : String , verity : Option < String > ) -> Self {
2124 Self {
2225 content_hash,
2326 metadata_hash,
27+ verity,
2428 }
2529 }
2630}
@@ -76,6 +80,26 @@ fn compute_diff(
7680 continue ;
7781 } ;
7882
83+ match ( & current_meta. verity , & old_meta. verity ) {
84+ ( Some ( v1) , Some ( v2) ) => {
85+ if v1 != v2 {
86+ modified. push ( current_file. clone ( ) ) ;
87+ pristine_etc_files. remove ( & current_file) ;
88+ continue ;
89+ }
90+ }
91+
92+ ( None , None ) => {
93+ // No verity enabled for files, so we move forward to checking metadata + content
94+ // checksum
95+ }
96+
97+ // This has to be some kind of error?
98+ ( None , Some ( _) ) | ( Some ( _) , None ) => {
99+ anyhow:: bail!( "File did not have fs-verity now it does or vice-versa" )
100+ }
101+ }
102+
79103 if old_meta. metadata_hash != current_meta. metadata_hash
80104 || old_meta. content_hash != current_meta. content_hash
81105 {
@@ -134,7 +158,12 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
134158
135159 list. insert (
136160 path. clone ( ) ,
137- Metadata :: new ( "" . into ( ) , hex:: encode ( compute_metadata_hash ( & metadata) ?) ) ,
161+ Metadata :: new (
162+ "" . into ( ) ,
163+ hex:: encode ( compute_metadata_hash ( & metadata) ?) ,
164+ // fs-verity is not enabled for directories
165+ None ,
166+ ) ,
138167 ) ;
139168
140169 recurse_dir ( & dir, path. clone ( ) , list) . context ( format ! ( "Recursing {path:?}" ) ) ?;
@@ -143,27 +172,53 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
143172 continue ;
144173 }
145174
146- let buf = if entry_type. is_symlink ( ) {
175+ // TODO: Another generic here but constrained to Sha256HashValue
176+ // Regarding this, we'll definitely get DigestMismatch error if SHA512 is being used
177+ let measured_verity =
178+ composefs:: fsverity:: measure_verity_opt :: < Sha256HashValue > ( entry. open ( ) ?) ?;
179+
180+ if let Some ( measured_verity) = measured_verity {
181+ list. insert (
182+ path. clone ( ) ,
183+ Metadata :: new ( "" . into ( ) , "" . into ( ) , Some ( measured_verity. to_hex ( ) ) ) ,
184+ ) ;
185+
186+ path. pop ( ) ;
187+
188+ // file has fs-verity enabled. We don't need to check the content/metadata
189+ continue ;
190+ }
191+
192+ let mut hasher = openssl:: hash:: Hasher :: new ( openssl:: hash:: MessageDigest :: sha256 ( ) ) ?;
193+
194+ if entry_type. is_symlink ( ) {
147195 let readlinkat_result = readlinkat ( & dir, & entry_name, vec ! [ ] )
148196 . context ( format ! ( "readlinkat {entry_name:?}" ) ) ?;
149197
150- readlinkat_result. into ( )
198+ hasher . update ( readlinkat_result. as_bytes ( ) ) ? ;
151199 } else if entry_type. is_file ( ) {
152- let mut buf = vec ! [ 0u8 ; entry. metadata( ) ?. size( ) as usize ] ;
200+ let mut buf = vec ! [ 0u8 ; 8192 ] ;
201+
202+ let file = entry. open ( ) . context ( format ! ( "Opening entry {path:?}" ) ) ?;
203+ let mut reader = BufReader :: new ( file) ;
153204
154- let mut file = entry. open ( ) . context ( format ! ( "Opening entry {path:?}" ) ) ?;
155- file. read_exact ( & mut buf)
156- . context ( format ! ( "Reading {path:?}. Buf: {buf:?}" ) ) ?;
205+ loop {
206+ let bytes_read = reader
207+ . read ( & mut buf)
208+ . context ( format ! ( "Reading chunk from {path:?}" ) ) ?;
157209
158- buf
210+ if bytes_read == 0 {
211+ break ;
212+ }
213+
214+ hasher. update ( & buf[ ..bytes_read] ) ?;
215+ }
159216 } else {
160217 // We cannot read any other device like socket, pipe, fifo.
161218 // We shouldn't really find these in /etc in the first place
162- unimplemented ! ( "Found file of type {:?}" , entry_type )
219+ tracing :: info !( "Ignoring non-regular/non-symlink file: {:?}" , path ) ;
163220 } ;
164221
165- let mut hasher = openssl:: hash:: Hasher :: new ( openssl:: hash:: MessageDigest :: sha256 ( ) ) ?;
166- hasher. update ( & buf) ?;
167222 let content_digest = hex:: encode ( hasher. finish ( ) ?) ;
168223
169224 let meta = entry
@@ -172,7 +227,11 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
172227
173228 list. insert (
174229 path. clone ( ) ,
175- Metadata :: new ( content_digest, hex:: encode ( compute_metadata_hash ( & meta) ?) ) ,
230+ Metadata :: new (
231+ content_digest,
232+ hex:: encode ( compute_metadata_hash ( & meta) ?) ,
233+ None ,
234+ ) ,
176235 ) ;
177236
178237 path. pop ( ) ;
0 commit comments