1#![expect(unsafe_code)]
6#![expect(clippy::undocumented_unsafe_blocks)]
7
8pub(crate) mod path;
9mod util;
10
11use crate::SetAttributes;
12use std::ffi;
13use std::mem;
14use std::os::unix::prelude::*;
15use std::path::Path;
16
17pub struct LxVolume {
20 root: std::fs::File,
21}
22
23impl LxVolume {
24 pub fn new(root_path: &Path, _options: &super::LxVolumeOptions) -> lx::Result<Self> {
25 let path = util::path_to_cstr(root_path)?;
26
27 unsafe {
29 let fd = util::check_lx_errno(libc::open(
31 path.as_ptr(),
32 libc::O_RDONLY | libc::O_DIRECTORY,
33 ))?;
34
35 Ok(Self {
36 root: std::fs::File::from_raw_fd(fd),
37 })
38 }
39 }
40
41 pub fn supports_stable_file_id(&self) -> bool {
42 true
43 }
44
45 pub fn lstat(&self, path: &Path) -> lx::Result<lx::Stat> {
46 assert!(path.is_relative());
47 let path = util::path_to_cstr(path)?;
48
49 let stat = unsafe {
51 let mut stat = mem::zeroed();
52 util::check_lx_errno(libc::fstatat(
53 self.root.as_raw_fd(),
54 path.as_ptr(),
55 &mut stat,
56 libc::AT_SYMLINK_NOFOLLOW | libc::AT_EMPTY_PATH,
57 ))?;
58 stat
59 };
60
61 Ok(util::libc_stat_to_lx_stat(stat))
62 }
63
64 pub fn set_attr(&self, path: &Path, attr: SetAttributes) -> lx::Result<()> {
65 util::set_attr(&self.root, Some(path), &attr)
66 }
67
68 pub fn set_attr_stat(&self, path: &Path, attr: SetAttributes) -> lx::Result<lx::Stat> {
69 util::set_attr(&self.root, Some(path), &attr)?;
70 self.lstat(path)
71 }
72
73 pub fn open(
74 &self,
75 path: &Path,
76 flags: i32,
77 options: Option<super::LxCreateOptions>,
78 ) -> lx::Result<LxFile> {
79 assert!(path.is_relative());
80
81 let fd = util::openat(&self.root, path, flags, options)?;
82
83 Ok(LxFile {
84 fd,
85 enumerator: None,
86 })
87 }
88
89 pub fn mkdir(&self, path: &Path, options: super::LxCreateOptions) -> lx::Result<()> {
90 assert!(path.is_relative());
91
92 let path = util::path_to_cstr(path)?;
93
94 unsafe {
96 util::check_lx_errno(libc::mkdirat(
97 self.root.as_raw_fd(),
98 path.as_ptr(),
99 options.mode,
100 ))?;
101 }
102
103 Ok(())
104 }
105
106 pub fn mkdir_stat(&self, path: &Path, options: super::LxCreateOptions) -> lx::Result<lx::Stat> {
107 self.mkdir(path, options)?;
108 self.lstat(path)
109 }
110
111 pub fn symlink(
114 &self,
115 path: &Path,
116 target: &lx::LxStr,
117 _: super::LxCreateOptions,
118 ) -> lx::Result<()> {
119 assert!(path.is_relative());
120
121 let path = util::path_to_cstr(path)?;
122 let target = util::create_cstr(target.as_bytes())?;
123
124 unsafe {
126 util::check_lx_errno(libc::symlinkat(
127 target.as_ptr(),
128 self.root.as_raw_fd(),
129 path.as_ptr(),
130 ))?;
131 }
132
133 Ok(())
134 }
135
136 pub fn symlink_stat(
137 &self,
138 path: &Path,
139 target: &lx::LxStr,
140 options: super::LxCreateOptions,
141 ) -> lx::Result<lx::Stat> {
142 self.symlink(path, target, options)?;
143 self.lstat(path)
144 }
145
146 pub fn read_link(&self, path: &Path) -> lx::Result<lx::LxString> {
147 assert!(path.is_relative());
148
149 let mut buffer = [0u8; libc::PATH_MAX as usize];
150 let path = util::path_to_cstr(path)?;
151
152 let size = unsafe {
154 util::check_lx_errno(libc::readlinkat(
155 self.root.as_raw_fd(),
156 path.as_ptr(),
157 buffer.as_mut_ptr().cast(),
158 buffer.len(),
159 ))?
160 };
161
162 Ok(lx::LxString::from_vec(Vec::from(&buffer[..size as usize])))
164 }
165
166 pub fn unlink(&self, path: &Path, flags: i32) -> lx::Result<()> {
167 assert!(path.is_relative());
168
169 let path = util::path_to_cstr(path)?;
170
171 unsafe {
173 util::check_lx_errno(libc::unlinkat(self.root.as_raw_fd(), path.as_ptr(), flags))?;
174 }
175
176 Ok(())
177 }
178
179 pub fn mknod(
180 &self,
181 path: &Path,
182 options: super::LxCreateOptions,
183 device_id: lx::dev_t,
184 ) -> lx::Result<()> {
185 assert!(path.is_relative());
186
187 let path = util::path_to_cstr(path)?;
188
189 unsafe {
191 util::check_lx_errno(libc::mknodat(
192 self.root.as_raw_fd(),
193 path.as_ptr(),
194 options.mode,
195 device_id as u64,
196 ))?;
197 }
198
199 Ok(())
200 }
201
202 pub fn mknod_stat(
203 &self,
204 path: &Path,
205 options: super::LxCreateOptions,
206 device_id: lx::dev_t,
207 ) -> lx::Result<lx::Stat> {
208 self.mknod(path, options, device_id)?;
209 self.lstat(path)
210 }
211
212 pub fn rename(&self, path: &Path, new_path: &Path, flags: u32) -> lx::Result<()> {
213 assert!(path.is_relative());
214 assert!(new_path.is_relative());
215 let path = util::path_to_cstr(path)?;
216 let new_path = util::path_to_cstr(new_path)?;
217
218 unsafe {
222 util::check_lx_errno(libc::syscall(
223 libc::SYS_renameat2,
224 self.root.as_raw_fd(),
225 path.as_ptr(),
226 self.root.as_raw_fd(),
227 new_path.as_ptr(),
228 flags,
229 ) as libc::c_int)?;
230 }
231
232 Ok(())
233 }
234
235 pub fn link(&self, path: &Path, new_path: &Path) -> lx::Result<()> {
236 assert!(path.is_relative());
237 assert!(new_path.is_relative());
238 let path = util::path_to_cstr(path)?;
239 let new_path = util::path_to_cstr(new_path)?;
240
241 unsafe {
243 util::check_lx_errno(libc::linkat(
244 self.root.as_raw_fd(),
245 path.as_ptr(),
246 self.root.as_raw_fd(),
247 new_path.as_ptr(),
248 0,
249 ))?;
250 }
251
252 Ok(())
253 }
254
255 pub fn link_stat(&self, path: &Path, new_path: &Path) -> lx::Result<lx::Stat> {
256 self.link(path, new_path)?;
257 self.lstat(new_path)
258 }
259
260 pub fn stat_fs(&self, path: &Path) -> lx::Result<lx::StatFs> {
261 assert!(path.is_relative());
262 let path = self.full_path(path)?;
263
264 let stat_fs = unsafe {
266 let mut stat_fs = mem::zeroed();
267 util::check_lx_errno(libc::statfs(path.as_ptr(), &mut stat_fs))?;
268 stat_fs
269 };
270
271 Ok(util::libc_stat_fs_to_lx_stat_fs(stat_fs))
272 }
273
274 pub fn set_xattr(
275 &self,
276 path: &Path,
277 name: &lx::LxStr,
278 value: &[u8],
279 flags: i32,
280 ) -> lx::Result<()> {
281 assert!(path.is_relative());
282
283 let path = self.full_path(path)?;
285 let name = util::create_cstr(name.as_bytes())?;
286
287 unsafe {
289 util::check_lx_errno(libc::lsetxattr(
290 path.as_ptr(),
291 name.as_ptr(),
292 value.as_ptr().cast::<ffi::c_void>(),
293 value.len(),
294 flags,
295 ))?;
296 }
297
298 Ok(())
299 }
300
301 pub fn get_xattr(
302 &self,
303 path: &Path,
304 name: &lx::LxStr,
305 value: Option<&mut [u8]>,
306 ) -> lx::Result<usize> {
307 assert!(path.is_relative());
308
309 let path = self.full_path(path)?;
311 let name = util::create_cstr(name.as_bytes())?;
312
313 let (value_ptr, size) = if let Some(value) = value {
315 (value.as_mut_ptr(), value.len())
316 } else {
317 (std::ptr::null_mut(), 0)
318 };
319
320 let size = unsafe {
322 util::check_lx_errno(libc::lgetxattr(
323 path.as_ptr(),
324 name.as_ptr(),
325 value_ptr.cast::<ffi::c_void>(),
326 size,
327 ))?
328 };
329
330 Ok(size as usize)
332 }
333
334 pub fn list_xattr(&self, path: &Path, list: Option<&mut [u8]>) -> lx::Result<usize> {
335 assert!(path.is_relative());
336
337 let path = self.full_path(path)?;
339
340 let (list_ptr, size) = if let Some(list) = list {
342 (list.as_mut_ptr(), list.len())
343 } else {
344 (std::ptr::null_mut(), 0)
345 };
346
347 let size = unsafe {
349 util::check_lx_errno(libc::llistxattr(path.as_ptr(), list_ptr.cast(), size))?
350 };
351
352 Ok(size as usize)
354 }
355
356 pub fn remove_xattr(&self, path: &Path, name: &lx::LxStr) -> lx::Result<()> {
357 assert!(path.is_relative());
358
359 let path = self.full_path(path)?;
361 let name = util::create_cstr(name.as_bytes())?;
362
363 unsafe {
365 util::check_lx_errno(libc::lremovexattr(path.as_ptr(), name.as_ptr()))?;
366 }
367
368 Ok(())
369 }
370
371 fn full_path(&self, path: &Path) -> lx::Result<ffi::CString> {
372 let mut full_path = util::get_fd_path(&self.root)?;
373 full_path.push(path);
374 util::path_to_cstr(&full_path)
375 }
376}
377
378pub struct LxFile {
381 fd: std::fs::File,
382 enumerator: Option<util::DirectoryEnumerator>,
383}
384
385impl LxFile {
386 pub fn fstat(&self) -> lx::Result<lx::Stat> {
387 let stat = unsafe {
389 let mut stat = mem::zeroed();
390 util::check_lx_errno(libc::fstat(self.fd.as_raw_fd(), &mut stat))?;
391 stat
392 };
393
394 Ok(util::libc_stat_to_lx_stat(stat))
395 }
396
397 pub fn set_attr(&self, attr: SetAttributes) -> lx::Result<()> {
398 util::set_attr(&self.fd, None, &attr)
399 }
400
401 pub fn pread(&self, buffer: &mut [u8], offset: lx::off_t) -> lx::Result<usize> {
402 let size = unsafe {
404 util::check_lx_errno(libc::pread(
405 self.fd.as_raw_fd(),
406 buffer.as_mut_ptr().cast::<ffi::c_void>(),
407 buffer.len(),
408 offset,
409 ))?
410 };
411
412 Ok(size as usize)
414 }
415
416 pub fn pwrite(&self, buffer: &[u8], offset: lx::off_t, _: lx::uid_t) -> lx::Result<usize> {
417 let size = unsafe {
421 util::check_lx_errno(libc::pwrite(
422 self.fd.as_raw_fd(),
423 buffer.as_ptr() as *mut ffi::c_void,
424 buffer.len(),
425 offset,
426 ))?
427 };
428
429 Ok(size as usize)
431 }
432
433 pub fn read_dir<F>(&mut self, offset: lx::off_t, mut callback: F) -> lx::Result<()>
434 where
435 F: FnMut(lx::DirEntry) -> lx::Result<bool>,
436 {
437 if self.enumerator.is_none() {
438 self.enumerator = Some(util::DirectoryEnumerator::new(self.fd.try_clone()?)?);
442 }
443
444 let enumerator = self.enumerator.as_mut().unwrap();
445 enumerator.seek(offset);
446 for entry in enumerator {
447 let result = callback(entry?)?;
448 if !result {
449 break;
450 }
451 }
452
453 Ok(())
454 }
455
456 pub fn fsync(&self, data_only: bool) -> lx::Result<()> {
457 unsafe {
459 if data_only {
460 util::check_lx_errno(libc::fdatasync(self.fd.as_raw_fd()))?;
461 } else {
462 util::check_lx_errno(libc::fsync(self.fd.as_raw_fd()))?;
463 }
464 }
465
466 Ok(())
467 }
468}