smithay/utils/
sealed_file.rs1use std::{
2 ffi::CStr,
3 fs::File,
4 io::Write,
5 os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd},
6};
7
8#[derive(Debug)]
17pub struct SealedFile {
18 file: File,
19 size: usize,
20}
21
22impl SealedFile {
23 pub fn with_content(name: &CStr, contents: &CStr) -> Result<Self, std::io::Error> {
25 Self::with_data(name, contents.to_bytes_with_nul())
26 }
27
28 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "android"))]
30 pub fn with_data(name: &CStr, data: &[u8]) -> Result<Self, std::io::Error> {
31 use rustix::fs::{MemfdFlags, SealFlags};
32 use std::io::Seek;
33
34 let fd = rustix::fs::memfd_create(name, MemfdFlags::CLOEXEC | MemfdFlags::ALLOW_SEALING)?;
35
36 let mut file: File = fd.into();
37 file.write_all(data)?;
38 file.flush()?;
39
40 file.seek(std::io::SeekFrom::Start(0))?;
41
42 rustix::fs::fcntl_add_seals(
43 &file,
44 SealFlags::SEAL | SealFlags::SHRINK | SealFlags::GROW | SealFlags::WRITE,
45 )?;
46
47 Ok(Self {
48 file,
49 size: data.len(),
50 })
51 }
52
53 #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "android")))]
55 pub fn with_data(name: &CStr, data: &[u8]) -> Result<Self, std::io::Error> {
56 use rand::{distr::Alphanumeric, Rng};
57 use rustix::{
58 io::Errno,
59 shm::{self, Mode},
60 };
61
62 let mut rng = rand::rng();
63
64 let mut n = 0;
67 let (shm_name, mut file) = loop {
68 let mut shm_name = name.to_bytes().to_owned();
69 shm_name.push(b'-');
70 shm_name.extend((0..7).map(|_| rng.sample(Alphanumeric)));
71 let fd = shm::open(
72 shm_name.as_slice(),
73 shm::OFlags::RDWR | shm::OFlags::CREATE | shm::OFlags::EXCL,
74 Mode::RWXU,
75 );
76 if !matches!(fd, Err(Errno::EXIST)) || n > 3 {
77 break (shm_name, File::from(fd?));
78 }
79 n += 1;
80 };
81
82 let fd_rdonly = shm::open(shm_name.as_slice(), shm::OFlags::RDONLY, Mode::empty())?;
84 let file_rdonly = File::from(fd_rdonly);
85
86 let _ = shm::unlink(shm_name.as_slice());
88
89 file.write_all(data)?;
90 file.flush()?;
91
92 Ok(Self {
93 file: file_rdonly,
94 size: data.len(),
95 })
96 }
97
98 pub fn size(&self) -> usize {
100 self.size
101 }
102}
103
104impl AsRawFd for SealedFile {
105 fn as_raw_fd(&self) -> RawFd {
106 self.file.as_raw_fd()
107 }
108}
109
110impl AsFd for SealedFile {
111 fn as_fd(&self) -> BorrowedFd<'_> {
112 self.file.as_fd()
113 }
114}