3
3
#![ allow( dead_code) ]
4
4
5
5
use fn_error_context:: context;
6
- use std:: ffi:: OsStr ;
6
+ use std:: cell:: RefCell ;
7
+ use std:: collections:: BTreeMap ;
8
+ use std:: ffi:: { OsStr , OsString } ;
7
9
use std:: io:: BufReader ;
10
+ use std:: os:: fd:: { AsFd , AsRawFd } ;
8
11
use std:: os:: unix:: ffi:: OsStrExt ;
9
- use std:: path:: PathBuf ;
12
+ use std:: path:: { Path , PathBuf } ;
10
13
use std:: rc:: Rc ;
11
14
12
15
use anyhow:: Context ;
@@ -16,7 +19,7 @@ use cap_std_ext::dirext::CapStdExtDirExt;
16
19
use composefs:: fsverity:: { FsVerityHashValue , Sha256HashValue , Sha512HashValue } ;
17
20
use composefs:: generic_tree:: { Directory , Inode , Leaf , LeafContent , Stat } ;
18
21
use composefs:: tree:: ImageError ;
19
- use rustix:: fs:: { AtFlags , Gid , Uid , readlinkat} ;
22
+ use rustix:: fs:: { AtFlags , Gid , Uid , XattrFlags , getxattr , listxattr , lsetxattr , readlinkat} ;
20
23
21
24
#[ derive( Debug ) ]
22
25
struct CustomMetadata {
@@ -33,16 +36,18 @@ impl CustomMetadata {
33
36
}
34
37
}
35
38
39
+ type Xattrs = RefCell < BTreeMap < Box < OsStr > , Box < [ u8 ] > > > ;
40
+
36
41
struct MyStat ( Stat ) ;
37
42
38
- impl From < & cap_std:: fs:: Metadata > for MyStat {
39
- fn from ( value : & cap_std:: fs:: Metadata ) -> Self {
43
+ impl From < ( & cap_std:: fs:: Metadata , Xattrs ) > for MyStat {
44
+ fn from ( value : ( & cap_std:: fs:: Metadata , Xattrs ) ) -> Self {
40
45
Self ( Stat {
41
- st_mode : value. mode ( ) ,
42
- st_uid : value. uid ( ) ,
43
- st_gid : value. gid ( ) ,
44
- st_mtim_sec : value. mtime ( ) ,
45
- xattrs : Default :: default ( ) ,
46
+ st_mode : value. 0 . mode ( ) ,
47
+ st_uid : value. 0 . uid ( ) ,
48
+ st_gid : value. 0 . gid ( ) ,
49
+ st_mtim_sec : value. 0 . mtime ( ) ,
50
+ xattrs : value . 1 ,
46
51
} )
47
52
}
48
53
}
@@ -288,6 +293,58 @@ fn compute_diff(
288
293
Ok ( diff)
289
294
}
290
295
296
+ #[ context( "Collecting xattrs" ) ]
297
+ fn collect_xattrs ( etc_fd : & CapStdDir , rel_path : & OsString ) -> anyhow:: Result < Xattrs > {
298
+ let link = format ! ( "/proc/self/fd/{}" , etc_fd. as_fd( ) . as_raw_fd( ) ) ;
299
+ let path = Path :: new ( & link) . join ( rel_path) ;
300
+
301
+ const DEFAULT_SIZE : usize = 128 ;
302
+
303
+ // Start with a guess for size
304
+ let mut buf: Vec < u8 > = vec ! [ 0 ; DEFAULT_SIZE ] ;
305
+ let size = listxattr ( & path, & mut buf) . context ( "listxattr" ) ?;
306
+
307
+ if size > DEFAULT_SIZE {
308
+ buf = vec ! [ 0 ; size] ;
309
+ listxattr ( & path, & mut buf) . context ( "listxattr" ) ?;
310
+ }
311
+
312
+ let xattrs: Xattrs = RefCell :: new ( BTreeMap :: new ( ) ) ;
313
+
314
+ for name_buf in buf[ ..size]
315
+ . split_inclusive ( |& b| b == 0 )
316
+ . filter ( |x| !x. is_empty ( ) )
317
+ {
318
+ let name = OsStr :: from_bytes ( name_buf) ;
319
+
320
+ let mut buf = vec ! [ 0 ; DEFAULT_SIZE ] ;
321
+ let size = getxattr ( & path, name_buf, & mut buf) . context ( "getxattr" ) ?;
322
+
323
+ if size > DEFAULT_SIZE {
324
+ buf = vec ! [ 0 ; size] ;
325
+ getxattr ( & path, name_buf, & mut buf) . context ( "getxattr" ) ?;
326
+ }
327
+
328
+ xattrs
329
+ . borrow_mut ( )
330
+ . insert ( Box :: < OsStr > :: from ( name) , Box :: < [ u8 ] > :: from ( & buf[ ..size] ) ) ;
331
+ }
332
+
333
+ Ok ( xattrs)
334
+ }
335
+
336
+ #[ context( "Copying xattrs" ) ]
337
+ fn copy_xattrs ( xattrs : & Xattrs , new_etc_fd : & CapStdDir , file : & PathBuf ) -> anyhow:: Result < ( ) > {
338
+ for ( attr, value) in xattrs. borrow ( ) . iter ( ) {
339
+ let path = Path :: new ( & format ! ( "/proc/self/fd/{}" , new_etc_fd. as_raw_fd( ) ) ) . join ( file) ;
340
+
341
+ lsetxattr ( path, attr. as_ref ( ) , value, XattrFlags :: empty ( ) )
342
+ . context ( format ! ( "setxattr for {file:?}" ) ) ?;
343
+ }
344
+
345
+ Ok ( ( ) )
346
+ }
347
+
291
348
fn recurse_dir ( dir : & CapStdDir , root : & mut Directory < CustomMetadata > ) -> anyhow:: Result < ( ) > {
292
349
for entry in dir. entries ( ) ? {
293
350
let entry = entry. context ( format ! ( "Getting entry" ) ) ?;
@@ -298,12 +355,14 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
298
355
. metadata ( )
299
356
. context ( format ! ( "Getting metadata for {entry_name:?}" ) ) ?;
300
357
358
+ let xattrs = collect_xattrs ( & dir, & entry_name) ?;
359
+
301
360
if entry_type. is_dir ( ) {
302
361
let dir = dir
303
362
. open_dir ( & entry_name)
304
363
. with_context ( || format ! ( "Opening dir {entry_name:?} inside {dir:?}" ) ) ?;
305
364
306
- let mut directory = Directory :: new ( MyStat :: from ( & entry_meta) . 0 ) ;
365
+ let mut directory = Directory :: new ( MyStat :: from ( ( & entry_meta, xattrs ) ) . 0 ) ;
307
366
308
367
recurse_dir ( & dir, & mut directory) ?;
309
368
@@ -328,7 +387,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
328
387
root. insert (
329
388
& entry_name,
330
389
Inode :: Leaf ( Rc :: new ( Leaf {
331
- stat : MyStat :: from ( & entry_meta) . 0 ,
390
+ stat : MyStat :: from ( ( & entry_meta, xattrs ) ) . 0 ,
332
391
content : LeafContent :: Symlink ( Box :: from ( os_str) ) ,
333
392
} ) ) ,
334
393
) ;
@@ -357,7 +416,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
357
416
root. insert (
358
417
& entry_name,
359
418
Inode :: Leaf ( Rc :: new ( Leaf {
360
- stat : MyStat :: from ( & entry_meta) . 0 ,
419
+ stat : MyStat :: from ( ( & entry_meta, xattrs ) ) . 0 ,
361
420
content : LeafContent :: Regular ( CustomMetadata :: new (
362
421
"" . into ( ) ,
363
422
Some ( measured_verity) ,
@@ -382,7 +441,7 @@ fn recurse_dir(dir: &CapStdDir, root: &mut Directory<CustomMetadata>) -> anyhow:
382
441
root. insert (
383
442
& entry_name,
384
443
Inode :: Leaf ( Rc :: new ( Leaf {
385
- stat : MyStat :: from ( & entry_meta) . 0 ,
444
+ stat : MyStat :: from ( ( & entry_meta, xattrs ) ) . 0 ,
386
445
content : LeafContent :: Regular ( CustomMetadata :: new ( content_digest, None ) ) ,
387
446
} ) ) ,
388
447
) ;
@@ -447,6 +506,8 @@ fn create_dir_with_perms(
447
506
)
448
507
. context ( format ! ( "chown {dir_name:?}" ) ) ?;
449
508
509
+ copy_xattrs ( & stat. xattrs , new_etc_fd, dir_name) ?;
510
+
450
511
Ok ( ( ) )
451
512
}
452
513
@@ -481,6 +542,8 @@ fn handle_leaf(
481
542
)
482
543
. context ( format ! ( "chown {file:?}" ) ) ?;
483
544
545
+ copy_xattrs ( & leaf. stat . xattrs , new_etc_fd, file) ?;
546
+
484
547
"file"
485
548
}
486
549
@@ -507,6 +570,8 @@ fn handle_leaf(
507
570
)
508
571
. context ( format ! ( "chown {file:?}" ) ) ?;
509
572
573
+ copy_xattrs ( & leaf. stat . xattrs , new_etc_fd, file) ?;
574
+
510
575
"symlink"
511
576
}
512
577
@@ -864,7 +929,6 @@ mod tests {
864
929
865
930
let ( pristine_etc_files, current_etc_files, new_etc_files) = traverse_etc ( & p, & c, & n) ?;
866
931
let diff = compute_diff ( & pristine_etc_files, & current_etc_files) ?;
867
- println ! ( "current_etc_files: {current_etc_files:#?}" ) ;
868
932
merge ( & c, & current_etc_files, & n, & new_etc_files, diff) ?;
869
933
870
934
assert ! ( files_eq( & c, & n, "new_file.txt" ) ?) ;
0 commit comments