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,53 @@ 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 ] ;
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) ;
153
204
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:?}" ) ) ?;
157
209
158
- buf
210
+ if bytes_read == 0 {
211
+ break ;
212
+ }
213
+
214
+ hasher. update ( & buf[ ..bytes_read] ) ?;
215
+ }
159
216
} else {
160
217
// We cannot read any other device like socket, pipe, fifo.
161
218
// 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 ) ;
163
220
} ;
164
221
165
- let mut hasher = openssl:: hash:: Hasher :: new ( openssl:: hash:: MessageDigest :: sha256 ( ) ) ?;
166
- hasher. update ( & buf) ?;
167
222
let content_digest = hex:: encode ( hasher. finish ( ) ?) ;
168
223
169
224
let meta = entry
@@ -172,7 +227,11 @@ fn recurse_dir(dir: &CapStdDir, mut path: PathBuf, list: &mut Map) -> anyhow::Re
172
227
173
228
list. insert (
174
229
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
+ ) ,
176
235
) ;
177
236
178
237
path. pop ( ) ;
0 commit comments