2
2
3
3
#![ allow( dead_code) ]
4
4
5
+ use std:: io:: BufReader ;
5
6
use std:: { collections:: BTreeMap , io:: Read , path:: PathBuf } ;
6
7
7
8
use anyhow:: Context ;
8
9
use 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 } ;
10
12
use openssl:: hash:: DigestBytes ;
11
13
use rustix:: fs:: readlinkat;
12
14
13
15
#[ derive( Debug ) ]
14
16
struct Metadata {
15
17
content_hash : String ,
16
18
metadata_hash : String ,
19
+ verity : Option < String > ,
17
20
}
18
21
19
22
impl Metadata {
20
- fn new ( content_hash : String , metadata_hash : String ) -> Self {
23
+ fn new ( content_hash : String , metadata_hash : String , verity : Option < String > ) -> Self {
21
24
Self {
22
25
content_hash,
23
26
metadata_hash,
27
+ verity,
24
28
}
25
29
}
26
30
}
@@ -76,6 +80,26 @@ fn compute_diff(
76
80
continue ;
77
81
} ;
78
82
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
+
79
103
if old_meta. metadata_hash != current_meta. metadata_hash
80
104
|| old_meta. content_hash != current_meta. content_hash
81
105
{
@@ -134,7 +158,12 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
134
158
135
159
list. insert (
136
160
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
+ ) ,
138
167
) ;
139
168
140
169
recurse_dir ( & dir, path. clone ( ) , list) . context ( format ! ( "Recursing {path:?}" ) ) ?;
@@ -143,27 +172,42 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
143
172
continue ;
144
173
}
145
174
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 ( ) {
147
195
let readlinkat_result = readlinkat ( & dir, & entry_name, vec ! [ ] )
148
196
. context ( format ! ( "readlinkat {entry_name:?}" ) ) ?;
149
197
150
- readlinkat_result. into ( )
198
+ hasher . update ( readlinkat_result. as_bytes ( ) ) ? ;
151
199
} else if entry_type. is_file ( ) {
152
- let mut buf = vec ! [ 0u8 ; entry. metadata( ) ?. size( ) as usize ] ;
153
-
154
- let mut file = entry. open ( ) . context ( format ! ( "Opening entry {path:?}" ) ) ?;
155
- file. read_exact ( & mut buf)
156
- . context ( format ! ( "Reading {path:?}. Buf: {buf:?}" ) ) ?;
200
+ let file = entry. open ( ) . context ( format ! ( "Opening entry {path:?}" ) ) ?;
201
+ let mut reader = BufReader :: new ( file) ;
157
202
158
- buf
203
+ std :: io :: copy ( & mut reader , & mut hasher ) ? ;
159
204
} else {
160
205
// We cannot read any other device like socket, pipe, fifo.
161
206
// We shouldn't really find these in /etc in the first place
162
- unimplemented ! ( "Found file of type {:?}" , entry_type)
207
+ tracing:: debug!( "Ignoring non-regular/non-symlink file: {:?}" , path) ;
208
+ continue ;
163
209
} ;
164
210
165
- let mut hasher = openssl:: hash:: Hasher :: new ( openssl:: hash:: MessageDigest :: sha256 ( ) ) ?;
166
- hasher. update ( & buf) ?;
167
211
let content_digest = hex:: encode ( hasher. finish ( ) ?) ;
168
212
169
213
let meta = entry
@@ -172,7 +216,11 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
172
216
173
217
list. insert (
174
218
path. clone ( ) ,
175
- Metadata :: new ( content_digest, hex:: encode ( compute_metadata_hash ( & meta) ?) ) ,
219
+ Metadata :: new (
220
+ content_digest,
221
+ hex:: encode ( compute_metadata_hash ( & meta) ?) ,
222
+ None ,
223
+ ) ,
176
224
) ;
177
225
178
226
path. pop ( ) ;
0 commit comments