]> git.proxmox.com Git - pxar.git/blame - src/accessor/sync.rs
clippy
[pxar.git] / src / accessor / sync.rs
CommitLineData
6cd4f635
WB
1//! Blocking `pxar` random access handling.
2
3use std::io;
ceb83806 4use std::ops::Range;
6cd4f635
WB
5use std::os::unix::fs::FileExt;
6use std::path::Path;
7use std::pin::Pin;
b764a2b1 8use std::sync::Arc;
e72062a9 9use std::task::Context;
6cd4f635 10
e72062a9 11use crate::accessor::{self, cache::Cache, MaybeReady, ReadAt, ReadAtOperation};
6cd4f635 12use crate::decoder::Decoder;
06070d26 13use crate::format::GoodbyeItem;
6cd4f635
WB
14use crate::util::poll_result_once;
15use crate::Entry;
16
17/// Blocking `pxar` random-access decoder.
18///
19/// This is the blocking I/O version of the `pxar` accessor. This will *not* work with an
20/// asynchronous I/O object. I/O must always return `Poll::Ready`.
21///
22/// Attempting to use a `Waker` from this context *will* `panic!`
23///
24/// If you need to use asynchronous I/O, use `aio::Accessor`.
25#[repr(transparent)]
26pub struct Accessor<T> {
27 inner: accessor::AccessorImpl<T>,
28}
29
29c17fc0 30impl<T: FileExt> Accessor<FileReader<T>> {
6cd4f635
WB
31 /// Decode a `pxar` archive from a standard file implementing `FileExt`.
32 #[inline]
29c17fc0 33 pub fn from_file_and_size(input: T, size: u64) -> io::Result<Self> {
6cd4f635
WB
34 Accessor::new(FileReader::new(input), size)
35 }
36}
37
38impl Accessor<FileReader<std::fs::File>> {
39 /// Decode a `pxar` archive from a regular `std::io::File` input.
40 #[inline]
41 pub fn from_file(input: std::fs::File) -> io::Result<Self> {
42 let size = input.metadata()?.len();
43 Accessor::from_file_and_size(input, size)
44 }
45
46 /// Convenience shortcut for `File::open` followed by `Accessor::from_file`.
47 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
48 Self::from_file(std::fs::File::open(path.as_ref())?)
49 }
50}
51
29c17fc0
WB
52impl<T: Clone + std::ops::Deref<Target = std::fs::File>> Accessor<FileRefReader<T>> {
53 /// Open an `Arc` or `Rc` of `File`.
54 pub fn from_file_ref(input: T) -> io::Result<Self> {
55 let size = input.deref().metadata()?.len();
56 Accessor::from_file_ref_and_size(input, size)
57 }
58}
59
60impl<T> Accessor<FileRefReader<T>>
61where
62 T: Clone + std::ops::Deref,
63 T::Target: FileExt,
64{
65 /// Open an `Arc` or `Rc` of `File`.
66 pub fn from_file_ref_and_size(input: T, size: u64) -> io::Result<Accessor<FileRefReader<T>>> {
67 Accessor::new(FileRefReader::new(input), size)
68 }
69}
70
6cd4f635
WB
71impl<T: ReadAt> Accessor<T> {
72 /// Create a *blocking* random-access decoder from an input implementing our internal read
73 /// interface.
74 ///
75 /// Note that the `input`'s `SeqRead` implementation must always return `Poll::Ready` and is
76 /// not allowed to use the `Waker`, as this will cause a `panic!`.
77 pub fn new(input: T, size: u64) -> io::Result<Self> {
78 Ok(Self {
79 inner: poll_result_once(accessor::AccessorImpl::new(input, size))?,
80 })
81 }
82
83 /// Open a directory handle to the root of the pxar archive.
9e7287ab 84 pub fn open_root_ref(&self) -> io::Result<Directory<&dyn ReadAt>> {
29c17fc0
WB
85 Ok(Directory::new(poll_result_once(
86 self.inner.open_root_ref(),
87 )?))
88 }
b764a2b1
WB
89
90 pub fn set_goodbye_table_cache<C>(&mut self, cache: Option<C>)
91 where
92 C: Cache<u64, [GoodbyeItem]> + Send + Sync + 'static,
93 {
94 self.inner
95 .set_goodbye_table_cache(cache.map(|cache| Arc::new(cache) as _))
96 }
a2530fb7
WB
97
98 /// Get the full archive size we're allowed to access.
99 #[inline]
100 pub fn size(&self) -> u64 {
101 self.inner.size()
102 }
29c17fc0
WB
103}
104
105impl<T: Clone + ReadAt> Accessor<T> {
106 pub fn open_root(&self) -> io::Result<Directory<T>> {
6cd4f635
WB
107 Ok(Directory::new(poll_result_once(self.inner.open_root())?))
108 }
ceb83806
WB
109
110 /// Allow opening a directory at a specified offset.
1b25fc08
WB
111 ///
112 /// # Safety
113 ///
114 /// This should only be used with offsets known to point to the end of a directory, otherwise
115 /// this usually fails, unless the data otherwise happens to look like a valid directory.
34da7103 116 pub unsafe fn open_dir_at_end(&self, offset: u64) -> io::Result<Directory<T>> {
d3a83ee3
WB
117 Ok(Directory::new(poll_result_once(
118 self.inner.open_dir_at_end(offset),
119 )?))
ceb83806 120 }
6b9e2478
WB
121
122 /// Allow opening a regular file from a specified range.
1b25fc08
WB
123 ///
124 /// # Safety
125 ///
126 /// Should only be used with `entry_range_info`s originating from the same archive, otherwise
127 /// the result will be undefined and likely fail (or contain unexpected data).
06070d26
WB
128 pub unsafe fn open_file_at_range(
129 &self,
130 entry_range_info: &accessor::EntryRangeInfo,
131 ) -> io::Result<FileEntry<T>> {
6b9e2478 132 Ok(FileEntry {
06070d26 133 inner: poll_result_once(self.inner.open_file_at_range(entry_range_info))?,
6b9e2478
WB
134 })
135 }
136
137 /// Allow opening arbitrary contents from a specific range.
1b25fc08
WB
138 ///
139 /// # Safety
140 ///
141 /// This will provide a reader over an arbitrary range of the archive file, so unless this
142 /// comes from a actual file entry data, the contents might not make much sense.
6b9e2478
WB
143 pub unsafe fn open_contents_at_range(&self, range: Range<u64>) -> FileContents<T> {
144 FileContents {
145 inner: self.inner.open_contents_at_range(range),
146 at: 0,
147 }
148 }
6bfadb8a
WB
149
150 /// Following a hardlink.
06070d26 151 pub fn follow_hardlink(&self, entry: &FileEntry<T>) -> io::Result<FileEntry<T>> {
6bfadb8a 152 Ok(FileEntry {
06070d26 153 inner: poll_result_once(self.inner.follow_hardlink(&entry.inner))?,
6bfadb8a
WB
154 })
155 }
6cd4f635
WB
156}
157
158/// Adapter for FileExt readers.
29c17fc0 159#[derive(Clone)]
6cd4f635
WB
160pub struct FileReader<T> {
161 inner: T,
162}
163
164impl<T: FileExt> FileReader<T> {
165 pub fn new(inner: T) -> Self {
166 Self { inner }
167 }
168}
169
170impl<T: FileExt> ReadAt for FileReader<T> {
e72062a9
WB
171 fn start_read_at<'a>(
172 self: Pin<&'a Self>,
6cd4f635 173 _cx: &mut Context,
e72062a9 174 buf: &'a mut [u8],
6cd4f635 175 offset: u64,
e72062a9
WB
176 ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
177 MaybeReady::Ready(self.get_ref().inner.read_at(buf, offset))
178 }
179
180 fn poll_complete<'a>(
181 self: Pin<&'a Self>,
182 _op: ReadAtOperation<'a>,
183 ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
184 panic!("start_read_at on sync file returned Pending");
6cd4f635
WB
185 }
186}
187
29c17fc0
WB
188/// Adapter for `Arc` or `Rc` to FileExt readers.
189#[derive(Clone)]
190pub struct FileRefReader<T: Clone> {
191 inner: T,
192}
193
194impl<T: Clone> FileRefReader<T> {
195 pub fn new(inner: T) -> Self {
196 Self { inner }
197 }
198}
199
200impl<T> ReadAt for FileRefReader<T>
201where
202 T: Clone + std::ops::Deref,
203 T::Target: FileExt,
204{
e72062a9
WB
205 fn start_read_at<'a>(
206 self: Pin<&'a Self>,
29c17fc0 207 _cx: &mut Context,
e72062a9 208 buf: &'a mut [u8],
29c17fc0 209 offset: u64,
e72062a9
WB
210 ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
211 MaybeReady::Ready(self.get_ref().inner.read_at(buf, offset))
212 }
213
214 fn poll_complete<'a>(
215 self: Pin<&'a Self>,
216 _op: ReadAtOperation<'a>,
217 ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
218 panic!("start_read_at on sync file returned Pending");
29c17fc0
WB
219 }
220}
221
aabb78a4 222/// A pxar directory entry. This provdies blocking access to the contents of a directory.
6cd4f635 223#[repr(transparent)]
29c17fc0
WB
224pub struct Directory<T> {
225 inner: accessor::DirectoryImpl<T>,
6cd4f635
WB
226}
227
29c17fc0
WB
228impl<T: Clone + ReadAt> Directory<T> {
229 fn new(inner: accessor::DirectoryImpl<T>) -> Self {
6cd4f635
WB
230 Self { inner }
231 }
232
233 /// Get a decoder for the directory contents.
29c17fc0 234 pub fn decode_full(&self) -> io::Result<Decoder<accessor::SeqReadAtAdapter<T>>> {
6cd4f635
WB
235 Ok(Decoder::from_impl(poll_result_once(
236 self.inner.decode_full(),
237 )?))
238 }
239
a5922fbc
WB
240 /// Get a `FileEntry` referencing the directory itself.
241 ///
242 /// Helper function for our fuse implementation.
243 pub fn lookup_self(&self) -> io::Result<FileEntry<T>> {
244 Ok(FileEntry {
245 inner: poll_result_once(self.inner.lookup_self())?,
246 })
247 }
248
4af15944
WB
249 /// Lookup an entry starting from this current directory.
250 ///
251 /// For convenience, this already resolves paths with multiple components.
29c17fc0 252 pub fn lookup<P: AsRef<Path>>(&self, path: P) -> io::Result<Option<FileEntry<T>>> {
6cd4f635
WB
253 if let Some(file_entry) = poll_result_once(self.inner.lookup(path.as_ref()))? {
254 Ok(Some(FileEntry { inner: file_entry }))
255 } else {
256 Ok(None)
257 }
258 }
259
260 /// Get an iterator over the directory's contents.
1b25fc08 261 pub fn read_dir(&self) -> ReadDir<T> {
6cd4f635
WB
262 ReadDir {
263 inner: self.inner.read_dir(),
264 }
265 }
d3a83ee3
WB
266
267 /// Get the number of entries in this directory.
268 #[inline]
269 pub fn entry_count(&self) -> usize {
270 self.inner.entry_count()
271 }
6cd4f635
WB
272}
273
aabb78a4
WB
274/// A file entry in a direcotry, retrieved via the `lookup` method or from
275/// `DirEntry::decode_entry``.
6cd4f635 276#[repr(transparent)]
29c17fc0
WB
277pub struct FileEntry<T: Clone + ReadAt> {
278 inner: accessor::FileEntryImpl<T>,
6cd4f635
WB
279}
280
29c17fc0 281impl<T: Clone + ReadAt> FileEntry<T> {
aabb78a4 282 /// Get a handle to the subdirectory this file entry points to, if it is in fact a directory.
29c17fc0 283 pub fn enter_directory(&self) -> io::Result<Directory<T>> {
6cd4f635
WB
284 Ok(Directory::new(poll_result_once(
285 self.inner.enter_directory(),
286 )?))
287 }
288
6b9e2478
WB
289 /// For use with unsafe accessor methods.
290 pub fn content_range(&self) -> io::Result<Option<Range<u64>>> {
291 self.inner.content_range()
292 }
293
98b894a9
WB
294 pub fn contents(&self) -> io::Result<FileContents<T>> {
295 Ok(FileContents {
296 inner: poll_result_once(self.inner.contents())?,
297 at: 0,
298 })
299 }
300
6cd4f635
WB
301 #[inline]
302 pub fn into_entry(self) -> Entry {
303 self.inner.into_entry()
304 }
305
306 #[inline]
307 pub fn entry(&self) -> &Entry {
308 &self.inner.entry()
309 }
ceb83806
WB
310
311 /// Exposed for raw by-offset access methods (use with `open_dir_at_end`).
312 #[inline]
06070d26
WB
313 pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
314 self.inner.entry_range_info()
ceb83806 315 }
6cd4f635
WB
316}
317
29c17fc0 318impl<T: Clone + ReadAt> std::ops::Deref for FileEntry<T> {
05005b14
WB
319 type Target = Entry;
320
321 fn deref(&self) -> &Self::Target {
322 self.entry()
323 }
324}
325
6cd4f635
WB
326/// An iterator over the contents of a `Directory`.
327#[repr(transparent)]
29c17fc0
WB
328pub struct ReadDir<'a, T> {
329 inner: accessor::ReadDirImpl<'a, T>,
6cd4f635
WB
330}
331
98b894a9
WB
332impl<'a, T: Clone + ReadAt> ReadDir<'a, T> {
333 /// Efficient alternative to `Iterator::skip`.
334 #[inline]
335 pub fn skip(self, n: usize) -> Self {
336 Self {
337 inner: self.inner.skip(n),
338 }
339 }
340
341 /// Efficient alternative to `Iterator::count`.
342 #[inline]
343 pub fn count(self) -> usize {
344 self.inner.count()
345 }
346}
347
29c17fc0
WB
348impl<'a, T: Clone + ReadAt> Iterator for ReadDir<'a, T> {
349 type Item = io::Result<DirEntry<'a, T>>;
6cd4f635
WB
350
351 fn next(&mut self) -> Option<Self::Item> {
352 match poll_result_once(self.inner.next()) {
353 Ok(Some(inner)) => Some(Ok(DirEntry { inner })),
354 Ok(None) => None,
355 Err(err) => Some(Err(err)),
356 }
357 }
358}
359
29c17fc0 360impl<'a, T: Clone + ReadAt> std::iter::FusedIterator for ReadDir<'a, T> {}
6cd4f635
WB
361
362/// A directory entry. When iterating through the contents of a directory we first get access to
363/// the file name. The remaining information can be decoded afterwards.
364#[repr(transparent)]
29c17fc0
WB
365pub struct DirEntry<'a, T: Clone + ReadAt> {
366 inner: accessor::DirEntryImpl<'a, T>,
6cd4f635
WB
367}
368
29c17fc0 369impl<'a, T: Clone + ReadAt> DirEntry<'a, T> {
aabb78a4 370 /// Get the current file name.
6cd4f635
WB
371 pub fn file_name(&self) -> &Path {
372 self.inner.file_name()
373 }
374
aabb78a4
WB
375 /// Decode the entry.
376 ///
377 /// When iterating over a directory, the file name is read separately from the file attributes,
378 /// so only the file name is available here, while the attributes still need to be decoded.
379 pub fn decode_entry(&self) -> io::Result<FileEntry<T>> {
380 poll_result_once(self.inner.decode_entry()).map(|inner| FileEntry { inner })
6cd4f635 381 }
ceb83806 382
ceb83806
WB
383 /// Exposed for raw by-offset access methods.
384 #[inline]
06070d26
WB
385 pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
386 self.inner.entry_range_info()
ceb83806 387 }
6cd4f635 388}
98b894a9
WB
389
390/// A reader for file contents.
391pub struct FileContents<T> {
392 inner: accessor::FileContentsImpl<T>,
393 at: u64,
394}
395
396impl<T: Clone + ReadAt> io::Read for FileContents<T> {
397 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
398 let got = poll_result_once(self.inner.read_at(buf, self.at))?;
399 self.at += got as u64;
400 Ok(got)
401 }
402}
403
404impl<T: Clone + ReadAt> FileExt for FileContents<T> {
405 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
406 poll_result_once(self.inner.read_at(buf, offset))
407 }
408
409 fn write_at(&self, _buf: &[u8], _offset: u64) -> io::Result<usize> {
410 io_bail!("write_at on read-only file");
411 }
412}
413
414impl<T: Clone + ReadAt> ReadAt for FileContents<T> {
e72062a9
WB
415 fn start_read_at<'a>(
416 self: Pin<&'a Self>,
417 cx: &mut Context,
418 buf: &'a mut [u8],
98b894a9 419 offset: u64,
e72062a9
WB
420 ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
421 match unsafe { self.map_unchecked(|this| &this.inner) }.start_read_at(cx, buf, offset) {
422 MaybeReady::Ready(ready) => MaybeReady::Ready(ready),
423 MaybeReady::Pending(_) => panic!("start_read_at on sync file returned Pending"),
424 }
425 }
426
427 fn poll_complete<'a>(
428 self: Pin<&'a Self>,
429 _op: ReadAtOperation<'a>,
430 ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
431 panic!("start_read_at on sync file returned Pending");
98b894a9
WB
432 }
433}